diff --git a/DEPS b/DEPS
index 42e490b..0c55cc12 100644
--- a/DEPS
+++ b/DEPS
@@ -36,11 +36,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'b560b5c8c03fe1b9b1b8a90c546481b8db7f043e',
+  'skia_revision': '2adecda92b2f2ac49d78326a3c76442a0f1c4139',
   # 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': 'cbf06062d3b8ffaf76d4fa66720ee74cb8a57bad',
+  'v8_revision': '4a6e53465637027d6abc2f90ab84b5d2fb2aea9d',
   # 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.
@@ -88,7 +88,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': 'ee0654d7c641b8a38613ac70062853133da6d192',
+  'catapult_revision': 'c3cf2e8ef854b67718e1185ad0b5f53cf2fa0016',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -178,7 +178,7 @@
     Var('chromium_git') + '/external/bidichecker/lib.git' + '@' + '97f2aa645b74c28c57eca56992235c79850fa9e0',
 
   'src/third_party/webgl/src':
-   Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '937acaae1251a1e338bafe1995c0d54cb7142c60',
+   Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'e3488c31e13202c8d41b92eb6b7358cd23e5e004',
 
   'src/third_party/webdriver/pylib':
     Var('chromium_git') + '/external/selenium/py.git' + '@' + '5fd78261a75fe08d27ca4835fb6c5ce4b42275bd',
diff --git a/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewThreadTest.java b/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewThreadTest.java
index ac8fbad2..3eba0d0 100644
--- a/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewThreadTest.java
+++ b/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewThreadTest.java
@@ -12,6 +12,7 @@
 
 import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
 
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.webview_shell.WebViewThreadTestActivity;
 
 /**
@@ -83,6 +84,7 @@
         });
     }
 
+    @DisabledTest
     @SmallTest
     public void testWebViewInitByWebStorage() throws InterruptedException {
         initThenCreateWebViewOnUiThread(new Runnable() {
diff --git a/ash/ash.gyp b/ash/ash.gyp
index ce88229..f6cf70eb 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -110,6 +110,7 @@
       'common/shelf/shelf_alignment_menu.h',
       'common/shelf/shelf_constants.cc',
       'common/shelf/shelf_constants.h',
+      'common/shelf/shelf_delegate.h',
       'common/shelf/shelf_item_delegate.h',
       'common/shelf/shelf_item_types.cc',
       'common/shelf/shelf_item_types.h',
@@ -618,7 +619,6 @@
       'shelf/shelf_button.h',
       'shelf/shelf_button_pressed_metric_tracker.cc',
       'shelf/shelf_button_pressed_metric_tracker.h',
-      'shelf/shelf_delegate.h',
       'shelf/shelf_icon_observer.h',
       'shelf/shelf_layout_manager.cc',
       'shelf/shelf_layout_manager.h',
diff --git a/ash/shelf/shelf_delegate.h b/ash/common/shelf/shelf_delegate.h
similarity index 92%
rename from ash/shelf/shelf_delegate.h
rename to ash/common/shelf/shelf_delegate.h
index 3722023..7c58121 100644
--- a/ash/shelf/shelf_delegate.h
+++ b/ash/common/shelf/shelf_delegate.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_SHELF_SHELF_DELEGATE_H_
-#define ASH_SHELF_SHELF_DELEGATE_H_
+#ifndef ASH_COMMON_SHELF_SHELF_DELEGATE_H_
+#define ASH_COMMON_SHELF_SHELF_DELEGATE_H_
 
 #include "ash/ash_export.h"
 #include "ash/common/shelf/shelf_item_types.h"
@@ -11,10 +11,9 @@
 namespace ash {
 class Shelf;
 
-// Delegate for the Shelf.
+// Delegate shared by all shelf instances.
 class ASH_EXPORT ShelfDelegate {
  public:
-  // Shelf owns the delegate.
   virtual ~ShelfDelegate() {}
 
   // Callback used to allow delegate to perform initialization actions that
@@ -62,4 +61,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_SHELF_SHELF_DELEGATE_H_
+#endif  // ASH_COMMON_SHELF_SHELF_DELEGATE_H_
diff --git a/ash/common/wm_shell.cc b/ash/common/wm_shell.cc
index f0ed84e..9af808d 100644
--- a/ash/common/wm_shell.cc
+++ b/ash/common/wm_shell.cc
@@ -10,7 +10,9 @@
 #include "ash/common/accessibility_delegate.h"
 #include "ash/common/focus_cycler.h"
 #include "ash/common/keyboard/keyboard_ui.h"
+#include "ash/common/session/session_state_delegate.h"
 #include "ash/common/shelf/app_list_shelf_item_delegate.h"
+#include "ash/common/shelf/shelf_delegate.h"
 #include "ash/common/shelf/shelf_model.h"
 #include "ash/common/shell_delegate.h"
 #include "ash/common/shell_window_ids.h"
@@ -67,6 +69,8 @@
   // ShelfItemDelegate subclasses it owns have complex cleanup to run (e.g. ARC
   // shelf items in Chrome) so explicitly shutdown early.
   shelf_model_->DestroyItemDelegates();
+  // Must be destroyed before FocusClient.
+  shelf_delegate_.reset();
 }
 
 void WmShell::OnMaximizeModeStarted() {
@@ -108,6 +112,11 @@
   lock_state_observers_.RemoveObserver(observer);
 }
 
+void WmShell::SetShelfDelegateForTesting(
+    std::unique_ptr<ShelfDelegate> test_delegate) {
+  shelf_delegate_ = std::move(test_delegate);
+}
+
 WmShell::WmShell(std::unique_ptr<ShellDelegate> shell_delegate)
     : delegate_(std::move(shell_delegate)),
       focus_cycler_(new FocusCycler),
@@ -195,6 +204,18 @@
   system_tray_delegate_.reset();
 }
 
+void WmShell::CreateShelfDelegate() {
+  // May be called multiple times as shelves are created and destroyed.
+  if (shelf_delegate_)
+    return;
+  // Must occur after SessionStateDelegate creation and user login because
+  // Chrome's implementation of ShelfDelegate assumes it can get information
+  // about multi-profile login state.
+  DCHECK(GetSessionStateDelegate());
+  DCHECK_GT(GetSessionStateDelegate()->NumberOfLoggedInUsers(), 0);
+  shelf_delegate_.reset(delegate_->CreateShelfDelegate(shelf_model_.get()));
+}
+
 void WmShell::DeleteWindowCycleController() {
   window_cycle_controller_.reset();
 }
diff --git a/ash/common/wm_shell.h b/ash/common/wm_shell.h
index ec0b094..1cdc2c5 100644
--- a/ash/common/wm_shell.h
+++ b/ash/common/wm_shell.h
@@ -33,6 +33,7 @@
 class MruWindowTracker;
 class ScopedDisableInternalMouseAndKeyboard;
 class SessionStateDelegate;
+class ShelfDelegate;
 class ShelfModel;
 class ShellDelegate;
 class ShellObserver;
@@ -100,6 +101,8 @@
 
   MediaDelegate* media_delegate() { return media_delegate_.get(); }
 
+  ShelfDelegate* shelf_delegate() { return shelf_delegate_.get(); }
+
   ShelfModel* shelf_model() { return shelf_model_.get(); }
 
   SystemTrayNotifier* system_tray_notifier() {
@@ -258,6 +261,8 @@
   void AddLockStateObserver(LockStateObserver* observer);
   void RemoveLockStateObserver(LockStateObserver* observer);
 
+  void SetShelfDelegateForTesting(std::unique_ptr<ShelfDelegate> test_delegate);
+
 #if defined(OS_CHROMEOS)
   LogoutConfirmationController* logout_confirmation_controller() {
     return logout_confirmation_controller_.get();
@@ -282,6 +287,8 @@
   void SetSystemTrayDelegate(std::unique_ptr<SystemTrayDelegate> delegate);
   void DeleteSystemTrayDelegate();
 
+  void CreateShelfDelegate();
+
   void DeleteWindowCycleController();
 
   void DeleteWindowSelectorController();
@@ -316,6 +323,7 @@
   std::unique_ptr<MaximizeModeController> maximize_mode_controller_;
   std::unique_ptr<MediaDelegate> media_delegate_;
   std::unique_ptr<MruWindowTracker> mru_window_tracker_;
+  std::unique_ptr<ShelfDelegate> shelf_delegate_;
   std::unique_ptr<ShelfModel> shelf_model_;
   std::unique_ptr<SystemTrayNotifier> system_tray_notifier_;
   std::unique_ptr<SystemTrayDelegate> system_tray_delegate_;
diff --git a/ash/metrics/user_metrics_recorder.cc b/ash/metrics/user_metrics_recorder.cc
index 8577b99..fdd2e41 100644
--- a/ash/metrics/user_metrics_recorder.cc
+++ b/ash/metrics/user_metrics_recorder.cc
@@ -5,6 +5,7 @@
 #include "ash/metrics/user_metrics_recorder.h"
 
 #include "ash/common/session/session_state_delegate.h"
+#include "ash/common/shelf/shelf_delegate.h"
 #include "ash/common/shelf/shelf_item_types.h"
 #include "ash/common/shelf/shelf_model.h"
 #include "ash/common/shell_window_ids.h"
@@ -13,7 +14,6 @@
 #include "ash/common/wm_shell.h"
 #include "ash/metrics/desktop_task_switch_metric_recorder.h"
 #include "ash/shelf/shelf.h"
-#include "ash/shelf/shelf_delegate.h"
 #include "ash/shelf/shelf_view.h"
 #include "ash/shell.h"
 #include "ash/wm/window_state_aura.h"
@@ -157,7 +157,9 @@
 
 // Records the number of items in the shelf as an UMA statistic.
 void RecordShelfItemCounts() {
-  ShelfDelegate* shelf_delegate = Shell::GetInstance()->GetShelfDelegate();
+  ShelfDelegate* shelf_delegate = WmShell::Get()->shelf_delegate();
+  DCHECK(shelf_delegate);
+
   int pinned_item_count = 0;
   int unpinned_item_count = 0;
 
diff --git a/ash/shelf/shelf.cc b/ash/shelf/shelf.cc
index 2030432..a55758639 100644
--- a/ash/shelf/shelf.cc
+++ b/ash/shelf/shelf.cc
@@ -8,14 +8,15 @@
 #include <cmath>
 
 #include "ash/aura/wm_window_aura.h"
+#include "ash/common/shelf/shelf_delegate.h"
 #include "ash/common/shelf/shelf_item_delegate.h"
 #include "ash/common/shelf/shelf_model.h"
 #include "ash/common/shelf/shelf_navigator.h"
 #include "ash/common/shelf/wm_shelf_util.h"
 #include "ash/common/shell_window_ids.h"
+#include "ash/common/wm_shell.h"
 #include "ash/root_window_controller.h"
 #include "ash/screen_util.h"
-#include "ash/shelf/shelf_delegate.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_util.h"
 #include "ash/shelf/shelf_view.h"
@@ -39,13 +40,14 @@
 const char Shelf::kNativeViewName[] = "ShelfView";
 
 Shelf::Shelf(ShelfModel* shelf_model,
-             ShelfDelegate* shelf_delegate,
              WmShelf* wm_shelf,
              ShelfWidget* shelf_widget)
-    : delegate_(shelf_delegate),
-      wm_shelf_(wm_shelf),
+    : wm_shelf_(wm_shelf),
       shelf_widget_(shelf_widget),
-      shelf_view_(new ShelfView(shelf_model, delegate_, wm_shelf, this)),
+      shelf_view_(new ShelfView(shelf_model,
+                                WmShell::Get()->shelf_delegate(),
+                                wm_shelf,
+                                this)),
       shelf_locking_manager_(wm_shelf) {
   DCHECK(wm_shelf_);
   shelf_view_->Init();
@@ -54,7 +56,7 @@
 }
 
 Shelf::~Shelf() {
-  delegate_->OnShelfDestroyed(this);
+  WmShell::Get()->shelf_delegate()->OnShelfDestroyed(this);
 }
 
 // static
@@ -82,7 +84,7 @@
   alignment_ = alignment;
   shelf_view_->OnShelfAlignmentChanged();
   shelf_widget_->OnShelfAlignmentChanged();
-  delegate_->OnShelfAlignmentChanged(this);
+  WmShell::Get()->shelf_delegate()->OnShelfAlignmentChanged(this);
   Shell::GetInstance()->OnShelfAlignmentChanged(
       WmWindowAura::Get(shelf_widget_->GetNativeWindow()->GetRootWindow()));
   // ShelfLayoutManager will resize the shelf.
@@ -97,7 +99,7 @@
     return;
 
   auto_hide_behavior_ = auto_hide_behavior;
-  delegate_->OnShelfAutoHideBehaviorChanged(this);
+  WmShell::Get()->shelf_delegate()->OnShelfAutoHideBehaviorChanged(this);
   Shell::GetInstance()->OnShelfAutoHideBehaviorChanged(
       WmWindowAura::Get(shelf_widget_->GetNativeWindow()->GetRootWindow()));
 }
diff --git a/ash/shelf/shelf.h b/ash/shelf/shelf.h
index fab10772..5cc1fed 100644
--- a/ash/shelf/shelf.h
+++ b/ash/shelf/shelf.h
@@ -54,7 +54,6 @@
   static const char kNativeViewName[];
 
   Shelf(ShelfModel* model,
-        ShelfDelegate* delegate,
         WmShelf* wm_shelf,
         ShelfWidget* widget);
   virtual ~Shelf();
@@ -160,7 +159,6 @@
  private:
   friend class test::ShelfTestAPI;
 
-  ShelfDelegate* delegate_;
   // The shelf controller. Owned by the root window controller.
   WmShelf* wm_shelf_;
   ShelfWidget* shelf_widget_;
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 2c34c109..e1c3425d 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -16,6 +16,7 @@
 #include "ash/common/material_design/material_design_controller.h"
 #include "ash/common/session/session_state_delegate.h"
 #include "ash/common/shelf/shelf_constants.h"
+#include "ash/common/shelf/shelf_delegate.h"
 #include "ash/common/shelf/wm_shelf_util.h"
 #include "ash/common/shell_window_ids.h"
 #include "ash/common/system/status_area_widget.h"
@@ -28,7 +29,6 @@
 #include "ash/screen_util.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_bezel_event_filter.h"
-#include "ash/shelf/shelf_delegate.h"
 #include "ash/shelf/shelf_layout_manager_observer.h"
 #include "ash/shelf/shelf_util.h"
 #include "ash/shelf/shelf_widget.h"
@@ -686,7 +686,8 @@
 
   // The delegate must be notified after |state_| is updated so that it can
   // query the new target bounds.
-  ShelfDelegate* shelf_delegate = Shell::GetInstance()->GetShelfDelegate();
+  ShelfDelegate* shelf_delegate = WmShell::Get()->shelf_delegate();
+  DCHECK(shelf_delegate);
   if (old_state.visibility_state != state_.visibility_state)
     shelf_delegate->OnShelfVisibilityStateChanged(shelf_widget_->shelf());
 
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 19fecf8..7ca66f89 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -15,6 +15,7 @@
 #include "ash/common/shelf/overflow_bubble_view.h"
 #include "ash/common/shelf/overflow_button.h"
 #include "ash/common/shelf/shelf_constants.h"
+#include "ash/common/shelf/shelf_delegate.h"
 #include "ash/common/shelf/shelf_menu_model.h"
 #include "ash/common/shelf/shelf_model.h"
 #include "ash/common/shell_delegate.h"
@@ -24,7 +25,6 @@
 #include "ash/scoped_target_root_window.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_button.h"
-#include "ash/shelf/shelf_delegate.h"
 #include "ash/shelf/shelf_icon_observer.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
@@ -390,6 +390,7 @@
       is_repost_event_on_same_item_(false),
       last_pressed_index_(-1) {
   DCHECK(model_);
+  DCHECK(delegate_);
   DCHECK(wm_shelf_);
   bounds_animator_.reset(new views::BoundsAnimator(this));
   bounds_animator_->AddObserver(this);
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 3d6e158..1031275 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -37,6 +37,7 @@
 #include "ash/test/test_system_tray_delegate.h"
 #include "base/compiler_specific.h"
 #include "base/i18n/rtl.h"
+#include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
@@ -685,20 +686,17 @@
   }
 
   void ReplaceShelfDelegate() {
-    // Replace ShelfDelegate.
-    ShellTestApi shell_test_api(Shell::GetInstance());
-    shell_test_api.SetShelfDelegate(NULL);
     shelf_delegate_ = new TestShelfDelegateForShelfView();
-    shell_test_api.SetShelfDelegate(shelf_delegate_);
-    ShelfTestAPI(Shelf::ForPrimaryDisplay()).set_delegate(shelf_delegate_);
     test_api_->SetShelfDelegate(shelf_delegate_);
+    WmShell::Get()->SetShelfDelegateForTesting(
+        base::WrapUnique(shelf_delegate_));
   }
 
   ShelfModel* model_;
   ShelfView* shelf_view_;
   int browser_index_;
 
-  // Owned by ash::Shell.
+  // Owned by ash::WmShell.
   TestShelfDelegateForShelfView* shelf_delegate_;
 
   std::unique_ptr<ShelfViewTestAPI> test_api_;
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index 9f98801..e974cca 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -11,13 +11,13 @@
 #include "ash/common/material_design/material_design_controller.h"
 #include "ash/common/session/session_state_delegate.h"
 #include "ash/common/shelf/shelf_constants.h"
+#include "ash/common/shelf/shelf_delegate.h"
 #include "ash/common/shelf/shelf_model.h"
 #include "ash/common/shelf/wm_shelf_util.h"
 #include "ash/common/shell_window_ids.h"
 #include "ash/common/system/tray/system_tray_delegate.h"
 #include "ash/common/wm_root_window_controller.h"
 #include "ash/common/wm_shell.h"
-#include "ash/shelf/shelf_delegate.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_util.h"
 #include "ash/shelf/shelf_view.h"
@@ -725,13 +725,11 @@
 void ShelfWidget::CreateShelf(WmShelfAura* wm_shelf_aura) {
   DCHECK(!shelf_);
 
-  ShelfDelegate* delegate = Shell::GetInstance()->GetShelfDelegate();
-  shelf_.reset(
-      new Shelf(WmShell::Get()->shelf_model(), delegate, wm_shelf_aura, this));
+  shelf_.reset(new Shelf(WmShell::Get()->shelf_model(), wm_shelf_aura, this));
   // Must be initialized before the delegate is notified because the delegate
   // may try to access the WmShelf.
   wm_shelf_aura->SetShelf(shelf_.get());
-  delegate->OnShelfCreated(shelf_.get());
+  WmShell::Get()->shelf_delegate()->OnShelfCreated(shelf_.get());
 
   SetFocusCycler(WmShell::Get()->focus_cycler());
 }
diff --git a/ash/shelf/shelf_widget_unittest.cc b/ash/shelf/shelf_widget_unittest.cc
index aa395c964..3a3c6d4 100644
--- a/ash/shelf/shelf_widget_unittest.cc
+++ b/ash/shelf/shelf_widget_unittest.cc
@@ -5,9 +5,9 @@
 #include "ash/shelf/shelf_widget.h"
 
 #include "ash/common/material_design/material_design_controller.h"
+#include "ash/common/shelf/shelf_delegate.h"
 #include "ash/root_window_controller.h"
 #include "ash/shelf/shelf.h"
-#include "ash/shelf/shelf_delegate.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_view.h"
 #include "ash/shell.h"
diff --git a/ash/shell.cc b/ash/shell.cc
index a237945..c27108d 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -22,6 +22,7 @@
 #include "ash/common/pointer_watcher_delegate.h"
 #include "ash/common/session/session_state_delegate.h"
 #include "ash/common/shelf/app_list_shelf_item_delegate.h"
+#include "ash/common/shelf/shelf_delegate.h"
 #include "ash/common/shelf/shelf_item_delegate.h"
 #include "ash/common/shelf/shelf_model.h"
 #include "ash/common/shell_delegate.h"
@@ -57,7 +58,6 @@
 #include "ash/new_window_delegate.h"
 #include "ash/root_window_controller.h"
 #include "ash/shelf/shelf.h"
-#include "ash/shelf/shelf_delegate.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shelf/shelf_window_watcher.h"
 #include "ash/shell_factory.h"
@@ -372,6 +372,18 @@
 }
 
 void Shell::CreateShelf() {
+  // Must occur after SessionStateDelegate creation and user login.
+  DCHECK(session_state_delegate_);
+  DCHECK_GT(session_state_delegate_->NumberOfLoggedInUsers(), 0);
+  wm_shell_->CreateShelfDelegate();
+
+  // TODO(jamescook): This is here for historical reasons. Move it to WmShell.
+  // http://crbug.com/629257
+  if (!shelf_window_watcher_) {
+    shelf_window_watcher_.reset(
+        new ShelfWindowWatcher(wm_shell_->shelf_model()));
+  }
+
   RootWindowControllerList controllers = GetAllRootWindowControllers();
   for (RootWindowControllerList::iterator iter = controllers.begin();
        iter != controllers.end(); ++iter)
@@ -503,16 +515,6 @@
   return GetPrimaryRootWindowController()->GetSystemTray();
 }
 
-ShelfDelegate* Shell::GetShelfDelegate() {
-  if (!shelf_delegate_) {
-    ShelfModel* shelf_model = wm_shell_->shelf_model();
-    shelf_delegate_.reset(
-        wm_shell_->delegate()->CreateShelfDelegate(shelf_model));
-    shelf_window_watcher_.reset(new ShelfWindowWatcher(shelf_model));
-  }
-  return shelf_delegate_.get();
-}
-
 void Shell::SetTouchHudProjectionEnabled(bool enabled) {
   if (is_touch_hud_projection_enabled_ == enabled)
     return;
@@ -668,10 +670,6 @@
   // path. (crbug.com/485438).
   wm_shell_->DeleteMruWindowTracker();
 
-  // Chrome implementation of shelf delegate depends on FocusClient,
-  // so must be deleted before |focus_client_| (below).
-  shelf_delegate_.reset();
-
   // These need a valid Shell instance to clean up properly, so explicitly
   // delete them before invalidating the instance.
   // Alphabetical. TODO(oshima): sort.
diff --git a/ash/shell.h b/ash/shell.h
index 85f9901..23c849b 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -121,7 +121,6 @@
 class ScreenPositionController;
 class SessionStateDelegate;
 class Shelf;
-class ShelfDelegate;
 class ShelfWindowWatcher;
 class ShellDelegate;
 struct ShellInitParams;
@@ -449,9 +448,6 @@
 
   WindowPositioner* window_positioner() { return window_positioner_.get(); }
 
-  // Returns the launcher delegate, creating if necesary.
-  ShelfDelegate* GetShelfDelegate();
-
   UserMetricsRecorder* metrics() { return user_metrics_recorder_.get(); }
 
   void SetTouchHudProjectionEnabled(bool enabled);
@@ -555,7 +551,6 @@
   std::unique_ptr<SessionStateDelegate> session_state_delegate_;
   std::unique_ptr<NewWindowDelegate> new_window_delegate_;
   std::unique_ptr<PointerWatcherDelegate> pointer_watcher_delegate_;
-  std::unique_ptr<ShelfDelegate> shelf_delegate_;
   std::unique_ptr<ShelfWindowWatcher> shelf_window_watcher_;
   std::unique_ptr<WindowPositioner> window_positioner_;
 
diff --git a/ash/shell/shelf_delegate_impl.h b/ash/shell/shelf_delegate_impl.h
index ebfd1b70..f957b62 100644
--- a/ash/shell/shelf_delegate_impl.h
+++ b/ash/shell/shelf_delegate_impl.h
@@ -5,7 +5,7 @@
 #ifndef ASH_SHELL_SHELF_DELEGATE_IMPL_H_
 #define ASH_SHELL_SHELF_DELEGATE_IMPL_H_
 
-#include "ash/shelf/shelf_delegate.h"
+#include "ash/common/shelf/shelf_delegate.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 
diff --git a/ash/sysui/shelf_delegate_mus.h b/ash/sysui/shelf_delegate_mus.h
index 049ee89f..c92575d7 100644
--- a/ash/sysui/shelf_delegate_mus.h
+++ b/ash/sysui/shelf_delegate_mus.h
@@ -7,9 +7,9 @@
 
 #include <map>
 
+#include "ash/common/shelf/shelf_delegate.h"
 #include "ash/public/interfaces/shelf_layout.mojom.h"
 #include "ash/public/interfaces/user_window_controller.mojom.h"
-#include "ash/shelf/shelf_delegate.h"
 #include "mash/shelf/public/interfaces/shelf.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
diff --git a/ash/sysui/sysui_application.cc b/ash/sysui/sysui_application.cc
index 4c81776..664adf3 100644
--- a/ash/sysui/sysui_application.cc
+++ b/ash/sysui/sysui_application.cc
@@ -11,6 +11,7 @@
 
 #include "ash/common/material_design/material_design_controller.h"
 #include "ash/common/shell_window_ids.h"
+#include "ash/common/wm_shell.h"
 #include "ash/desktop_background/desktop_background_controller.h"
 #include "ash/display/display_manager.h"
 #include "ash/host/ash_window_tree_host_init_params.h"
@@ -333,7 +334,8 @@
     ::shell::Connection* connection,
     mash::shelf::mojom::ShelfControllerRequest request) {
   mash::shelf::mojom::ShelfController* shelf_controller =
-      static_cast<ShelfDelegateMus*>(Shell::GetInstance()->GetShelfDelegate());
+      static_cast<ShelfDelegateMus*>(WmShell::Get()->shelf_delegate());
+  DCHECK(shelf_controller);
   shelf_controller_bindings_.AddBinding(shelf_controller, std::move(request));
 }
 
diff --git a/ash/test/shelf_test_api.h b/ash/test/shelf_test_api.h
index 9d04cbe..f5e9de6 100644
--- a/ash/test/shelf_test_api.h
+++ b/ash/test/shelf_test_api.h
@@ -21,8 +21,6 @@
 
   ShelfView* shelf_view() { return shelf_->shelf_view_; }
 
-  void set_delegate(ShelfDelegate* delegate) { shelf_->delegate_ = delegate; }
-
  private:
   Shelf* shelf_;
 
diff --git a/ash/test/shell_test_api.cc b/ash/test/shell_test_api.cc
index 457d201..b904335 100644
--- a/ash/test/shell_test_api.cc
+++ b/ash/test/shell_test_api.cc
@@ -9,7 +9,6 @@
 #include "ash/common/shell_delegate.h"
 #include "ash/display/display_configuration_controller.h"
 #include "ash/root_window_controller.h"
-#include "ash/shelf/shelf_delegate.h"
 #include "ash/shell.h"
 
 namespace ash {
@@ -50,10 +49,6 @@
   shell_->display_configuration_controller()->ResetAnimatorForTest();
 }
 
-void ShellTestApi::SetShelfDelegate(ShelfDelegate* delegate) {
-  shell_->shelf_delegate_.reset(delegate);
-}
-
 void ShellTestApi::SetSessionStateDelegate(
     SessionStateDelegate* session_state_delegate) {
   shell_->session_state_delegate_.reset(session_state_delegate);
diff --git a/ash/test/shell_test_api.h b/ash/test/shell_test_api.h
index 8d7a4cf..4915934 100644
--- a/ash/test/shell_test_api.h
+++ b/ash/test/shell_test_api.h
@@ -40,9 +40,6 @@
   MaximizeModeWindowManager* maximize_mode_window_manager();
   void DisableDisplayAnimator();
 
-  // Set ShelfDelegate.
-  void SetShelfDelegate(ShelfDelegate* delegate);
-
   // Set SessionStateDelegate.
   void SetSessionStateDelegate(SessionStateDelegate* session_state_delegate);
 
diff --git a/ash/test/test_shelf_delegate.h b/ash/test/test_shelf_delegate.h
index bde40790..0e51e96 100644
--- a/ash/test/test_shelf_delegate.h
+++ b/ash/test/test_shelf_delegate.h
@@ -9,8 +9,7 @@
 #include <set>
 #include <string>
 
-#include "ash/shelf/shelf_delegate.h"
-#include "base/compiler_specific.h"
+#include "ash/common/shelf/shelf_delegate.h"
 #include "base/macros.h"
 #include "ui/aura/window_observer.h"
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index bef337a..ddb9ab4 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -727,7 +727,8 @@
     "sync_socket.h",
     "sync_socket_posix.cc",
     "sync_socket_win.cc",
-    "synchronization/cancellation_flag.cc",
+    "synchronization/atomic_flag.cc",
+    "synchronization/atomic_flag.h",
     "synchronization/cancellation_flag.h",
     "synchronization/condition_variable.h",
     "synchronization/condition_variable_posix.cc",
@@ -1885,7 +1886,7 @@
     "strings/utf_string_conversions_unittest.cc",
     "supports_user_data_unittest.cc",
     "sync_socket_unittest.cc",
-    "synchronization/cancellation_flag_unittest.cc",
+    "synchronization/atomic_flag_unittest.cc",
     "synchronization/condition_variable_unittest.cc",
     "synchronization/lock_unittest.cc",
     "synchronization/read_write_lock_unittest.cc",
diff --git a/base/base.gyp b/base/base.gyp
index 408640b..644e262 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -539,7 +539,7 @@
         'strings/utf_string_conversions_unittest.cc',
         'supports_user_data_unittest.cc',
         'sync_socket_unittest.cc',
-        'synchronization/cancellation_flag_unittest.cc',
+        'synchronization/atomic_flag_unittest.cc',
         'synchronization/condition_variable_unittest.cc',
         'synchronization/lock_unittest.cc',
         'synchronization/read_write_lock_unittest.cc',
diff --git a/base/base.gypi b/base/base.gypi
index 8bf210bb..77bb73b 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -599,7 +599,8 @@
           'strings/utf_string_conversions.h',
           'supports_user_data.cc',
           'supports_user_data.h',
-          'synchronization/cancellation_flag.cc',
+          'synchronization/atomic_flag.cc',
+          'synchronization/atomic_flag.h',
           'synchronization/cancellation_flag.h',
           'synchronization/condition_variable.h',
           'synchronization/condition_variable_posix.cc',
diff --git a/base/synchronization/cancellation_flag.cc b/base/synchronization/atomic_flag.cc
similarity index 60%
rename from base/synchronization/cancellation_flag.cc
rename to base/synchronization/atomic_flag.cc
index ca5c0a8..98d90cb8 100644
--- a/base/synchronization/cancellation_flag.cc
+++ b/base/synchronization/atomic_flag.cc
@@ -2,24 +2,24 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 
 #include "base/logging.h"
 
 namespace base {
 
-void CancellationFlag::Set() {
-#if !defined(NDEBUG)
-  DCHECK_EQ(set_on_, PlatformThread::CurrentId());
-#endif
+AtomicFlag::AtomicFlag() = default;
+
+void AtomicFlag::Set() {
+  DCHECK(thread_checker_.CalledOnValidThread());
   base::subtle::Release_Store(&flag_, 1);
 }
 
-bool CancellationFlag::IsSet() const {
+bool AtomicFlag::IsSet() const {
   return base::subtle::Acquire_Load(&flag_) != 0;
 }
 
-void CancellationFlag::UnsafeResetForTesting() {
+void AtomicFlag::UnsafeResetForTesting() {
   base::subtle::Release_Store(&flag_, 0);
 }
 
diff --git a/base/synchronization/atomic_flag.h b/base/synchronization/atomic_flag.h
new file mode 100644
index 0000000..c754e8729
--- /dev/null
+++ b/base/synchronization/atomic_flag.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SYNCHRONIZATION_ATOMIC_FLAG_H_
+#define BASE_SYNCHRONIZATION_ATOMIC_FLAG_H_
+
+#include "base/atomicops.h"
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+
+namespace base {
+
+// A flag that can safely be set from one thread and read from other threads.
+//
+// This class IS NOT intended for synchronization between threads.
+class BASE_EXPORT AtomicFlag {
+ public:
+  AtomicFlag();
+  ~AtomicFlag() = default;
+
+  // Set the flag. May only be called on the thread which created the object.
+  void Set();
+
+  // Returns true iff the flag was set.
+  bool IsSet() const;
+
+  // Resets the flag. Be careful when using this: callers might not expect
+  // IsSet() to return false after returning true once.
+  void UnsafeResetForTesting();
+
+ private:
+  base::subtle::Atomic32 flag_ = 0;
+  ThreadChecker thread_checker_;
+
+  DISALLOW_COPY_AND_ASSIGN(AtomicFlag);
+};
+
+}  // namespace base
+
+#endif  // BASE_SYNCHRONIZATION_ATOMIC_FLAG_H_
diff --git a/base/synchronization/atomic_flag_unittest.cc b/base/synchronization/atomic_flag_unittest.cc
new file mode 100644
index 0000000..b0dfd47
--- /dev/null
+++ b/base/synchronization/atomic_flag_unittest.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/synchronization/atomic_flag.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Death tests misbehave on Android.
+// TODO(fdoray): Remove this when https://codereview.chromium.org/2162053006
+// lands.
+#if DCHECK_IS_ON() && defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
+#define EXPECT_DCHECK_DEATH(statement, regex) EXPECT_DEATH(statement, regex)
+#else
+#define EXPECT_DCHECK_DEATH(statement, regex)
+#endif
+
+namespace base {
+
+namespace {
+
+void ExpectSetFlagDeath(AtomicFlag* flag) {
+  ASSERT_TRUE(flag);
+  EXPECT_DCHECK_DEATH(flag->Set(), "");
+}
+
+void BusyWaitUntilFlagIsSet(AtomicFlag* flag) {
+  while (!flag->IsSet())
+    PlatformThread::YieldCurrentThread();
+}
+
+}  // namespace
+
+TEST(AtomicFlagTest, SimpleSingleThreadedTest) {
+  AtomicFlag flag;
+  ASSERT_FALSE(flag.IsSet());
+  flag.Set();
+  ASSERT_TRUE(flag.IsSet());
+}
+
+TEST(AtomicFlagTest, DoubleSetTest) {
+  AtomicFlag flag;
+  ASSERT_FALSE(flag.IsSet());
+  flag.Set();
+  ASSERT_TRUE(flag.IsSet());
+  flag.Set();
+  ASSERT_TRUE(flag.IsSet());
+}
+
+TEST(AtomicFlagTest, ReadFromDifferentThread) {
+  AtomicFlag flag;
+
+  Thread thread("AtomicFlagTest.ReadFromDifferentThread");
+  ASSERT_TRUE(thread.Start());
+  thread.task_runner()->PostTask(FROM_HERE,
+                                 Bind(&BusyWaitUntilFlagIsSet, &flag));
+
+  // To verify that IsSet() fetches the flag's value from memory every time it
+  // is called (not just the first time that it is called on a thread), sleep
+  // before setting the flag.
+  PlatformThread::Sleep(TimeDelta::FromMilliseconds(25));
+
+  flag.Set();
+
+  // The |thread|'s destructor will block until the posted task completes, so
+  // the test will time out if it fails to see the flag be set.
+}
+
+TEST(AtomicFlagTest, SetOnDifferentThreadDeathTest) {
+  // Checks that Set() can't be called from any other thread. AtomicFlag should
+  // die on a DCHECK if Set() is called from other thread.
+  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+  Thread t("AtomicFlagTest.SetOnDifferentThreadDeathTest");
+  ASSERT_TRUE(t.Start());
+
+  AtomicFlag flag;
+  t.task_runner()->PostTask(FROM_HERE, Bind(&ExpectSetFlagDeath, &flag));
+}
+
+}  // namespace base
diff --git a/base/synchronization/cancellation_flag.h b/base/synchronization/cancellation_flag.h
index f2f83f47..39094e2d 100644
--- a/base/synchronization/cancellation_flag.h
+++ b/base/synchronization/cancellation_flag.h
@@ -5,44 +5,15 @@
 #ifndef BASE_SYNCHRONIZATION_CANCELLATION_FLAG_H_
 #define BASE_SYNCHRONIZATION_CANCELLATION_FLAG_H_
 
-#include "base/atomicops.h"
-#include "base/base_export.h"
-#include "base/macros.h"
-#include "base/threading/platform_thread.h"
+#include "base/synchronization/atomic_flag.h"
 
 namespace base {
 
-// CancellationFlag allows one thread to cancel jobs executed on some worker
-// thread. Calling Set() from one thread and IsSet() from a number of threads
-// is thread-safe.
-//
-// This class IS NOT intended for synchronization between threads.
-class BASE_EXPORT CancellationFlag {
- public:
-  CancellationFlag() : flag_(false) {
-#if !defined(NDEBUG)
-    set_on_ = PlatformThread::CurrentId();
-#endif
-  }
-  ~CancellationFlag() {}
-
-  // Set the flag. May only be called on the thread which owns the object.
-  void Set();
-  bool IsSet() const;  // Returns true iff the flag was set.
-
-  // For subtle reasons that may be different on different architectures,
-  // a different thread testing IsSet() may erroneously read 'true' after
-  // this method has been called.
-  void UnsafeResetForTesting();
-
- private:
-  base::subtle::Atomic32 flag_;
-#if !defined(NDEBUG)
-  PlatformThreadId set_on_;
-#endif
-
-  DISALLOW_COPY_AND_ASSIGN(CancellationFlag);
-};
+// Use inheritance instead of "using" to allow forward declaration of "class
+// CancellationFlag".
+// TODO(fdoray): Replace CancellationFlag with AtomicFlag throughout the
+// codebase and delete this file. crbug.com/630251
+class CancellationFlag : public AtomicFlag {};
 
 }  // namespace base
 
diff --git a/base/synchronization/cancellation_flag_unittest.cc b/base/synchronization/cancellation_flag_unittest.cc
deleted file mode 100644
index 13c74bcb..0000000
--- a/base/synchronization/cancellation_flag_unittest.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Tests of CancellationFlag class.
-
-#include "base/synchronization/cancellation_flag.h"
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/single_thread_task_runner.h"
-#include "base/synchronization/spin_wait.h"
-#include "base/threading/thread.h"
-#include "base/time/time.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/platform_test.h"
-
-namespace base {
-
-namespace {
-
-//------------------------------------------------------------------------------
-// Define our test class.
-//------------------------------------------------------------------------------
-
-void CancelHelper(CancellationFlag* flag) {
-#if GTEST_HAS_DEATH_TEST
-  ASSERT_DEBUG_DEATH(flag->Set(), "");
-#endif
-}
-
-TEST(CancellationFlagTest, SimpleSingleThreadedTest) {
-  CancellationFlag flag;
-  ASSERT_FALSE(flag.IsSet());
-  flag.Set();
-  ASSERT_TRUE(flag.IsSet());
-}
-
-TEST(CancellationFlagTest, DoubleSetTest) {
-  CancellationFlag flag;
-  ASSERT_FALSE(flag.IsSet());
-  flag.Set();
-  ASSERT_TRUE(flag.IsSet());
-  flag.Set();
-  ASSERT_TRUE(flag.IsSet());
-}
-
-TEST(CancellationFlagTest, SetOnDifferentThreadDeathTest) {
-  // Checks that Set() can't be called from any other thread.
-  // CancellationFlag should die on a DCHECK if Set() is called from
-  // other thread.
-  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
-  Thread t("CancellationFlagTest.SetOnDifferentThreadDeathTest");
-  ASSERT_TRUE(t.Start());
-  ASSERT_TRUE(t.message_loop());
-  ASSERT_TRUE(t.IsRunning());
-
-  CancellationFlag flag;
-  t.task_runner()->PostTask(FROM_HERE, base::Bind(&CancelHelper, &flag));
-}
-
-}  // namespace
-
-}  // namespace base
diff --git a/base/task_scheduler/task_scheduler_impl.cc b/base/task_scheduler/task_scheduler_impl.cc
index b99dfea..4487b098 100644
--- a/base/task_scheduler/task_scheduler_impl.cc
+++ b/base/task_scheduler/task_scheduler_impl.cc
@@ -31,7 +31,7 @@
 
 TaskSchedulerImpl::~TaskSchedulerImpl() {
 #if DCHECK_IS_ON()
-  DCHECK(join_for_testing_returned_.IsSignaled());
+  DCHECK(join_for_testing_returned_.IsSet());
 #endif
 }
 
@@ -59,13 +59,13 @@
 
 void TaskSchedulerImpl::JoinForTesting() {
 #if DCHECK_IS_ON()
-  DCHECK(!join_for_testing_returned_.IsSignaled());
+  DCHECK(!join_for_testing_returned_.IsSet());
 #endif
   for (const auto& worker_pool : worker_pools_)
     worker_pool->JoinForTesting();
   service_thread_->JoinForTesting();
 #if DCHECK_IS_ON()
-  join_for_testing_returned_.Signal();
+  join_for_testing_returned_.Set();
 #endif
 }
 
@@ -75,11 +75,6 @@
           Bind(&TaskSchedulerImpl::OnDelayedRunTimeUpdated, Unretained(this))),
       worker_pool_index_for_traits_callback_(
           worker_pool_index_for_traits_callback)
-#if DCHECK_IS_ON()
-      ,
-      join_for_testing_returned_(WaitableEvent::ResetPolicy::MANUAL,
-                                 WaitableEvent::InitialState::NOT_SIGNALED)
-#endif
 {
   DCHECK(!worker_pool_index_for_traits_callback_.is_null());
 }
diff --git a/base/task_scheduler/task_scheduler_impl.h b/base/task_scheduler/task_scheduler_impl.h
index 9c8f4c5..9efd6d76 100644
--- a/base/task_scheduler/task_scheduler_impl.h
+++ b/base/task_scheduler/task_scheduler_impl.h
@@ -16,7 +16,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/synchronization/waitable_event.h"
+#include "base/synchronization/atomic_flag.h"
 #include "base/task_runner.h"
 #include "base/task_scheduler/delayed_task_manager.h"
 #include "base/task_scheduler/scheduler_worker_pool_impl.h"
@@ -93,8 +93,8 @@
   std::unique_ptr<SchedulerServiceThread> service_thread_;
 
 #if DCHECK_IS_ON()
-  // Signaled once JoinForTesting() has returned.
-  WaitableEvent join_for_testing_returned_;
+  // Set once JoinForTesting() has returned.
+  AtomicFlag join_for_testing_returned_;
 #endif
 
   DISALLOW_COPY_AND_ASSIGN(TaskSchedulerImpl);
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 7d683bd2..ad9c545e 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -981,6 +981,15 @@
   return effect_tree_index_;
 }
 
+int Layer::render_target_effect_tree_index() const {
+  EffectNode* effect_node =
+      layer_tree_host_->property_trees()->effect_tree.Node(effect_tree_index_);
+  if (effect_node->has_render_surface)
+    return effect_node->id;
+  else
+    return effect_node->target_id;
+}
+
 void Layer::SetScrollTreeIndex(int index) {
   DCHECK(IsPropertyChangeAllowed());
   if (scroll_tree_index_ == index)
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 879beaab..25586cff 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -430,6 +430,10 @@
   void SetEffectTreeIndex(int index);
   int effect_tree_index() const;
 
+  // TODO(sunxd): Remove this when we do not compute target space transforms on
+  // main thread in tests.
+  int render_target_effect_tree_index() const;
+
   void SetScrollTreeIndex(int index);
   int scroll_tree_index() const;
 
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index 404c298..44260b6 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -160,6 +160,15 @@
   effect_tree_index_ = index;
 }
 
+int LayerImpl::render_target_effect_tree_index() const {
+  EffectNode* effect_node =
+      layer_tree_impl_->property_trees()->effect_tree.Node(effect_tree_index_);
+  if (effect_node->render_surface)
+    return effect_node->id;
+  else
+    return effect_node->target_id;
+}
+
 void LayerImpl::SetScrollTreeIndex(int index) {
   scroll_tree_index_ = index;
 }
@@ -1162,23 +1171,13 @@
 
 RenderSurfaceImpl* LayerImpl::render_target() {
   EffectTree& effect_tree = layer_tree_impl_->property_trees()->effect_tree;
-  EffectNode* node = effect_tree.Node(effect_tree_index_);
-
-  if (node->render_surface)
-    return node->render_surface;
-  else
-    return effect_tree.Node(node->target_id)->render_surface;
+  return effect_tree.Node(render_target_effect_tree_index())->render_surface;
 }
 
 const RenderSurfaceImpl* LayerImpl::render_target() const {
   const EffectTree& effect_tree =
       layer_tree_impl_->property_trees()->effect_tree;
-  const EffectNode* node = effect_tree.Node(effect_tree_index_);
-
-  if (node->render_surface)
-    return node->render_surface;
-  else
-    return effect_tree.Node(node->target_id)->render_surface;
+  return effect_tree.Node(render_target_effect_tree_index())->render_surface;
 }
 
 bool LayerImpl::IsHidden() const {
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index a3c621d..eef15e7 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -119,6 +119,7 @@
 
   void SetEffectTreeIndex(int index);
   int effect_tree_index() const { return effect_tree_index_; }
+  int render_target_effect_tree_index() const;
 
   void SetScrollTreeIndex(int index);
   int scroll_tree_index() const { return scroll_tree_index_; }
diff --git a/cc/proto/property_tree.proto b/cc/proto/property_tree.proto
index 890911c..b1c988f 100644
--- a/cc/proto/property_tree.proto
+++ b/cc/proto/property_tree.proto
@@ -204,7 +204,7 @@
 }
 
 // Proto for class PropertyTrees.
-// NEXT ID: 16
+// NEXT ID: 17
 message PropertyTrees {
   optional PropertyTree transform_tree = 1;
   optional PropertyTree effect_tree = 2;
@@ -218,6 +218,7 @@
   optional int64 sequence_number = 6;
   optional bool is_main_thread = 13;
   optional bool is_active = 14;
+  optional bool verify_transform_tree_calculations = 16;
 
   optional Vector2dF inner_viewport_container_bounds_delta = 8;
   optional Vector2dF outer_viewport_container_bounds_delta = 9;
diff --git a/cc/test/fake_layer_tree_host.cc b/cc/test/fake_layer_tree_host.cc
index 4d214fb..9e68fa33 100644
--- a/cc/test/fake_layer_tree_host.cc
+++ b/cc/test/fake_layer_tree_host.cc
@@ -33,7 +33,9 @@
 std::unique_ptr<FakeLayerTreeHost> FakeLayerTreeHost::Create(
     FakeLayerTreeHostClient* client,
     TestTaskGraphRunner* task_graph_runner) {
-  return Create(client, task_graph_runner, LayerTreeSettings());
+  LayerTreeSettings settings;
+  settings.verify_transform_tree_calculations = true;
+  return Create(client, task_graph_runner, settings);
 }
 
 std::unique_ptr<FakeLayerTreeHost> FakeLayerTreeHost::Create(
diff --git a/cc/test/layer_tree_host_common_test.cc b/cc/test/layer_tree_host_common_test.cc
index aabd43c..1eed9ba 100644
--- a/cc/test/layer_tree_host_common_test.cc
+++ b/cc/test/layer_tree_host_common_test.cc
@@ -227,7 +227,7 @@
 }
 
 LayerTreeHostCommonTest::LayerTreeHostCommonTest()
-    : LayerTreeHostCommonTestBase(LayerTreeSettings()) {}
+    : LayerTreeHostCommonTestBase(LayerTreeHostCommonTestSettings()) {}
 
 LayerTreeHostCommonTest::LayerTreeHostCommonTest(
     const LayerTreeSettings& settings)
diff --git a/cc/test/layer_tree_host_common_test.h b/cc/test/layer_tree_host_common_test.h
index 8399743..3b99fd6 100644
--- a/cc/test/layer_tree_host_common_test.h
+++ b/cc/test/layer_tree_host_common_test.h
@@ -156,6 +156,12 @@
                              layer_impl->layer_tree_impl())
         .starting_animation_scale;
   }
+
+  LayerTreeSettings LayerTreeHostCommonTestSettings() {
+    LayerTreeSettings settings;
+    settings.verify_transform_tree_calculations = true;
+    return settings;
+  }
 };
 
 }  // namespace cc
diff --git a/cc/test/test_layer_tree_host_base.cc b/cc/test/test_layer_tree_host_base.cc
index 9a32fc4..5a08d10 100644
--- a/cc/test/test_layer_tree_host_base.cc
+++ b/cc/test/test_layer_tree_host_base.cc
@@ -31,7 +31,9 @@
 }
 
 LayerTreeSettings TestLayerTreeHostBase::CreateSettings() {
-  return LayerTreeSettings();
+  LayerTreeSettings settings;
+  settings.verify_transform_tree_calculations = true;
+  return settings;
 }
 
 std::unique_ptr<OutputSurface> TestLayerTreeHostBase::CreateOutputSurface() {
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc
index 366fafc..2dc73ee 100644
--- a/cc/trees/draw_property_utils.cc
+++ b/cc/trees/draw_property_utils.cc
@@ -485,8 +485,8 @@
     }
 
     // The clip rect should be intersected with layer rect in target space.
-    gfx::Transform content_to_target =
-        transform_tree.ToTarget(transform_node->id);
+    gfx::Transform content_to_target = transform_tree.ToTarget(
+        transform_node->id, layer->render_target_effect_tree_index());
     content_to_target.Translate(layer->offset_to_transform_parent().x(),
                                 layer->offset_to_transform_parent().y());
     gfx::Rect layer_content_rect = gfx::Rect(layer_bounds);
@@ -510,7 +510,8 @@
 
     gfx::Transform target_to_layer;
     if (transform_node->ancestors_are_invertible) {
-      target_to_layer = transform_tree.FromTarget(transform_node->id);
+      target_to_layer = transform_tree.FromTarget(
+          transform_node->id, layer->render_target_effect_tree_index());
     } else {
       bool success = transform_tree.ComputeTransform(
           target_node_id, transform_node->id, &target_to_layer);
@@ -565,7 +566,9 @@
   const TransformNode* node = tree.Node(transform_tree_index);
   return layer->use_local_transform_for_backface_visibility()
              ? node->local.IsBackFaceVisible()
-             : tree.ToTarget(transform_tree_index).IsBackFaceVisible();
+             : tree.ToTarget(transform_tree_index,
+                             layer->render_target_effect_tree_index())
+                   .IsBackFaceVisible();
 }
 
 static inline bool TransformToScreenIsKnown(Layer* layer,
@@ -831,7 +834,9 @@
     if (clip_node->resets_clip && non_root_surfaces_enabled) {
       if (clip_node->applies_local_clip) {
         clip_node->clip_in_target_space = MathUtil::MapClippedRect(
-            transform_tree.ToTarget(clip_node->transform_id), clip_node->clip);
+            transform_tree.ToTarget(clip_node->transform_id,
+                                    clip_node->target_effect_id),
+            clip_node->clip);
         ResetIfHasNanCoordinate(&clip_node->clip_in_target_space);
         clip_node->combined_clip_in_target_space =
             gfx::IntersectRects(clip_node->clip_in_target_space,
@@ -867,7 +872,8 @@
         source_to_target = transform_tree.ToScreen(clip_node->transform_id);
       } else if (transform_tree.ContentTargetId(transform_node->id) ==
                  clip_node->target_transform_id) {
-        source_to_target = transform_tree.ToTarget(clip_node->transform_id);
+        source_to_target = transform_tree.ToTarget(clip_node->transform_id,
+                                                   clip_node->target_effect_id);
       } else {
         success = property_trees->ComputeTransformToTarget(
             transform_node->id, clip_node->target_effect_id, &source_to_target);
@@ -1192,7 +1198,8 @@
   if (!owns_non_root_surface) {
     // If you're not the root, or you don't own a surface, you need to apply
     // your local offset.
-    xform = transform_tree.ToTarget(layer->transform_tree_index());
+    xform = transform_tree.ToTarget(layer->transform_tree_index(),
+                                    layer->render_target_effect_tree_index());
     if (layer->should_flatten_transform_from_property_tree())
       xform.FlattenTo2d();
     xform.Translate(layer->offset_to_transform_parent().x(),
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index b66c40f..33b7094a 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -100,6 +100,7 @@
  public:
   LayerTreeSettingsScaleContent() {
     layer_transforms_should_scale_layer_contents = true;
+    verify_transform_tree_calculations = true;
   }
 };
 
@@ -5379,6 +5380,7 @@
     layers_always_allowed_lcd_text_ = std::tr1::get<1>(GetParam());
     settings.can_use_lcd_text = can_use_lcd_text_;
     settings.layers_always_allowed_lcd_text = layers_always_allowed_lcd_text_;
+    settings.verify_transform_tree_calculations = true;
     return settings;
   }
 
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index f1dd7ca..1176007 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -250,7 +250,7 @@
 
   gfx::Transform combined_transform;
   if (current->id > dest_id) {
-    combined_transform = ToTarget(current->id);
+    combined_transform = ToTarget(current->id, kInvalidNodeId);
     // The stored target space transform has surface contents scale baked in,
     // but we need the unscaled transform.
     combined_transform.matrix().postScale(
@@ -567,8 +567,15 @@
   return !nodes_affected_by_outer_viewport_bounds_delta_.empty();
 }
 
-const gfx::Transform& TransformTree::FromTarget(int node_id) const {
+const gfx::Transform& TransformTree::FromTarget(int node_id,
+                                                int effect_id) const {
   DCHECK(static_cast<int>(cached_data_.size()) > node_id);
+  if (effect_id != kInvalidNodeId &&
+      property_trees()->verify_transform_tree_calculations) {
+    const gfx::Transform& transform =
+        property_trees()->GetDrawTransforms(node_id, effect_id).from_target;
+    CHECK(transform.ApproximatelyEqual(cached_data_[node_id].from_target));
+  }
   return cached_data_[node_id].from_target;
 }
 
@@ -578,8 +585,18 @@
   cached_data_[node_id].from_target = transform;
 }
 
-const gfx::Transform& TransformTree::ToTarget(int node_id) const {
+const gfx::Transform& TransformTree::ToTarget(int node_id,
+                                              int effect_id) const {
   DCHECK(static_cast<int>(cached_data_.size()) > node_id);
+  if (effect_id != kInvalidNodeId &&
+      property_trees()->verify_transform_tree_calculations) {
+    const gfx::Transform& transform =
+        property_trees()->GetDrawTransforms(node_id, effect_id).to_target;
+    if (property_trees()->non_root_surfaces_enabled)
+      CHECK(transform.ApproximatelyEqual(cached_data_[node_id].to_target));
+    else
+      CHECK(transform.ApproximatelyEqual(cached_data_[node_id].to_screen));
+  }
   return cached_data_[node_id].to_target;
 }
 
@@ -1457,7 +1474,8 @@
       full_tree_damaged(false),
       sequence_number(0),
       is_main_thread(true),
-      is_active(false) {
+      is_active(false),
+      verify_transform_tree_calculations(false) {
   transform_tree.SetPropertyTrees(this);
   effect_tree.SetPropertyTrees(this);
   clip_tree.SetPropertyTrees(this);
@@ -1502,6 +1520,7 @@
   sequence_number = from.sequence_number;
   is_main_thread = from.is_main_thread;
   is_active = from.is_active;
+  verify_transform_tree_calculations = from.verify_transform_tree_calculations;
   inner_viewport_container_bounds_delta_ =
       from.inner_viewport_container_bounds_delta();
   outer_viewport_container_bounds_delta_ =
@@ -1529,6 +1548,8 @@
   proto->set_non_root_surfaces_enabled(non_root_surfaces_enabled);
   proto->set_is_main_thread(is_main_thread);
   proto->set_is_active(is_active);
+  proto->set_verify_transform_tree_calculations(
+      verify_transform_tree_calculations);
 
   // TODO(khushalsagar): Consider using the sequence number to decide if
   // property trees need to be serialized again for a commit. See crbug/555370.
@@ -1553,6 +1574,8 @@
   sequence_number = proto.sequence_number();
   is_main_thread = proto.is_main_thread();
   is_active = proto.is_active();
+  verify_transform_tree_calculations =
+      proto.verify_transform_tree_calculations();
 
   transform_tree.SetPropertyTrees(this);
   effect_tree.SetPropertyTrees(this);
@@ -1744,7 +1767,8 @@
     // Computing maximum animated scale in the presence of non-scale/translation
     // transforms isn't supported.
     bool failed_for_non_scale_or_translation =
-        !transform_tree.ToTarget(transform_node_id).IsScaleOrTranslation();
+        !transform_tree.ToTarget(transform_node_id, effect_tree.kInvalidNodeId)
+             .IsScaleOrTranslation();
 
     // We don't attempt to accumulate animation scale from multiple nodes with
     // scale animations, because of the risk of significant overestimation. For
@@ -1811,7 +1835,9 @@
       } else {
         gfx::Vector2dF ancestor_scales =
             parent_node ? MathUtil::ComputeTransform2dScaleComponents(
-                              transform_tree.ToTarget(parent_node->id), 0.f)
+                              transform_tree.ToTarget(
+                                  parent_node->id, effect_tree.kInvalidNodeId),
+                              0.f)
                         : gfx::Vector2dF(1.f, 1.f);
         float max_ancestor_scale =
             std::max(ancestor_scales.x(), ancestor_scales.y());
@@ -1848,10 +1874,64 @@
       cached_data_.property_tree_update_number;
 }
 
+const DrawTransforms& PropertyTrees::GetDrawTransforms(int transform_id,
+                                                       int effect_id) const {
+  if (cached_data_.draw_transforms[effect_id][transform_id].update_number !=
+      cached_data_.property_tree_update_number) {
+    gfx::Transform target_space_transform;
+    gfx::Transform from_target;
+    const TransformNode* transform_node = transform_tree.Node(transform_id);
+    const EffectNode* effect_node = effect_tree.Node(effect_id);
+    DCHECK(effect_id == effect_tree.kRootNodeId ||
+           effect_node->has_render_surface);
+    if (transform_id == effect_node->transform_id) {
+      target_space_transform.Scale(effect_node->surface_contents_scale.x(),
+                                   effect_node->surface_contents_scale.y());
+    } else {
+      // Compute transform from transform_id to effect_node->transform.
+      DCHECK_GT(transform_id, effect_node->transform_id);
+      const TransformNode* dest_node =
+          transform_tree.Node(effect_node->transform_id);
+      if (!dest_node || (dest_node->ancestors_are_invertible &&
+                         dest_node->node_and_ancestors_are_flat)) {
+        target_space_transform.ConcatTransform(
+            transform_tree.ToScreen(transform_id));
+        if (dest_node)
+          target_space_transform.ConcatTransform(
+              transform_tree.FromScreen(dest_node->id));
+        if (dest_node->needs_surface_contents_scale)
+          target_space_transform.matrix().postScale(
+              dest_node->surface_contents_scale.x(),
+              dest_node->surface_contents_scale.y(), 1.f);
+      } else {
+        target_space_transform =
+            GetDrawTransforms(transform_node->parent_id, effect_id).to_target;
+        if (transform_node->flattens_inherited_transform)
+          target_space_transform.FlattenTo2d();
+        target_space_transform.PreconcatTransform(transform_node->to_parent);
+      }
+    }
+    cached_data_.draw_transforms[effect_id][transform_id]
+        .transforms.invertible =
+        target_space_transform.GetInverse(&from_target);
+    cached_data_.draw_transforms[effect_id][transform_id].update_number =
+        cached_data_.property_tree_update_number;
+    cached_data_.draw_transforms[effect_id][transform_id]
+        .transforms.from_target = from_target;
+    cached_data_.draw_transforms[effect_id][transform_id].transforms.to_target =
+        target_space_transform;
+  }
+  return cached_data_.draw_transforms[effect_id][transform_id].transforms;
+}
+
 void PropertyTrees::ResetCachedData() {
   cached_data_.property_tree_update_number = 0;
   cached_data_.animation_scales = std::vector<AnimationScaleData>(
       transform_tree.nodes().size(), AnimationScaleData());
+  cached_data_.draw_transforms =
+      std::vector<std::unordered_map<int, DrawTransformData>>(
+          effect_tree.nodes().size(),
+          std::unordered_map<int, DrawTransformData>());
 }
 
 void PropertyTrees::UpdateCachedNumber() {
diff --git a/cc/trees/property_tree.h b/cc/trees/property_tree.h
index 86d95ab7..2101d59 100644
--- a/cc/trees/property_tree.h
+++ b/cc/trees/property_tree.h
@@ -67,6 +67,10 @@
   ~PropertyTree();
   PropertyTree<T>& operator=(const PropertyTree<T>&);
 
+  // Property tree node starts from index 0.
+  static const int kInvalidNodeId = -1;
+  static const int kRootNodeId = 0;
+
   bool operator==(const PropertyTree<T>& other) const;
 
   int Insert(const T& tree_node, int parent_id);
@@ -211,10 +215,12 @@
     return nodes_affected_by_outer_viewport_bounds_delta_;
   }
 
-  const gfx::Transform& FromTarget(int node_id) const;
+  const gfx::Transform& FromTarget(int node_id, int effect) const;
   void SetFromTarget(int node_id, const gfx::Transform& transform);
 
-  const gfx::Transform& ToTarget(int node_id) const;
+  // TODO(sunxd): Remove target space transforms in cached data when we
+  // completely implement computing draw transforms on demand.
+  const gfx::Transform& ToTarget(int node_id, int effect_id) const;
   void SetToTarget(int node_id, const gfx::Transform& transform);
 
   const gfx::Transform& FromScreen(int node_id) const;
@@ -462,9 +468,34 @@
   }
 };
 
+struct DrawTransforms {
+  bool invertible;
+  gfx::Transform from_target;
+  gfx::Transform to_target;
+
+  DrawTransforms(gfx::Transform from, gfx::Transform to)
+      : invertible(true), from_target(from), to_target(to) {}
+  bool operator==(const DrawTransforms& other) const {
+    return invertible == other.invertible && from_target == other.from_target &&
+           to_target == other.to_target;
+  }
+};
+
+struct DrawTransformData {
+  int update_number;
+  DrawTransforms transforms;
+
+  // TODO(sunxd): Move screen space transforms here if it can improve
+  // performance.
+  DrawTransformData()
+      : update_number(-1), transforms(gfx::Transform(), gfx::Transform()) {}
+};
+
 struct PropertyTreesCachedData {
   int property_tree_update_number;
   std::vector<AnimationScaleData> animation_scales;
+  mutable std::vector<std::unordered_map<int, DrawTransformData>>
+      draw_transforms;
 
   PropertyTreesCachedData();
   ~PropertyTreesCachedData();
@@ -509,6 +540,7 @@
   int sequence_number;
   bool is_main_thread;
   bool is_active;
+  bool verify_transform_tree_calculations;
 
   void SetInnerViewportContainerBoundsDelta(gfx::Vector2dF bounds_delta);
   void SetOuterViewportContainerBoundsDelta(gfx::Vector2dF bounds_delta);
@@ -539,6 +571,11 @@
   void SetAnimationScalesForTesting(int transform_id,
                                     float maximum_animation_scale,
                                     float starting_animation_scale);
+
+  // GetDrawTransforms may change the value of cached_data_.
+  const DrawTransforms& GetDrawTransforms(int transform_id,
+                                          int effect_id) const;
+
   void ResetCachedData();
   void UpdateCachedNumber();
   gfx::Transform ToScreenSpaceTransformWithoutSurfaceContentsScale(
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index e066b21..27c15bd6 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -1421,6 +1421,10 @@
     PropertyTrees* property_trees) {
   property_trees->is_main_thread = true;
   property_trees->is_active = false;
+  property_trees->verify_transform_tree_calculations =
+      root_layer->layer_tree_host()
+          ->settings()
+          .verify_transform_tree_calculations;
   SkColor color = root_layer->layer_tree_host()->background_color();
   if (SkColorGetA(color) != 255)
     color = SkColorSetA(color, 255);
@@ -1433,6 +1437,7 @@
   for (auto* layer : *root_layer->layer_tree_host())
     CheckScrollAndClipPointersForLayer(layer);
 #endif
+  property_trees->ResetCachedData();
 }
 
 void PropertyTreeBuilder::BuildPropertyTrees(
@@ -1449,6 +1454,10 @@
     PropertyTrees* property_trees) {
   property_trees->is_main_thread = false;
   property_trees->is_active = root_layer->IsActive();
+  property_trees->verify_transform_tree_calculations =
+      root_layer->layer_tree_impl()
+          ->settings()
+          .verify_transform_tree_calculations;
   SkColor color = root_layer->layer_tree_impl()->background_color();
   if (SkColorGetA(color) != 255)
     color = SkColorSetA(color, 255);
diff --git a/cc/trees/property_tree_unittest.cc b/cc/trees/property_tree_unittest.cc
index 597ac799..88c759a7 100644
--- a/cc/trees/property_tree_unittest.cc
+++ b/cc/trees/property_tree_unittest.cc
@@ -595,9 +595,16 @@
  protected:
   void StartTest() override {
     PropertyTrees property_trees;
+    property_trees.verify_transform_tree_calculations = true;
     TransformTree& tree = property_trees.transform_tree;
+    EffectTree& effect_tree = property_trees.effect_tree;
 
     int grand_parent = tree.Insert(TransformNode(), 0);
+    int effect_grand_parent = effect_tree.Insert(EffectNode(), 0);
+    effect_tree.Node(effect_grand_parent)->has_render_surface = true;
+    effect_tree.Node(effect_grand_parent)->transform_id = grand_parent;
+    effect_tree.Node(effect_grand_parent)->surface_contents_scale =
+        gfx::Vector2dF(1.f, 1.f);
     tree.SetContentTargetId(grand_parent, grand_parent);
     tree.SetTargetId(grand_parent, grand_parent);
     tree.Node(grand_parent)->source_node_id = 0;
@@ -606,6 +613,11 @@
     rotation_about_x.RotateAboutXAxis(15);
 
     int parent = tree.Insert(TransformNode(), grand_parent);
+    int effect_parent = effect_tree.Insert(EffectNode(), effect_grand_parent);
+    effect_tree.Node(effect_parent)->transform_id = parent;
+    effect_tree.Node(effect_parent)->has_render_surface = true;
+    effect_tree.Node(effect_parent)->surface_contents_scale =
+        gfx::Vector2dF(1.f, 1.f);
     tree.Node(parent)->needs_surface_contents_scale = true;
     tree.SetTargetId(parent, grand_parent);
     tree.SetContentTargetId(parent, parent);
@@ -629,18 +641,20 @@
     tree.set_needs_update(true);
     SetupTransformTreeForTest(&tree);
     draw_property_utils::ComputeTransforms(&tree);
+    property_trees.ResetCachedData();
 
     gfx::Transform flattened_rotation_about_x = rotation_about_x;
     flattened_rotation_about_x.FlattenTo2d();
 
-    EXPECT_TRANSFORMATION_MATRIX_EQ(rotation_about_x, tree.ToTarget(child));
+    EXPECT_TRANSFORMATION_MATRIX_EQ(rotation_about_x,
+                                    tree.ToTarget(child, effect_parent));
 
     EXPECT_TRANSFORMATION_MATRIX_EQ(
         flattened_rotation_about_x * rotation_about_x, tree.ToScreen(child));
 
     EXPECT_TRANSFORMATION_MATRIX_EQ(
         flattened_rotation_about_x * rotation_about_x,
-        tree.ToTarget(grand_child));
+        tree.ToTarget(grand_child, effect_parent));
 
     EXPECT_TRANSFORMATION_MATRIX_EQ(flattened_rotation_about_x *
                                         flattened_rotation_about_x *
@@ -660,7 +674,7 @@
     draw_property_utils::ComputeTransforms(&tree);
 
     EXPECT_TRANSFORMATION_MATRIX_EQ(rotation_about_x * rotation_about_x,
-                                    tree.ToTarget(grand_child));
+                                    tree.ToTarget(grand_child, effect_parent));
 
     EXPECT_TRANSFORMATION_MATRIX_EQ(
         flattened_rotation_about_x * rotation_about_x * rotation_about_x,
@@ -1101,9 +1115,13 @@
     // This tests that to_target transform is not snapped when it has a singular
     // transform.
     PropertyTrees property_trees;
+    property_trees.verify_transform_tree_calculations = true;
     TransformTree& tree = property_trees.transform_tree;
+    EffectTree& effect_tree = property_trees.effect_tree;
 
     int parent = tree.Insert(TransformNode(), 0);
+    int effect_parent = effect_tree.Insert(EffectNode(), 0);
+    effect_tree.Node(effect_parent)->has_render_surface = true;
     tree.SetTargetId(parent, parent);
     tree.Node(parent)->scrolls = true;
 
@@ -1117,20 +1135,21 @@
 
     SetupTransformTreeForTest(&tree);
     draw_property_utils::ComputeTransforms(&tree);
+    property_trees.ResetCachedData();
 
     gfx::Transform from_target;
-    EXPECT_FALSE(tree.ToTarget(child).GetInverse(&from_target));
+    EXPECT_FALSE(tree.ToTarget(child, effect_parent).GetInverse(&from_target));
     // The following checks are to ensure that snapping is skipped because of
     // singular transform (and not because of other reasons which also cause
     // snapping to be skipped).
     EXPECT_TRUE(child_node->scrolls);
-    EXPECT_TRUE(tree.ToTarget(child).IsScaleOrTranslation());
+    EXPECT_TRUE(tree.ToTarget(child, effect_parent).IsScaleOrTranslation());
     EXPECT_FALSE(child_node->to_screen_is_potentially_animated);
     EXPECT_FALSE(child_node->ancestors_are_invertible);
 
-    gfx::Transform rounded = tree.ToTarget(child);
+    gfx::Transform rounded = tree.ToTarget(child, effect_parent);
     rounded.RoundTranslationComponents();
-    EXPECT_NE(tree.ToTarget(child), rounded);
+    EXPECT_NE(tree.ToTarget(child, effect_parent), rounded);
   }
 };
 
diff --git a/chrome/VERSION b/chrome/VERSION
index e72ad0f..ba18a3d 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=54
 MINOR=0
-BUILD=2807
+BUILD=2808
 PATCH=0
diff --git a/chrome/android/java/res/xml/main_preferences.xml b/chrome/android/java/res/xml/main_preferences.xml
index 927ce125d..b3dae2ae 100644
--- a/chrome/android/java/res/xml/main_preferences.xml
+++ b/chrome/android/java/res/xml/main_preferences.xml
@@ -19,9 +19,7 @@
         android:key="autofill_settings"
         android:title="@string/prefs_autofill" />
     <org.chromium.chrome.browser.preferences.ChromeBasePreference
-        android:fragment="org.chromium.chrome.browser.preferences.password.SavePasswordsPreferences"
-        android:key="saved_passwords"
-        android:title="@string/prefs_saved_passwords" />
+        android:key="saved_passwords"/>
     <Preference
         android:fragment="org.chromium.chrome.browser.preferences.HomepagePreferences"
         android:key="homepage"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
index 72e80b0..19671fe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
@@ -17,7 +17,7 @@
 import org.chromium.chrome.browser.ntp.NewTabPageUma;
 import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager;
 import org.chromium.chrome.browser.ntp.UiConfig;
-import org.chromium.chrome.browser.ntp.snippets.DisabledReason;
+import org.chromium.chrome.browser.ntp.snippets.ContentSuggestionsCategoryStatus;
 import org.chromium.chrome.browser.ntp.snippets.SnippetArticleListItem;
 import org.chromium.chrome.browser.ntp.snippets.SnippetArticleViewHolder;
 import org.chromium.chrome.browser.ntp.snippets.SnippetHeaderListItem;
@@ -37,22 +37,17 @@
 public class NewTabPageAdapter extends Adapter<NewTabPageViewHolder> implements SnippetsObserver {
     private static final String TAG = "Ntp";
 
-    /**
-     * Position of the first card in the adapter. This is always going to be a valid position,
-     * occupied either by a card showing content or by a status card.
-     */
-    private static final int FIRST_CARD_POSITION = 2;
-
     private final NewTabPageManager mNewTabPageManager;
     private final NewTabPageLayout mNewTabPageLayout;
-    private final AboveTheFoldListItem mAboveTheFoldListItem;
-    private final SnippetHeaderListItem mHeaderListItem;
+    private final AboveTheFoldListItem mAboveTheFold;
+    private final SnippetHeaderListItem mHeader;
     private final UiConfig mUiConfig;
-    private StatusListItem mStatusListItem;
-    private final List<NewTabPageListItem> mNewTabPageListItems;
+    private StatusListItem mStatusCard;
+    private final SpacingListItem mBottomSpacer;
+    private final List<NewTabPageListItem> mItems;
     private final ItemTouchCallbacks mItemTouchCallbacks;
     private NewTabPageRecyclerView mRecyclerView;
-    private int mServiceStatus;
+    private int mProviderStatus;
 
     private SnippetsBridge mSnippetsBridge;
 
@@ -118,14 +113,15 @@
             SnippetsBridge snippetsBridge, UiConfig uiConfig) {
         mNewTabPageManager = manager;
         mNewTabPageLayout = newTabPageLayout;
-        mAboveTheFoldListItem = new AboveTheFoldListItem();
-        mHeaderListItem = new SnippetHeaderListItem();
+        mAboveTheFold = new AboveTheFoldListItem();
+        mHeader = new SnippetHeaderListItem();
+        mBottomSpacer = new SpacingListItem();
         mItemTouchCallbacks = new ItemTouchCallbacks();
-        mNewTabPageListItems = new ArrayList<NewTabPageListItem>();
-        mServiceStatus = DisabledReason.NONE;
+        mItems = new ArrayList<>();
+        mProviderStatus = ContentSuggestionsCategoryStatus.INITIALIZING;
         mSnippetsBridge = snippetsBridge;
         mUiConfig = uiConfig;
-        mStatusListItem = StatusListItem.create(snippetsBridge.getDisabledReason(), this, manager);
+        mStatusCard = StatusListItem.create(snippetsBridge.getCategoryStatus(), this, manager);
 
         loadSnippets(new ArrayList<SnippetArticleListItem>());
         mSnippetsBridge.setObserver(this);
@@ -137,54 +133,54 @@
     }
 
     @Override
-    public void onSnippetsReceived(List<SnippetArticleListItem> listSnippets) {
+    public void onSnippetsReceived(List<SnippetArticleListItem> snippets) {
         // We never want to refresh the suggestions if we already have some content.
         if (hasSuggestions()) return;
 
-        if (!(mServiceStatus == DisabledReason.NONE
-                    || mServiceStatus == DisabledReason.HISTORY_SYNC_STATE_UNKNOWN)) {
+        if (!SnippetsBridge.isCategoryStatusInitOrAvailable(mProviderStatus)) {
             return;
         }
 
-        int newSnippetCount = listSnippets.size();
-        Log.d(TAG, "Received %d new snippets.", newSnippetCount);
+        Log.d(TAG, "Received %d new snippets.", snippets.size());
 
         // At first, there might be no snippets available, we wait until they have been fetched.
-        if (newSnippetCount == 0) return;
+        if (snippets.isEmpty()) return;
 
-        loadSnippets(listSnippets);
+        loadSnippets(snippets);
 
         NewTabPageUma.recordSnippetAction(NewTabPageUma.SNIPPETS_ACTION_SHOWN);
     }
 
     @Override
-    public void onDisabledReasonChanged(int disabledReason) {
+    public void onCategoryStatusChanged(int categoryStatus) {
         // Observers should not be registered for that state
-        assert disabledReason != DisabledReason.EXPLICITLY_DISABLED;
+        assert categoryStatus
+                != ContentSuggestionsCategoryStatus.ALL_SUGGESTIONS_EXPLICITLY_DISABLED;
 
-        mServiceStatus = disabledReason;
-        mStatusListItem = StatusListItem.create(mServiceStatus, this, mNewTabPageManager);
+        mProviderStatus = categoryStatus;
+        mStatusCard = StatusListItem.create(mProviderStatus, this, mNewTabPageManager);
 
-        // We had suggestions but we just got notified about the service being enabled. Nothing to
+        // We had suggestions but we just got notified about the provider being enabled. Nothing to
         // do then.
-        if (disabledReason == DisabledReason.NONE && hasSuggestions()) return;
+        if (SnippetsBridge.isCategoryStatusAvailable(mProviderStatus) && hasSuggestions()) return;
 
         if (hasSuggestions()) {
-            // We had many items, implies that the service was previously enabled and just
-            // transitioned to a disabled state. We now clear it.
+            // We have suggestions, this implies that the service was previously enabled and just
+            // transitioned to a disabled state. Clear them.
             loadSnippets(new ArrayList<SnippetArticleListItem>());
         } else {
-            mNewTabPageListItems.set(FIRST_CARD_POSITION, mStatusListItem);
-
-            // Update both the first card and the spacing item coming after it.
-            notifyItemRangeChanged(FIRST_CARD_POSITION, 2);
+            // If there are no suggestions there is an old status card that must be replaced.
+            int firstCardPosition = getFirstCardPosition();
+            mItems.set(firstCardPosition, mStatusCard);
+            // Update both the status card and the spacer after it.
+            notifyItemRangeChanged(firstCardPosition, 2);
         }
     }
 
     @Override
     @NewTabPageListItem.ViewType
     public int getItemViewType(int position) {
-        return mNewTabPageListItems.get(position).getType();
+        return mItems.get(position).getType();
     }
 
     @Override
@@ -217,12 +213,28 @@
 
     @Override
     public void onBindViewHolder(NewTabPageViewHolder holder, final int position) {
-        holder.onBindViewHolder(mNewTabPageListItems.get(position));
+        holder.onBindViewHolder(mItems.get(position));
     }
 
     @Override
     public int getItemCount() {
-        return mNewTabPageListItems.size();
+        return mItems.size();
+    }
+
+    public int getHeaderPosition() {
+        return mItems.indexOf(mHeader);
+    }
+
+    public int getFirstCardPosition() {
+        return getHeaderPosition() + 1;
+    }
+
+    public int getLastCardPosition() {
+        return getBottomSpacerPosition() - 1;
+    }
+
+    public int getBottomSpacerPosition() {
+        return mItems.indexOf(mBottomSpacer);
     }
 
     /** Start a request for new snippets. */
@@ -230,31 +242,32 @@
         SnippetsBridge.fetchSnippets();
     }
 
-    private void loadSnippets(List<SnippetArticleListItem> listSnippets) {
+    private void loadSnippets(List<SnippetArticleListItem> snippets) {
         // Copy thumbnails over
-        for (SnippetArticleListItem newSnippet : listSnippets) {
-            int existingSnippetIdx = mNewTabPageListItems.indexOf(newSnippet);
+        for (SnippetArticleListItem snippet : snippets) {
+            int existingSnippetIdx = mItems.indexOf(snippet);
             if (existingSnippetIdx == -1) continue;
 
-            newSnippet.setThumbnailBitmap(
-                    ((SnippetArticleListItem) mNewTabPageListItems.get(existingSnippetIdx))
-                            .getThumbnailBitmap());
+            snippet.setThumbnailBitmap(
+                    ((SnippetArticleListItem) mItems.get(existingSnippetIdx)).getThumbnailBitmap());
         }
 
-        boolean hasContentToShow = !listSnippets.isEmpty();
-        mHeaderListItem.setVisible(hasContentToShow);
+        boolean hasContentToShow = !snippets.isEmpty();
 
-        mNewTabPageListItems.clear();
-        mNewTabPageListItems.add(mAboveTheFoldListItem);
-        mNewTabPageListItems.add(mHeaderListItem);
+        // TODO(mvanouwerkerk): Make it so that the header does not need to be manipulated
+        // separately from the cards to which it belongs - crbug.com/616090.
+        mHeader.setVisible(hasContentToShow);
 
+        mItems.clear();
+        mItems.add(mAboveTheFold);
+        mItems.add(mHeader);
         if (hasContentToShow) {
-            mNewTabPageListItems.addAll(listSnippets);
+            mItems.addAll(snippets);
         } else {
-            mNewTabPageListItems.add(mStatusListItem);
+            mItems.add(mStatusCard);
         }
 
-        mNewTabPageListItems.add(new SpacingListItem());
+        mItems.add(mBottomSpacer);
 
         notifyDataSetChanged();
     }
@@ -277,8 +290,7 @@
         assert itemViewHolder.getItemViewType() == NewTabPageListItem.VIEW_TYPE_SNIPPET;
 
         int position = itemViewHolder.getAdapterPosition();
-        SnippetArticleListItem dismissedSnippet =
-                (SnippetArticleListItem) mNewTabPageListItems.get(position);
+        SnippetArticleListItem dismissedSnippet = (SnippetArticleListItem) mItems.get(position);
 
         mSnippetsBridge.getSnippedVisited(dismissedSnippet, new Callback<Boolean>() {
             @Override
@@ -290,28 +302,29 @@
         });
 
         mSnippetsBridge.discardSnippet(dismissedSnippet);
-        mNewTabPageListItems.remove(position);
+        mItems.remove(position);
         notifyItemRemoved(position);
     }
 
     private void addStatusCardIfNecessary() {
-        if (mNewTabPageListItems.size() == 3 /* above-the-fold + header + spacing */) {
-            // TODO(dgn) hack until we refactor the entire class with sections, etc.
-            // (see https://crbug.com/616090)
-            mNewTabPageListItems.add(FIRST_CARD_POSITION, mStatusListItem);
+        if (!hasSuggestions() && !mItems.contains(mStatusCard)) {
+            mItems.add(getFirstCardPosition(), mStatusCard);
 
             // We also want to refresh the header and the bottom padding.
-            mHeaderListItem.setVisible(false);
+            mHeader.setVisible(false);
             notifyDataSetChanged();
         }
     }
 
     /** Returns whether we have some suggested content to display. */
     private boolean hasSuggestions() {
-        return getItemViewType(FIRST_CARD_POSITION) == NewTabPageListItem.VIEW_TYPE_SNIPPET;
+        for (NewTabPageListItem item : mItems) {
+            if (item instanceof SnippetArticleListItem) return true;
+        }
+        return false;
     }
 
     List<NewTabPageListItem> getItemsForTesting() {
-        return mNewTabPageListItems;
+        return mItems;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java
index e1f1155..1062092 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java
@@ -8,6 +8,7 @@
 import android.content.res.Resources;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.util.AttributeSet;
 import android.view.GestureDetector;
 import android.view.MotionEvent;
@@ -26,13 +27,6 @@
 public class NewTabPageRecyclerView extends RecyclerView {
     private static final String TAG = "NtpCards";
 
-    /**
-     * Positions of key items in the RecyclerView.
-     */
-    private static final int ABOVE_THE_FOLD_ITEM_POSITION = 0;
-    private static final int ARTICLES_HEADER_ITEM_POSITION = 1;
-    private static final int FIRST_CARD_ITEM_POSITION = 2;
-
     private final GestureDetector mGestureDetector;
     private final LinearLayoutManager mLayoutManager;
     private final int mToolbarHeight;
@@ -118,10 +112,6 @@
         super.onLayout(changed, l, t, r, b);
     }
 
-    public LinearLayoutManager getLinearLayoutManager() {
-        return mLayoutManager;
-    }
-
     public void setAboveTheFoldView(View aboveTheFoldView) {
         mAboveTheFoldView = aboveTheFoldView;
     }
@@ -140,8 +130,7 @@
      * distinction can be preserved.
      */
     public void refreshBottomSpacing() {
-        ViewHolder bottomSpacingViewHolder =
-                findViewHolderForAdapterPosition(getAdapter().getItemCount() - 1);
+        ViewHolder bottomSpacingViewHolder = findBottomSpacer();
 
         // It might not be in the layout yet if it's not visible or ready to be displayed.
         if (bottomSpacingViewHolder == null) return;
@@ -158,16 +147,15 @@
         int firstVisiblePos = mLayoutManager.findFirstVisibleItemPosition();
 
         // We have enough items to fill the view, since the snap point item is not even visible.
-        if (firstVisiblePos > ARTICLES_HEADER_ITEM_POSITION) return mMinBottomSpacing;
+        if (firstVisiblePos > getNewTabPageAdapter().getHeaderPosition()) {
+            return mMinBottomSpacing;
+        }
 
-        // The spacing item is the last item, the last content item is directly above that.
-        int lastContentItemPosition = getAdapter().getItemCount() - 2;
-
-        ViewHolder lastContentItem = findViewHolderForAdapterPosition(lastContentItemPosition);
-        ViewHolder snapItem = findViewHolderForAdapterPosition(ARTICLES_HEADER_ITEM_POSITION);
+        ViewHolder lastCard = findLastCard();
+        ViewHolder firstHeader = findHeader();
 
         int bottomSpacing = getHeight() - mToolbarHeight;
-        if (lastContentItem == null || snapItem == null) {
+        if (lastCard == null || firstHeader == null) {
             // This can happen in several cases, where some elements are not visible and the
             // RecyclerView didn't already attach them. We handle it by just adding space to make
             // sure that we never run out and force the UI to jump around and get stuck in a
@@ -178,13 +166,13 @@
             //  - Dismissing a snippet and having the status card coming to take its place.
             //  - Refresh while being below the fold, for example by tapping the status card.
 
-            if (snapItem != null) bottomSpacing -= snapItem.itemView.getTop();
+            if (firstHeader != null) bottomSpacing -= firstHeader.itemView.getTop();
 
             Log.w(TAG, "The RecyclerView items are not attached, can't determine the content "
                             + "height: snap=%s, last=%s. Using full height: %d ",
-                    snapItem, lastContentItem, bottomSpacing);
+                    firstHeader, lastCard, bottomSpacing);
         } else {
-            int contentHeight = lastContentItem.itemView.getBottom() - snapItem.itemView.getTop();
+            int contentHeight = lastCard.itemView.getBottom() - firstHeader.itemView.getTop();
             bottomSpacing -= contentHeight - mCompensationHeight;
         }
 
@@ -204,23 +192,11 @@
     }
 
     /**
-     * Finds the first card in this RecyclerView.
-     * @return The viewholder for the first card or null if no card is available.
-     */
-    private CardViewHolder findFirstCard() {
-        int firstCardIndex = FIRST_CARD_ITEM_POSITION;
-        ViewHolder viewHolder = findViewHolderForAdapterPosition(firstCardIndex);
-        if (!(viewHolder instanceof CardViewHolder)) return null;
-
-        return (CardViewHolder) viewHolder;
-    }
-
-    /**
      * Show the snippets header when the user scrolls down and snippet articles starts reaching the
      * top of the screen.
      */
     public void updateSnippetsHeaderDisplay() {
-        SnippetHeaderViewHolder header = findHeaderView();
+        SnippetHeaderViewHolder header = findHeader();
         if (header == null) return;
 
         // Start doing the calculations if the snippet header is currently shown on screen.
@@ -230,18 +206,54 @@
         refreshBottomSpacing();
     }
 
+    private NewTabPageAdapter getNewTabPageAdapter() {
+        return (NewTabPageAdapter) getAdapter();
+    }
+
     /**
-     * Finds the header view.
-     * @return The ViewHolder of the header or null, if it is not present.
+     * Finds the view holder for the first header.
+     * @return The {@link ViewHolder} of the header, or null if it is not present.
      */
-    private SnippetHeaderViewHolder findHeaderView() {
-        // Get the snippet header view. It is always at position 1
-        ViewHolder viewHolder = findViewHolderForAdapterPosition(ARTICLES_HEADER_ITEM_POSITION);
+    private SnippetHeaderViewHolder findHeader() {
+        ViewHolder viewHolder =
+                findViewHolderForAdapterPosition(getNewTabPageAdapter().getHeaderPosition());
         if (!(viewHolder instanceof SnippetHeaderViewHolder)) return null;
 
         return (SnippetHeaderViewHolder) viewHolder;
     }
 
+    /**
+     * Finds the view holder for the first card.
+     * @return The {@link ViewHolder} for the first card, or null if it is not present.
+     */
+    private CardViewHolder findFirstCard() {
+        ViewHolder viewHolder =
+                findViewHolderForAdapterPosition(getNewTabPageAdapter().getFirstCardPosition());
+        if (!(viewHolder instanceof CardViewHolder)) return null;
+
+        return (CardViewHolder) viewHolder;
+    }
+
+    /**
+     * Finds the view holder for the last card.
+     * @return The {@link ViewHolder} of the last card, or null if it is not present.
+     */
+    private CardViewHolder findLastCard() {
+        ViewHolder viewHolder =
+                findViewHolderForAdapterPosition(getNewTabPageAdapter().getLastCardPosition());
+        if (!(viewHolder instanceof CardViewHolder)) return null;
+
+        return (CardViewHolder) viewHolder;
+    }
+
+    /**
+     * Finds the view holder for the bottom spacer.
+     * @return The {@link ViewHolder} of the bottom spacer, or null if it is not present.
+     */
+    private ViewHolder findBottomSpacer() {
+        return findViewHolderForAdapterPosition(getNewTabPageAdapter().getBottomSpacerPosition());
+    }
+
     /** Called when an item is in the process of being removed from the view. */
     void onItemDismissStarted(View itemView) {
         mCompensationHeight += itemView.getHeight();
@@ -302,7 +314,7 @@
             if (!peekingCardViewHolder.canPeek()) return;
 
             View peekingCardView = findFirstCard().itemView;
-            View headerView = findHeaderView().itemView;
+            View headerView = findHeader().itemView;
             final int peekingHeight = getResources().getDimensionPixelSize(
                     R.dimen.snippets_padding_and_peeking_card_height);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/StatusListItem.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/StatusListItem.java
index 88f33d0..599d94a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/StatusListItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/StatusListItem.java
@@ -14,7 +14,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager;
 import org.chromium.chrome.browser.ntp.UiConfig;
-import org.chromium.chrome.browser.ntp.snippets.DisabledReason;
+import org.chromium.chrome.browser.ntp.snippets.ContentSuggestionsCategoryStatus;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 import org.chromium.chrome.browser.signin.AccountSigninActivity;
 import org.chromium.chrome.browser.signin.SigninAccessPoint;
@@ -49,14 +49,35 @@
             final StatusListItem listItem = (StatusListItem) item;
             mTitleView.setText(listItem.mHeaderStringId);
             mBodyView.setText(listItem.mDescriptionStringId);
-            mActionView.setText(listItem.mActionStringId);
-            mActionView.setOnClickListener(new OnClickListener() {
 
-                @Override
-                public void onClick(View v) {
-                    listItem.performAction(v.getContext());
-                }
-            });
+            if (listItem.hasAction()) {
+                mActionView.setText(listItem.mActionStringId);
+                mActionView.setOnClickListener(new OnClickListener() {
+
+                    @Override
+                    public void onClick(View v) {
+                        listItem.performAction(v.getContext());
+                    }
+                });
+                mActionView.setVisibility(View.VISIBLE);
+            } else {
+                mActionView.setVisibility(View.GONE);
+            }
+        }
+    }
+
+    private static class ErrorListItem extends StatusListItem {
+        public ErrorListItem(int headerStringId, int descriptionStringId) {
+            super(headerStringId, descriptionStringId, 0);
+        }
+        @Override
+        protected void performAction(Context context) {
+            // No action.
+        }
+
+        @Override
+        protected boolean hasAction() {
+            return false;
         }
     }
 
@@ -139,6 +160,26 @@
         }
     }
 
+    private static class CategoryExplicitlyDisabled extends ErrorListItem {
+        public CategoryExplicitlyDisabled() {
+            // TODO(pke): Those are technically the wrong strings, but they roughly fit in this
+            // case. This should only be called when the category has been disabled via enterprise
+            // policy. Replace this with proper error card once it has been specified.
+            super(R.string.ntp_status_card_title_empty, R.string.ntp_status_card_body_empty);
+            Log.d(TAG, "Registering card for error: Category Explicitly Disabled");
+        }
+    }
+
+    private static class ProviderError extends ErrorListItem {
+        public ProviderError() {
+            // TODO(pke): Those are technically the wrong strings, but they roughly fit in this
+            // case. This is only called if NTPSnippetsDatabase encounters an error.
+            // Replace this with proper error card once it has been specified.
+            super(R.string.ntp_status_card_title_empty, R.string.ntp_status_card_body_empty);
+            Log.d(TAG, "Registering card for error: Provider Error");
+        }
+    }
+
     private static final String TAG = "NtpCards";
 
     private final int mHeaderStringId;
@@ -146,37 +187,53 @@
     private final int mActionStringId;
 
     public static StatusListItem create(
-            int disabledReason, NewTabPageAdapter adapter, NewTabPageManager manager) {
-        switch (disabledReason) {
-            case DisabledReason.NONE:
+            int categoryStatus, NewTabPageAdapter adapter, NewTabPageManager manager) {
+        switch (categoryStatus) {
+            case ContentSuggestionsCategoryStatus.AVAILABLE:
+            case ContentSuggestionsCategoryStatus.AVAILABLE_LOADING:
                 return new NoSnippets(adapter);
 
-            case DisabledReason.SIGNED_OUT:
+            case ContentSuggestionsCategoryStatus.SIGNED_OUT:
                 return new SignedOut();
 
-            case DisabledReason.SYNC_DISABLED:
+            case ContentSuggestionsCategoryStatus.SYNC_DISABLED:
                 return new SyncDisabled();
 
-            case DisabledReason.PASSPHRASE_ENCRYPTION_ENABLED:
+            case ContentSuggestionsCategoryStatus.PASSPHRASE_ENCRYPTION_ENABLED:
                 return new PassphraseEncryptionEnabled(manager);
 
-            case DisabledReason.HISTORY_SYNC_STATE_UNKNOWN:
-                // This should only be a transient state: during app launch, or when the sync
-                // settings are being modified, and the user should never see a card showing this.
-                // So let's just use HistorySyncDisabled as fallback.
-                // TODO(dgn): If we add a spinner at some point (e.g. to show that we are fetching
-                // snippets) we could use it here too.
-            case DisabledReason.HISTORY_SYNC_DISABLED:
+            // INITIALIZING should only be a transient state: during app launch, or when the sync
+            // settings are being modified, and the user should never see a card showing this.
+            // So let's just use HistorySyncDisabled as fallback.
+            // TODO(dgn): If we add a spinner at some point (e.g. to show that we are fetching
+            // snippets) we could use it here too.
+            case ContentSuggestionsCategoryStatus.INITIALIZING:
+            case ContentSuggestionsCategoryStatus.HISTORY_SYNC_DISABLED:
                 return new HistorySyncDisabled();
 
-            case DisabledReason.EXPLICITLY_DISABLED:
+            case ContentSuggestionsCategoryStatus.ALL_SUGGESTIONS_EXPLICITLY_DISABLED:
                 Log.wtf(TAG, "FATAL: Attempted to create a status card while the feature should be "
                         + "off.");
                 return null;
 
+            case ContentSuggestionsCategoryStatus.CATEGORY_EXPLICITLY_DISABLED:
+                Log.d(TAG, "Not showing ARTICLES suggestions because this category is disabled.");
+                // TODO(pke): Replace this.
+                return new CategoryExplicitlyDisabled();
+
+            case ContentSuggestionsCategoryStatus.NOT_PROVIDED:
+                Log.wtf(TAG, "FATAL: Attempted to create a status card for content suggestions "
+                                + " when provider for ARTICLES is not registered.");
+                return null;
+
+            case ContentSuggestionsCategoryStatus.LOADING_ERROR:
+                Log.d(TAG, "Not showing ARTICLES suggestions because of provider error.");
+                // TODO(pke): Replace this.
+                return new ProviderError();
+
             default:
                 Log.wtf(TAG, "FATAL: Attempted to create a status card for an unknown value: %d",
-                        disabledReason);
+                        categoryStatus);
                 return null;
         }
     }
@@ -189,6 +246,10 @@
 
     protected abstract void performAction(Context context);
 
+    protected boolean hasAction() {
+        return true;
+    }
+
     @Override
     public int getType() {
         return NewTabPageListItem.VIEW_TYPE_STATUS;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleListItem.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleListItem.java
index 9c78190..59cb780 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleListItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetArticleListItem.java
@@ -20,7 +20,6 @@
     public final String mPreviewText;
     public final String mUrl;
     public final String mAmpUrl;
-    public final String mThumbnailUrl;
     public final long mPublishTimestampMilliseconds;
     public final float mScore;
     public final int mPosition;
@@ -41,21 +40,18 @@
      * @param previewText the snippet preview text
      * @param url the URL of the article
      * @param ampUrl the AMP url for the article (possible for this to be empty)
-     * @param thumbnailUrl the URL of the thumbnail
      * @param timestamp the time in ms when this article was published
      * @param score the score expressing relative quality of the article for the user
      * @param position the position of this article in the list of snippets
      */
     public SnippetArticleListItem(String id, String title, String publisher, String previewText,
-            String url, String ampUrl, String thumbnailUrl, long timestamp, float score,
-            int position) {
+            String url, String ampUrl, long timestamp, float score, int position) {
         mId = id;
         mTitle = title;
         mPublisher = publisher;
         mPreviewText = previewText;
         mUrl = url;
         mAmpUrl = ampUrl;
-        mThumbnailUrl = thumbnailUrl;
         mPublishTimestampMilliseconds = timestamp;
         mScore = score;
         mPosition = position;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java
index 3be0bc6..71918c3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsBridge.java
@@ -28,8 +28,20 @@
     public interface SnippetsObserver {
         void onSnippetsReceived(List<SnippetArticleListItem> snippets);
 
-        /** Called when the service is about to change its state. */
-        void onDisabledReasonChanged(int disabledReason);
+        /** Called when the ARTICLES category changed its status. */
+        void onCategoryStatusChanged(int newStatus);
+    }
+
+    public static boolean isCategoryStatusAvailable(int status) {
+        // Note: This code is duplicated in content_suggestions_category_status.cc.
+        return status == ContentSuggestionsCategoryStatus.AVAILABLE_LOADING
+                || status == ContentSuggestionsCategoryStatus.AVAILABLE;
+    }
+
+    public static boolean isCategoryStatusInitOrAvailable(int status) {
+        // Note: This code is duplicated in content_suggestions_category_status.cc.
+        return status == ContentSuggestionsCategoryStatus.INITIALIZING
+                || isCategoryStatusAvailable(status);
     }
 
     /**
@@ -109,31 +121,29 @@
         nativeSetObserver(mNativeSnippetsBridge, observer == null ? null : this);
     }
 
-    public int getDisabledReason() {
+    public int getCategoryStatus() {
         assert mNativeSnippetsBridge != 0;
-        return nativeGetDisabledReason(mNativeSnippetsBridge);
+        return nativeGetCategoryStatus(mNativeSnippetsBridge);
     }
 
     @CalledByNative
     private void onSnippetsAvailable(String[] ids, String[] titles, String[] urls, String[] ampUrls,
-            String[] thumbnailUrls, String[] previewText, long[] timestamps, String[] publishers,
-            float[] scores) {
+            String[] previewText, long[] timestamps, String[] publishers, float[] scores) {
         assert mNativeSnippetsBridge != 0;
         assert mObserver != null;
 
         List<SnippetArticleListItem> newSnippets = new ArrayList<>(ids.length);
         for (int i = 0; i < ids.length; i++) {
-            newSnippets.add(
-                    new SnippetArticleListItem(ids[i], titles[i], publishers[i], previewText[i],
-                            urls[i], ampUrls[i], thumbnailUrls[i], timestamps[i], scores[i], i));
+            newSnippets.add(new SnippetArticleListItem(ids[i], titles[i], publishers[i],
+                    previewText[i], urls[i], ampUrls[i], timestamps[i], scores[i], i));
         }
 
         mObserver.onSnippetsReceived(newSnippets);
     }
 
     @CalledByNative
-    private void onDisabledReasonChanged(int disabledReason) {
-        if (mObserver != null) mObserver.onDisabledReasonChanged(disabledReason);
+    private void onCategoryStatusChanged(int newStatus) {
+        if (mObserver != null) mObserver.onCategoryStatusChanged(newStatus);
     }
 
     private native long nativeInit(Profile profile);
@@ -146,5 +156,5 @@
             Callback<Boolean> callback, String url);
     private native void nativeFetchImage(
             long nativeNTPSnippetsBridge, String snippetId, Callback<Bitmap> callback);
-    private native int nativeGetDisabledReason(long nativeNTPSnippetsBridge);
+    private native int nativeGetCategoryStatus(long nativeNTPSnippetsBridge);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
index ff2012d..4e81850 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
@@ -4,24 +4,32 @@
 
 package org.chromium.chrome.browser.preferences;
 
+import android.content.Intent;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.preference.Preference;
 import android.preference.PreferenceFragment;
 
+import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.PasswordUIView;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
 import org.chromium.chrome.browser.preferences.datareduction.DataReductionPreferences;
+import org.chromium.chrome.browser.preferences.password.SavePasswordsPreferences;
 import org.chromium.chrome.browser.signin.SigninManager;
 import org.chromium.chrome.browser.signin.SigninManager.SignInStateObserver;
+import org.chromium.chrome.browser.sync.ProfileSyncService;
+import org.chromium.sync.AndroidSyncSettings;
 
 /**
  * The main settings screen, shown when the user first opens Settings.
  */
-public class MainPreferences extends PreferenceFragment implements SignInStateObserver {
+public class MainPreferences extends PreferenceFragment implements SignInStateObserver,
+        Preference.OnPreferenceClickListener {
 
     public static final String PREF_SIGN_IN = "sign_in";
     public static final String PREF_SEARCH_ENGINE = "search_engine";
@@ -34,6 +42,11 @@
     public static final String ACCOUNT_PICKER_DIALOG_TAG = "account_picker_dialog_tag";
     public static final String EXTRA_SHOW_SEARCH_ENGINE_PICKER = "show_search_engine_picker";
 
+    public static final String PREF_MANAGE_ACCOUNT_LINK = "manage_account_link";
+
+    @VisibleForTesting
+    public static final String VIEW_PASSWORDS = "view-passwords";
+
     private SignInPreference mSignInPreference;
     private ManagedPreferenceDelegate mManagedPreferenceDelegate;
 
@@ -77,6 +90,16 @@
         clearSignInPref();
     }
 
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        Intent intent = new Intent(
+                  Intent.ACTION_VIEW,
+                  Uri.parse(PasswordUIView.getAccountDashboardURL()));
+        intent.setPackage(getActivity().getPackageName());
+        getActivity().startActivity(intent);
+        return true;
+    }
+
     private void updatePreferences() {
         if (getPreferenceScreen() != null) getPreferenceScreen().removeAll();
         addPreferencesFromResource(R.xml.main_preferences);
@@ -88,13 +111,31 @@
 
         ChromeBasePreference passwordsPref =
                 (ChromeBasePreference) findPreference(PREF_SAVED_PASSWORDS);
-        if (PasswordUIView.shouldUseSmartLockBranding()) {
-            passwordsPref.setTitle(getResources().getString(
-                    R.string.prefs_smart_lock_for_passwords));
+
+        ProfileSyncService syncService = ProfileSyncService.get();
+
+        if (AndroidSyncSettings.isSyncEnabled(getActivity().getApplicationContext())
+                  && syncService.isBackendInitialized()
+                  && !syncService.isUsingSecondaryPassphrase()
+                  && ChromeFeatureList.isEnabled(VIEW_PASSWORDS)) {
+            passwordsPref.setKey(PREF_MANAGE_ACCOUNT_LINK);
+            passwordsPref.setTitle(R.string.redirect_to_passwords_text);
+            passwordsPref.setSummary(R.string.redirect_to_passwords_link);
+            passwordsPref.setOnPreferenceClickListener(this);
+            passwordsPref.setManagedPreferenceDelegate(null);
+        } else {
+            if (PasswordUIView.shouldUseSmartLockBranding()) {
+                passwordsPref.setTitle(getResources().getString(
+                         R.string.prefs_smart_lock_for_passwords));
+            } else {
+                passwordsPref.setTitle(getResources().getString(
+                          R.string.prefs_saved_passwords));
+            }
+            passwordsPref.setFragment(SavePasswordsPreferences.class.getCanonicalName());
+            setOnOffSummary(passwordsPref,
+                    PrefServiceBridge.getInstance().isRememberPasswordsEnabled());
+            passwordsPref.setManagedPreferenceDelegate(mManagedPreferenceDelegate);
         }
-        setOnOffSummary(passwordsPref,
-                PrefServiceBridge.getInstance().isRememberPasswordsEnabled());
-        passwordsPref.setManagedPreferenceDelegate(mManagedPreferenceDelegate);
 
         Preference homepagePref = findPreference(PREF_HOMEPAGE);
         if (HomepageManager.shouldShowHomepageSetting()) {
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 9494600..799223f 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -336,6 +336,12 @@
       <message name="IDS_MANAGE_PASSWORDS_TEXT" desc="Text for link to manage passwords on Account Central.">
         View and manage saved passwords at <ph name="BEGIN_LINK">&lt;link&gt;</ph>passwords.google.com<ph name="END_LINK">&lt;/link&gt;</ph>
       </message>
+      <message name="IDS_REDIRECT_TO_PASSWORDS_TEXT" desc="Title of password settings entry in main Chrome preferences for users who will be redirected to passwords.google.com to manage their passwords.">
+        Saved passwords
+      </message>
+      <message name="IDS_REDIRECT_TO_PASSWORDS_LINK" desc="Hostname of the site where users will go if they tap the passwords settings in main Chrome preferences.">
+        passwords.google.com
+      </message>
       <message name="IDS_SAVED_PASSWORDS_NONE_TEXT" desc="Text when there are no saved passwords/exceptions.">
         Saved passwords will appear here.
       </message>
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 49956971..c3cbd19d 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -1208,6 +1208,7 @@
   "javatests/src/org/chromium/chrome/browser/precache/PrecacheLauncherTest.java",
   "javatests/src/org/chromium/chrome/browser/precache/PrecacheControllerTest.java",
   "javatests/src/org/chromium/chrome/browser/precache/PrecacheUMATest.java",
+  "javatests/src/org/chromium/chrome/browser/preferences/PasswordViewingTypeTest.java",
   "javatests/src/org/chromium/chrome/browser/preferences/PreferencesTest.java",
   "javatests/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoUtilsTest.java",
   "javatests/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferencesTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PasswordViewingTypeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PasswordViewingTypeTest.java
new file mode 100644
index 0000000..9a3a7b8
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/PasswordViewingTypeTest.java
@@ -0,0 +1,165 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.preferences;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.preferences.password.SavePasswordsPreferences;
+import org.chromium.chrome.browser.sync.ProfileSyncService;
+import org.chromium.content.browser.test.NativeLibraryTestBase;
+import org.chromium.sync.AndroidSyncSettings;
+import org.chromium.sync.signin.AccountManagerHelper;
+import org.chromium.sync.test.util.AccountHolder;
+import org.chromium.sync.test.util.MockAccountManager;
+import org.chromium.sync.test.util.MockSyncContentResolverDelegate;
+
+/**
+ * Tests for verifying whether users are presented with the correct option of viewing
+ * passwords according to the user group they belong to (syncing with sync passphrase,
+ * syncing without sync passsphrase, non-syncing).
+ */
+
+public class PasswordViewingTypeTest extends NativeLibraryTestBase {
+
+    private MainPreferences mMainPreferences;
+    private ChromeBasePreference mPasswordsPref;
+    private static final String DEFAULT_ACCOUNT = "test@gmail.com";
+    private Context mContext;
+    private MockSyncContentResolverDelegate mSyncContentResolverDelegate;
+    private String mAuthority;
+    private Account mAccount;
+    private MockAccountManager mAccountManager;
+
+    @Override
+    protected void setUp() throws Exception {
+
+        mSyncContentResolverDelegate = new MockSyncContentResolverDelegate();
+        mContext = getInstrumentation().getTargetContext();
+        mMainPreferences = (MainPreferences) startMainPreferences(getInstrumentation(),
+            mContext).getFragmentForTest();
+        mPasswordsPref = (ChromeBasePreference) mMainPreferences.findPreference(
+                MainPreferences.PREF_SAVED_PASSWORDS);
+        setupTestAccount(mContext);
+        AndroidSyncSettings.overrideForTests(mContext, mSyncContentResolverDelegate);
+        mAuthority = AndroidSyncSettings.getContractAuthority(mContext);
+        AndroidSyncSettings.updateAccount(mContext, mAccount);
+        super.setUp();
+        loadNativeLibraryAndInitBrowserProcess();
+    }
+
+    private void setupTestAccount(Context context) {
+        mAccountManager = new MockAccountManager(context, context);
+        AccountManagerHelper.overrideAccountManagerHelperForTests(context, mAccountManager);
+        mAccount = AccountManagerHelper.createAccountFromName("account@example.com");
+        AccountHolder.Builder accountHolder =
+                AccountHolder.create().account(mAccount).password("password").alwaysAccept(true);
+        mAccountManager.addAccountHolderExplicitly(accountHolder.build());
+    }
+
+    /**
+     * Launches the main preferences.
+     */
+    private static Preferences startMainPreferences(Instrumentation instrumentation,
+            final Context mContext) {
+        Intent intent = PreferencesLauncher.createIntentForSettingsPage(mContext,
+                MainPreferences.class.getName());
+        Activity activity = (Preferences) instrumentation.startActivitySync(intent);
+        return (Preferences) activity;
+    }
+
+    /**
+     * Override ProfileSyncService using FakeProfileSyncService.
+     */
+    private void overrideProfileSyncService(final boolean usingPassphrase) {
+        class FakeProfileSyncService extends ProfileSyncService {
+
+            @Override
+            public boolean isUsingSecondaryPassphrase() {
+                return usingPassphrase;
+            }
+
+            @Override
+            public boolean isBackendInitialized() {
+                return true;
+            }
+        }
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                ProfileSyncService.overrideForTests(new FakeProfileSyncService());
+            }
+        });
+    }
+
+    /**
+     * Turn syncability on/off.
+     */
+    private void setSyncability(boolean syncState) throws InterruptedException {
+
+        // Turn on syncability
+        mSyncContentResolverDelegate.setMasterSyncAutomatically(syncState);
+        mSyncContentResolverDelegate.waitForLastNotificationCompleted();
+
+        // First sync
+        mSyncContentResolverDelegate.setIsSyncable(mAccount, mAuthority, (syncState) ? 1 : 0);
+        mSyncContentResolverDelegate.waitForLastNotificationCompleted();
+
+        if (syncState) {
+            mSyncContentResolverDelegate.setSyncAutomatically(mAccount, mAuthority, syncState);
+            mSyncContentResolverDelegate.waitForLastNotificationCompleted();
+        }
+    }
+
+    /**
+     * Verifies that sync settings are being set up correctly in the case of redirecting users.
+     */
+    @SmallTest
+    @CommandLineFlags.Add("enable-features=" + MainPreferences.VIEW_PASSWORDS)
+    @Feature({"Sync"})
+    public void testUserRedirectSyncSettings() throws InterruptedException {
+        setSyncability(true);
+        overrideProfileSyncService(false);
+        assertTrue(AndroidSyncSettings.isSyncEnabled(mContext));
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                assertTrue(ProfileSyncService.get().isBackendInitialized());
+                assertFalse(ProfileSyncService.get().isUsingSecondaryPassphrase());
+            }
+        });
+    }
+
+    /**
+     * Verifies that syncing users with a custom passphrase are allowed to
+     * natively view passwords.
+     */
+    @SmallTest
+    public void testSyncingNativePasswordView() throws InterruptedException {
+        setSyncability(true);
+        overrideProfileSyncService(true);
+        assertEquals(SavePasswordsPreferences.class.getCanonicalName(),
+                mPasswordsPref.getFragment());
+        assertNotNull(mMainPreferences.getActivity().getIntent());
+    }
+
+    /**
+     * Verifies that non-syncing users are allowed to natively view passwords.
+     */
+    @SmallTest
+    public void testNonSyncingNativePasswordView() throws InterruptedException {
+        setSyncability(false);
+        assertEquals(SavePasswordsPreferences.class.getCanonicalName(),
+                mPasswordsPref.getFragment());
+    }
+
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
index 3de31cf..4bea2a1 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
@@ -12,7 +12,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager;
-import org.chromium.chrome.browser.ntp.snippets.DisabledReason;
+import org.chromium.chrome.browser.ntp.snippets.ContentSuggestionsCategoryStatus;
 import org.chromium.chrome.browser.ntp.snippets.SnippetArticleListItem;
 import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge;
 import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge.SnippetsObserver;
@@ -85,7 +85,7 @@
         // The adapter should ignore any new incoming data.
         mSnippetsObserver.onSnippetsReceived(
                 Arrays.asList(new SnippetArticleListItem[] {new SnippetArticleListItem(
-                        "foo", "title1", "pub1", "txt1", "foo", "bar", null, 0, 0, 0)}));
+                        "foo", "title1", "pub1", "txt1", "foo", "bar", 0, 0, 0)}));
         assertEquals(loadedItems, ntpa.getItemsForTesting());
     }
 
@@ -120,7 +120,7 @@
         // The adapter should ignore any new incoming data.
         mSnippetsObserver.onSnippetsReceived(
                 Arrays.asList(new SnippetArticleListItem[] {new SnippetArticleListItem(
-                        "foo", "title1", "pub1", "txt1", "foo", "bar", null, 0, 0, 0)}));
+                        "foo", "title1", "pub1", "txt1", "foo", "bar", 0, 0, 0)}));
         assertEquals(loadedItems, ntpa.getItemsForTesting());
     }
 
@@ -139,16 +139,16 @@
 
         // If we get told that snippets are enabled, we just leave the current
         // ones there and not clear.
-        mSnippetsObserver.onDisabledReasonChanged(DisabledReason.NONE);
+        mSnippetsObserver.onCategoryStatusChanged(ContentSuggestionsCategoryStatus.AVAILABLE);
         assertEquals(3 + snippets.size(), ntpa.getItemCount());
 
         // When snippets are disabled, we clear them and we should go back to
         // the situation with the status card.
-        mSnippetsObserver.onDisabledReasonChanged(DisabledReason.SIGNED_OUT);
+        mSnippetsObserver.onCategoryStatusChanged(ContentSuggestionsCategoryStatus.SIGNED_OUT);
         assertEquals(4, ntpa.getItemCount());
 
         // The adapter should now be waiting for new snippets.
-        mSnippetsObserver.onDisabledReasonChanged(DisabledReason.NONE);
+        mSnippetsObserver.onCategoryStatusChanged(ContentSuggestionsCategoryStatus.AVAILABLE);
         mSnippetsObserver.onSnippetsReceived(snippets);
         assertEquals(3 + snippets.size(), ntpa.getItemCount());
     }
@@ -164,28 +164,28 @@
 
         List<SnippetArticleListItem> snippets = createDummySnippets();
 
-        // By default, state is DisabledReason.NONE, so we can load snippets
+        // By default, status is INITIALIZING, so we can load snippets
         mSnippetsObserver.onSnippetsReceived(snippets);
         assertEquals(3 + snippets.size(), ntpa.getItemCount());
 
         // If we have snippets, we should not load the new list.
         snippets.add(new SnippetArticleListItem("https://site.com/url1", "title1", "pub1", "txt1",
-                "https://site.com/url1", "https://amp.site.com/url1", null, 0, 0, 0));
+                "https://site.com/url1", "https://amp.site.com/url1", 0, 0, 0));
         mSnippetsObserver.onSnippetsReceived(snippets);
         assertEquals(3 + snippets.size() - 1, ntpa.getItemCount());
 
         // When snippets are disabled, we should not be able to load them
-        mSnippetsObserver.onDisabledReasonChanged(DisabledReason.SIGNED_OUT);
+        mSnippetsObserver.onCategoryStatusChanged(ContentSuggestionsCategoryStatus.SIGNED_OUT);
         mSnippetsObserver.onSnippetsReceived(snippets);
         assertEquals(4, ntpa.getItemCount());
 
-        // HISTORY_SYNC_STATE_UNKNOWN lets us load snippets still.
-        mSnippetsObserver.onDisabledReasonChanged(DisabledReason.HISTORY_SYNC_STATE_UNKNOWN);
+        // INITIALIZING lets us load snippets still.
+        mSnippetsObserver.onCategoryStatusChanged(ContentSuggestionsCategoryStatus.INITIALIZING);
         mSnippetsObserver.onSnippetsReceived(snippets);
         assertEquals(3 + snippets.size(), ntpa.getItemCount());
 
         // The adapter should now be waiting for new snippets.
-        mSnippetsObserver.onDisabledReasonChanged(DisabledReason.NONE);
+        mSnippetsObserver.onCategoryStatusChanged(ContentSuggestionsCategoryStatus.AVAILABLE);
         mSnippetsObserver.onSnippetsReceived(snippets);
         assertEquals(3 + snippets.size(), ntpa.getItemCount());
     }
@@ -193,11 +193,11 @@
     private List<SnippetArticleListItem> createDummySnippets() {
         List<SnippetArticleListItem> snippets = new ArrayList<>();
         snippets.add(new SnippetArticleListItem("https://site.com/url1", "title1", "pub1", "txt1",
-                "https://site.com/url1", "https://amp.site.com/url1", null, 0, 0, 0));
+                "https://site.com/url1", "https://amp.site.com/url1", 0, 0, 0));
         snippets.add(new SnippetArticleListItem("https://site.com/url2", "title2", "pub2", "txt2",
-                "https://site.com/url2", "https://amp.site.com/url2", null, 0, 0, 0));
+                "https://site.com/url2", "https://amp.site.com/url2", 0, 0, 0));
         snippets.add(new SnippetArticleListItem("https://site.com/url3", "title3", "pub3", "txt3",
-                "https://site.com/url3", "https://amp.site.com/url3", null, 0, 0, 0));
+                "https://site.com/url3", "https://amp.site.com/url3", 0, 0, 0));
         return snippets;
     }
 }
diff --git a/chrome/app/chrome_exe_main_win.cc b/chrome/app/chrome_exe_main_win.cc
index 532621d..2ec47c28 100644
--- a/chrome/app/chrome_exe_main_win.cc
+++ b/chrome/app/chrome_exe_main_win.cc
@@ -126,7 +126,6 @@
   const base::CommandLine* command_line =
       base::CommandLine::ForCurrentProcess();
   bool per_monitor_dpi_switch =
-      command_line->HasSwitch(switches::kEnablePerMonitorDpi) &&
       !command_line->HasSwitch(switches::kDisablePerMonitorDpi);
   PROCESS_DPI_AWARENESS process_dpi_awareness =
       allowed_platform && per_monitor_dpi_switch
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 5dade7f..d4f18e5 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -503,6 +503,8 @@
     sources += rebase_path(gypi_values.chrome_browser_spellchecker_sources,
                            ".",
                            "//chrome")
+    deps += [ "//components/spellcheck/common:common" ]
+
     if (!is_android) {
       deps += [ "//third_party/hunspell" ]
     }
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 6364578..80be2c4d 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -53,6 +53,7 @@
 #include "components/search/search_switches.h"
 #include "components/security_state/switches.h"
 #include "components/signin/core/common/signin_switches.h"
+#include "components/spellcheck/common/spellcheck_switches.h"
 #include "components/ssl_config/ssl_config_switches.h"
 #include "components/sync_driver/sync_driver_switches.h"
 #include "components/tracing/common/tracing_switches.h"
@@ -830,7 +831,7 @@
 #if defined(ENABLE_SPELLCHECK) && defined(OS_ANDROID)
     {"enable-android-spellchecker", IDS_OPTIONS_ENABLE_SPELLCHECK,
      IDS_OPTIONS_ENABLE_ANDROID_SPELLCHECKER_DESCRIPTION, kOsAndroid,
-     SINGLE_VALUE_TYPE(switches::kEnableAndroidSpellChecker)},
+     SINGLE_VALUE_TYPE(spellcheck::switches::kEnableAndroidSpellChecker)},
 #endif
     {"enable-scroll-prediction", IDS_FLAGS_SCROLL_PREDICTION_NAME,
      IDS_FLAGS_SCROLL_PREDICTION_DESCRIPTION, kOsDesktop,
@@ -1220,7 +1221,8 @@
     {"enable-spelling-feedback-field-trial",
      IDS_FLAGS_SPELLING_FEEDBACK_FIELD_TRIAL_NAME,
      IDS_FLAGS_SPELLING_FEEDBACK_FIELD_TRIAL_DESCRIPTION, kOsAll,
-     SINGLE_VALUE_TYPE(switches::kEnableSpellingFeedbackFieldTrial)},
+     SINGLE_VALUE_TYPE(
+         spellcheck::switches::kEnableSpellingFeedbackFieldTrial)},
 #endif
     {"enable-webgl-draft-extensions", IDS_FLAGS_WEBGL_DRAFT_EXTENSIONS_NAME,
      IDS_FLAGS_WEBGL_DRAFT_EXTENSIONS_DESCRIPTION, kOsAll,
diff --git a/chrome/browser/after_startup_task_utils.cc b/chrome/browser/after_startup_task_utils.cc
index f61fec8..0d0eab6 100644
--- a/chrome/browser/after_startup_task_utils.cc
+++ b/chrome/browser/after_startup_task_utils.cc
@@ -13,7 +13,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/process/process_info.h"
 #include "base/rand_util.h"
-#include "base/synchronization/cancellation_flag.h"
+#include "base/synchronization/atomic_flag.h"
 #include "base/task_runner.h"
 #include "base/tracked_objects.h"
 #include "build/build_config.h"
@@ -28,7 +28,6 @@
 using content::BrowserThread;
 using content::WebContents;
 using content::WebContentsObserver;
-using StartupCompleteFlag = base::CancellationFlag;
 
 namespace {
 
@@ -45,7 +44,7 @@
 };
 
 // The flag may be read on any thread, but must only be set on the UI thread.
-base::LazyInstance<StartupCompleteFlag>::Leaky g_startup_complete_flag;
+base::LazyInstance<base::AtomicFlag>::Leaky g_startup_complete_flag;
 
 // The queue may only be accessed on the UI thread.
 base::LazyInstance<std::deque<AfterStartupTask*>>::Leaky g_after_startup_tasks;
diff --git a/chrome/browser/android/ntp/ntp_snippets_bridge.cc b/chrome/browser/android/ntp/ntp_snippets_bridge.cc
index 373de14c8..f6a6b5a 100644
--- a/chrome/browser/android/ntp/ntp_snippets_bridge.cc
+++ b/chrome/browser/android/ntp/ntp_snippets_bridge.cc
@@ -12,12 +12,13 @@
 #include "base/android/jni_string.h"
 #include "base/callback.h"
 #include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/ntp_snippets/content_suggestions_service_factory.h"
 #include "chrome/browser/ntp_snippets/ntp_snippets_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "components/history/core/browser/history_service.h"
-#include "components/ntp_snippets/ntp_snippet.h"
+#include "components/ntp_snippets/content_suggestion.h"
 #include "components/ntp_snippets/ntp_snippets_service.h"
 #include "jni/SnippetsBridge_jni.h"
 #include "ui/gfx/android/java_bitmap.h"
@@ -31,6 +32,8 @@
 using base::android::ToJavaFloatArray;
 using base::android::ScopedJavaGlobalRef;
 using base::android::ScopedJavaLocalRef;
+using ntp_snippets::ContentSuggestionsCategory;
+using ntp_snippets::ContentSuggestionsCategoryStatus;
 
 namespace {
 
@@ -68,13 +71,14 @@
 
 NTPSnippetsBridge::NTPSnippetsBridge(JNIEnv* env,
                                      const JavaParamRef<jobject>& j_profile)
-    : snippet_service_observer_(this), weak_ptr_factory_(this) {
+    : content_suggestions_service_observer_(this), weak_ptr_factory_(this) {
   Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile);
-  ntp_snippets_service_ = NTPSnippetsServiceFactory::GetForProfile(profile);
+  content_suggestions_service_ =
+      ContentSuggestionsServiceFactory::GetForProfile(profile);
   history_service_ =
       HistoryServiceFactory::GetForProfile(profile,
                                            ServiceAccessType::EXPLICIT_ACCESS);
-  snippet_service_observer_.Add(ntp_snippets_service_);
+  content_suggestions_service_observer_.Add(content_suggestions_service_);
 }
 
 void NTPSnippetsBridge::Destroy(JNIEnv* env, const JavaParamRef<jobject>& obj) {
@@ -85,7 +89,7 @@
                                     const JavaParamRef<jobject>& obj,
                                     const JavaParamRef<jobject>& j_observer) {
   observer_.Reset(env, j_observer);
-  NTPSnippetsServiceLoaded();
+  OnNewSuggestions();
 }
 
 void NTPSnippetsBridge::FetchImage(JNIEnv* env,
@@ -93,7 +97,7 @@
                                    const JavaParamRef<jstring>& snippet_id,
                                    const JavaParamRef<jobject>& j_callback) {
   base::android::ScopedJavaGlobalRef<jobject> callback(j_callback);
-  ntp_snippets_service_->FetchSuggestionImage(
+  content_suggestions_service_->FetchSuggestionImage(
       ConvertJavaStringToUTF8(env, snippet_id),
       base::Bind(&NTPSnippetsBridge::OnImageFetched,
                  weak_ptr_factory_.GetWeakPtr(), callback));
@@ -102,7 +106,8 @@
 void NTPSnippetsBridge::DiscardSnippet(JNIEnv* env,
                                        const JavaParamRef<jobject>& obj,
                                        const JavaParamRef<jstring>& id) {
-  ntp_snippets_service_->DiscardSuggestion(ConvertJavaStringToUTF8(env, id));
+  content_suggestions_service_->DismissSuggestion(
+      ConvertJavaStringToUTF8(env, id));
 }
 
 void NTPSnippetsBridge::SnippetVisited(JNIEnv* env,
@@ -118,14 +123,15 @@
       &tracker_);
 }
 
-int NTPSnippetsBridge::GetDisabledReason(JNIEnv* env,
+int NTPSnippetsBridge::GetCategoryStatus(JNIEnv* env,
                                          const JavaParamRef<jobject>& obj) {
-  return static_cast<int>(ntp_snippets_service_->disabled_reason());
+  return static_cast<int>(content_suggestions_service_->GetCategoryStatus(
+      ContentSuggestionsCategory::ARTICLES));
 }
 
 NTPSnippetsBridge::~NTPSnippetsBridge() {}
 
-void NTPSnippetsBridge::NTPSnippetsServiceLoaded() {
+void NTPSnippetsBridge::OnNewSuggestions() {
   if (observer_.is_null())
     return;
 
@@ -137,24 +143,23 @@
   // URL for the AMP version of the article if it exists. This will be used as
   // the URL to direct the user to on tap.
   std::vector<std::string> amp_urls;
-  std::vector<std::string> thumbnail_urls;
   std::vector<std::string> snippets;
   std::vector<int64_t> timestamps;
   std::vector<std::string> publishers;
   std::vector<float> scores;
-  for (const std::unique_ptr<ntp_snippets::NTPSnippet>& snippet :
-       ntp_snippets_service_->snippets()) {
-    ids.push_back(snippet->id());
-    titles.push_back(snippet->title());
+  for (const ntp_snippets::ContentSuggestion& suggestion :
+       content_suggestions_service_->GetSuggestionsForCategory(
+           ContentSuggestionsCategory::ARTICLES)) {
+    ids.push_back(suggestion.id());
+    titles.push_back(suggestion.title());
     // The url from source_info is a url for a site that is one of the
     // HOST_RESTRICT parameters, so this is preferred.
-    urls.push_back(snippet->best_source().url.spec());
-    amp_urls.push_back(snippet->best_source().amp_url.spec());
-    thumbnail_urls.push_back(snippet->salient_image_url().spec());
-    snippets.push_back(snippet->snippet());
-    timestamps.push_back(snippet->publish_date().ToJavaTime());
-    publishers.push_back(snippet->best_source().publisher_name);
-    scores.push_back(snippet->score());
+    urls.push_back(suggestion.url().spec());
+    amp_urls.push_back(suggestion.amp_url().spec());
+    snippets.push_back(suggestion.snippet_text());
+    timestamps.push_back(suggestion.publish_date().ToJavaTime());
+    publishers.push_back(suggestion.publisher_name());
+    scores.push_back(suggestion.score());
   }
 
   JNIEnv* env = base::android::AttachCurrentThread();
@@ -163,25 +168,26 @@
       ToJavaArrayOfStrings(env, titles).obj(),
       ToJavaArrayOfStrings(env, urls).obj(),
       ToJavaArrayOfStrings(env, amp_urls).obj(),
-      ToJavaArrayOfStrings(env, thumbnail_urls).obj(),
       ToJavaArrayOfStrings(env, snippets).obj(),
       ToJavaLongArray(env, timestamps).obj(),
       ToJavaArrayOfStrings(env, publishers).obj(),
       ToJavaFloatArray(env, scores).obj());
 }
 
-void NTPSnippetsBridge::NTPSnippetsServiceShutdown() {
-  observer_.Reset();
-  snippet_service_observer_.Remove(ntp_snippets_service_);
+void NTPSnippetsBridge::OnCategoryStatusChanged(
+    ContentSuggestionsCategory category,
+    ContentSuggestionsCategoryStatus new_status) {
+  if (category != ContentSuggestionsCategory::ARTICLES)
+    return;
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_SnippetsBridge_onCategoryStatusChanged(env, observer_.obj(),
+                                              static_cast<int>(new_status));
 }
 
-void NTPSnippetsBridge::NTPSnippetsServiceDisabledReasonChanged(
-    ntp_snippets::DisabledReason disabled_reason) {
-  // The user signed out or disabled sync. Since snippets rely on those, we
-  // clear them to be consistent with the initially signed out state.
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_SnippetsBridge_onDisabledReasonChanged(
-      env, observer_.obj(), static_cast<int>(disabled_reason));
+void NTPSnippetsBridge::ContentSuggestionsServiceShutdown() {
+  observer_.Reset();
+  content_suggestions_service_observer_.Remove(content_suggestions_service_);
 }
 
 void NTPSnippetsBridge::OnImageFetched(ScopedJavaGlobalRef<jobject> callback,
diff --git a/chrome/browser/android/ntp/ntp_snippets_bridge.h b/chrome/browser/android/ntp/ntp_snippets_bridge.h
index 11dc4dc..3c06440 100644
--- a/chrome/browser/android/ntp/ntp_snippets_bridge.h
+++ b/chrome/browser/android/ntp/ntp_snippets_bridge.h
@@ -12,7 +12,9 @@
 #include "base/scoped_observer.h"
 #include "base/task/cancelable_task_tracker.h"
 #include "components/history/core/browser/history_service.h"
-#include "components/ntp_snippets/ntp_snippets_service.h"
+#include "components/ntp_snippets/content_suggestions_category.h"
+#include "components/ntp_snippets/content_suggestions_category_status.h"
+#include "components/ntp_snippets/content_suggestions_service.h"
 
 namespace gfx {
 class Image;
@@ -20,7 +22,8 @@
 
 // The C++ counterpart to SnippetsBridge.java. Enables Java code to access
 // the list of snippets to show on the NTP
-class NTPSnippetsBridge : public ntp_snippets::NTPSnippetsServiceObserver {
+class NTPSnippetsBridge
+    : public ntp_snippets::ContentSuggestionsService::Observer {
  public:
   NTPSnippetsBridge(JNIEnv* env,
                     const base::android::JavaParamRef<jobject>& j_profile);
@@ -46,9 +49,9 @@
                       const base::android::JavaParamRef<jobject>& callback,
                       const base::android::JavaParamRef<jstring>& jurl);
 
-  // Returns a reason why the snippet service is disabled, or 0 if it isn't.
-  // See NTPSnippetsService::DisabledReason for more info.
-  int GetDisabledReason(JNIEnv* env,
+  // Returns the status of the ARTICLES category.
+  // See ContentSuggestionsCategoryStatus for more info.
+  int GetCategoryStatus(JNIEnv* env,
                         const base::android::JavaParamRef<jobject>& obj);
 
   static bool Register(JNIEnv* env);
@@ -56,25 +59,27 @@
  private:
   ~NTPSnippetsBridge() override;
 
-  // NTPSnippetsServiceObserver overrides
-  void NTPSnippetsServiceLoaded() override;
-  void NTPSnippetsServiceShutdown() override;
-  void NTPSnippetsServiceDisabledReasonChanged(
-      ntp_snippets::DisabledReason disabled_reason) override;
+  // ContentSuggestionsService::Observer overrides
+  void OnNewSuggestions() override;
+  void OnCategoryStatusChanged(
+      ntp_snippets::ContentSuggestionsCategory category,
+      ntp_snippets::ContentSuggestionsCategoryStatus new_status) override;
+  void ContentSuggestionsServiceShutdown() override;
 
   void OnImageFetched(base::android::ScopedJavaGlobalRef<jobject> callback,
                       const std::string& snippet_id,
                       const gfx::Image& image);
 
-  ntp_snippets::NTPSnippetsService* ntp_snippets_service_;
+  ntp_snippets::ContentSuggestionsService* content_suggestions_service_;
   history::HistoryService* history_service_;
   base::CancelableTaskTracker tracker_;
 
+  ScopedObserver<ntp_snippets::ContentSuggestionsService,
+                 ntp_snippets::ContentSuggestionsService::Observer>
+      content_suggestions_service_observer_;
+
   // Used to notify the Java side when new snippets have been fetched.
   base::android::ScopedJavaGlobalRef<jobject> observer_;
-  ScopedObserver<ntp_snippets::NTPSnippetsService,
-                 ntp_snippets::NTPSnippetsServiceObserver>
-      snippet_service_observer_;
 
   base::WeakPtrFactory<NTPSnippetsBridge> weak_ptr_factory_;
 
diff --git a/chrome/browser/browsing_data/autofill_counter_browsertest.cc b/chrome/browser/browsing_data/autofill_counter_browsertest.cc
index c9a39a07..08f8bbe 100644
--- a/chrome/browser/browsing_data/autofill_counter_browsertest.cc
+++ b/chrome/browser/browsing_data/autofill_counter_browsertest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/browsing_data/autofill_counter.h"
+#include "components/browsing_data/core/counters/autofill_counter.h"
 
 #include "base/guid.h"
 #include "base/macros.h"
@@ -142,6 +142,10 @@
         browsing_data::prefs::kDeleteTimePeriod, static_cast<int>(period));
   }
 
+  scoped_refptr<autofill::AutofillWebDataService> GetWebDataService() {
+    return web_data_service_;
+  }
+
   // Callback and result retrieval ---------------------------------------------
 
   void WaitForCounting() {
@@ -169,8 +173,9 @@
     finished_ = result->Finished();
 
     if (finished_) {
-      AutofillCounter::AutofillResult* autofill_result =
-          static_cast<AutofillCounter::AutofillResult*>(result.get());
+      browsing_data::AutofillCounter::AutofillResult* autofill_result =
+          static_cast<browsing_data::AutofillCounter::AutofillResult*>(
+              result.get());
 
       num_suggestions_ = autofill_result->Value();
       num_credit_cards_ = autofill_result->num_credit_cards();
@@ -203,7 +208,7 @@
   SetAutofillDeletionPref(false);
 
   Profile* profile = browser()->profile();
-  AutofillCounter counter(profile);
+  browsing_data::AutofillCounter counter(GetWebDataService());
   counter.Init(profile->GetPrefs(), base::Bind(&AutofillCounterTest::Callback,
                                                base::Unretained(this)));
   counter.Restart();
@@ -214,7 +219,7 @@
 // Tests that we count the correct number of autocomplete suggestions.
 IN_PROC_BROWSER_TEST_F(AutofillCounterTest, AutocompleteSuggestions) {
   Profile* profile = browser()->profile();
-  AutofillCounter counter(profile);
+  browsing_data::AutofillCounter counter(GetWebDataService());
   counter.Init(profile->GetPrefs(), base::Bind(&AutofillCounterTest::Callback,
                                                base::Unretained(this)));
   counter.Restart();
@@ -250,7 +255,7 @@
 // Tests that we count the correct number of credit cards.
 IN_PROC_BROWSER_TEST_F(AutofillCounterTest, CreditCards) {
   Profile* profile = browser()->profile();
-  AutofillCounter counter(profile);
+  browsing_data::AutofillCounter counter(GetWebDataService());
   counter.Init(profile->GetPrefs(), base::Bind(&AutofillCounterTest::Callback,
                                                base::Unretained(this)));
   counter.Restart();
@@ -286,7 +291,7 @@
 // Tests that we count the correct number of addresses.
 IN_PROC_BROWSER_TEST_F(AutofillCounterTest, Addresses) {
   Profile* profile = browser()->profile();
-  AutofillCounter counter(profile);
+  browsing_data::AutofillCounter counter(GetWebDataService());
   counter.Init(profile->GetPrefs(), base::Bind(&AutofillCounterTest::Callback,
                                                base::Unretained(this)));
   counter.Restart();
@@ -336,7 +341,7 @@
   AddAddress("John", "Smith", "Side Street 47");
 
   Profile* profile = browser()->profile();
-  AutofillCounter counter(profile);
+  browsing_data::AutofillCounter counter(GetWebDataService());
   counter.Init(profile->GetPrefs(), base::Bind(&AutofillCounterTest::Callback,
                                                base::Unretained(this)));
   counter.Restart();
@@ -391,7 +396,7 @@
   };
 
   Profile* profile = browser()->profile();
-  AutofillCounter counter(profile);
+  browsing_data::AutofillCounter counter(GetWebDataService());
   counter.Init(profile->GetPrefs(), base::Bind(&AutofillCounterTest::Callback,
                                                base::Unretained(this)));
 
diff --git a/chrome/browser/browsing_data/browsing_data_counter_factory.cc b/chrome/browser/browsing_data/browsing_data_counter_factory.cc
index 020a70d3..0946701 100644
--- a/chrome/browser/browsing_data/browsing_data_counter_factory.cc
+++ b/chrome/browser/browsing_data/browsing_data_counter_factory.cc
@@ -4,22 +4,40 @@
 
 #include "chrome/browser/browsing_data/browsing_data_counter_factory.h"
 
+#include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/memory/ptr_util.h"
-#include "chrome/browser/browsing_data/autofill_counter.h"
 #include "chrome/browser/browsing_data/browsing_data_counter_utils.h"
 #include "chrome/browser/browsing_data/cache_counter.h"
 #include "chrome/browser/browsing_data/downloads_counter.h"
-#include "chrome/browser/browsing_data/history_counter.h"
 #include "chrome/browser/browsing_data/media_licenses_counter.h"
-#include "chrome/browser/browsing_data/passwords_counter.h"
+#include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/history/web_history_service_factory.h"
+#include "chrome/browser/password_manager/password_store_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/web_data_service_factory.h"
+#include "components/browser_sync/browser/profile_sync_service.h"
+#include "components/browsing_data/core/counters/autofill_counter.h"
 #include "components/browsing_data/core/counters/browsing_data_counter.h"
+#include "components/browsing_data/core/counters/history_counter.h"
+#include "components/browsing_data/core/counters/passwords_counter.h"
 #include "components/browsing_data/core/pref_names.h"
+#include "components/history/core/browser/web_history_service.h"
+#include "components/password_manager/core/browser/password_store.h"
 
 #if defined(ENABLE_EXTENSIONS)
 #include "chrome/browser/browsing_data/hosted_apps_counter.h"
 #endif
 
+namespace {
+
+history::WebHistoryService* GetUpdatedWebHistoryService(Profile* profile) {
+  return WebHistoryServiceFactory::GetForProfile(profile);
+}
+
+}  // namespace
+
 // static
 std::unique_ptr<browsing_data::BrowsingDataCounter>
 BrowsingDataCounterFactory::GetForProfileAndPref(Profile* profile,
@@ -27,17 +45,29 @@
   if (!AreCountersEnabled())
     return nullptr;
 
-  if (pref_name == browsing_data::prefs::kDeleteBrowsingHistory)
-    return base::MakeUnique<HistoryCounter>(profile);
+  if (pref_name == browsing_data::prefs::kDeleteBrowsingHistory) {
+    return base::MakeUnique<browsing_data::HistoryCounter>(
+        HistoryServiceFactory::GetForProfile(
+            profile, ServiceAccessType::EXPLICIT_ACCESS),
+        base::Bind(&GetUpdatedWebHistoryService,
+                   base::Unretained(profile)),
+        ProfileSyncServiceFactory::GetForProfile(profile));
+  }
 
   if (pref_name == browsing_data::prefs::kDeleteCache)
     return base::MakeUnique<CacheCounter>(profile);
 
-  if (pref_name == browsing_data::prefs::kDeletePasswords)
-    return base::MakeUnique<PasswordsCounter>(profile);
+  if (pref_name == browsing_data::prefs::kDeletePasswords) {
+    return base::MakeUnique<browsing_data::PasswordsCounter>(
+        PasswordStoreFactory::GetForProfile(
+            profile, ServiceAccessType::EXPLICIT_ACCESS));
+  }
 
-  if (pref_name == browsing_data::prefs::kDeleteFormData)
-    return base::MakeUnique<AutofillCounter>(profile);
+  if (pref_name == browsing_data::prefs::kDeleteFormData) {
+    return base::MakeUnique<browsing_data::AutofillCounter>(
+        WebDataServiceFactory::GetAutofillWebDataForProfile(
+            profile, ServiceAccessType::EXPLICIT_ACCESS));
+  }
 
   if (pref_name == browsing_data::prefs::kDeleteDownloadHistory)
     return base::MakeUnique<DownloadsCounter>(profile);
diff --git a/chrome/browser/browsing_data/browsing_data_counter_utils.cc b/chrome/browser/browsing_data/browsing_data_counter_utils.cc
index 18543855..8d6f22fd 100644
--- a/chrome/browser/browsing_data/browsing_data_counter_utils.cc
+++ b/chrome/browser/browsing_data/browsing_data_counter_utils.cc
@@ -6,15 +6,15 @@
 
 #include "base/command_line.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/browsing_data/autofill_counter.h"
 #include "chrome/browser/browsing_data/cache_counter.h"
-#include "chrome/browser/browsing_data/history_counter.h"
 #include "chrome/browser/browsing_data/media_licenses_counter.h"
-#include "chrome/browser/browsing_data/passwords_counter.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/browsing_data/core/counters/autofill_counter.h"
+#include "components/browsing_data/core/counters/history_counter.h"
+#include "components/browsing_data/core/counters/passwords_counter.h"
 #include "components/browsing_data/core/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -103,8 +103,9 @@
 
   } else if (pref_name == browsing_data::prefs::kDeleteBrowsingHistory) {
     // History counter.
-    const HistoryCounter::HistoryResult* history_result =
-        static_cast<const HistoryCounter::HistoryResult*>(result);
+    const browsing_data::HistoryCounter::HistoryResult* history_result =
+        static_cast<const browsing_data::HistoryCounter::HistoryResult*>(
+            result);
     browsing_data::BrowsingDataCounter::ResultInt local_item_count =
         history_result->Value();
     bool has_synced_visits = history_result->has_synced_visits();
@@ -117,12 +118,15 @@
 
   } else if (pref_name == browsing_data::prefs::kDeleteFormData) {
     // Autofill counter.
-    const AutofillCounter::AutofillResult* autofill_result =
-        static_cast<const AutofillCounter::AutofillResult*>(result);
-    AutofillCounter::ResultInt num_suggestions = autofill_result->Value();
-    AutofillCounter::ResultInt num_credit_cards =
+    const browsing_data::AutofillCounter::AutofillResult* autofill_result =
+        static_cast<const browsing_data::AutofillCounter::AutofillResult*>(
+            result);
+    browsing_data::AutofillCounter::ResultInt num_suggestions =
+        autofill_result->Value();
+    browsing_data::AutofillCounter::ResultInt num_credit_cards =
         autofill_result->num_credit_cards();
-    AutofillCounter::ResultInt num_addresses = autofill_result->num_addresses();
+    browsing_data::AutofillCounter::ResultInt num_addresses =
+        autofill_result->num_addresses();
 
     std::vector<base::string16> displayed_strings;
 
diff --git a/chrome/browser/browsing_data/browsing_data_counter_utils_unittest.cc b/chrome/browser/browsing_data/browsing_data_counter_utils_unittest.cc
index e25ed93..c008128 100644
--- a/chrome/browser/browsing_data/browsing_data_counter_utils_unittest.cc
+++ b/chrome/browser/browsing_data/browsing_data_counter_utils_unittest.cc
@@ -10,9 +10,10 @@
 #include "base/message_loop/message_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/browsing_data/autofill_counter.h"
+#include "chrome/browser/web_data_service_factory.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/browsing_data/core/counters/autofill_counter.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if defined(ENABLE_EXTENSIONS)
@@ -34,7 +35,10 @@
 
 // Tests the complex output of the Autofill counter.
 TEST_F(BrowsingDataCounterUtilsTest, AutofillCounterResult) {
-  AutofillCounter counter(GetProfile());
+  browsing_data::AutofillCounter counter(
+      WebDataServiceFactory::GetAutofillWebDataForProfile(
+          GetProfile(), ServiceAccessType::EXPLICIT_ACCESS)
+          .get());
 
   // This test assumes that the strings are served exactly as defined,
   // i.e. that the locale is set to the default "en".
@@ -61,10 +65,8 @@
   };
 
   for (const TestCase& test_case : kTestCases) {
-    AutofillCounter::AutofillResult result(
-        &counter,
-        test_case.num_suggestions,
-        test_case.num_credit_cards,
+    browsing_data::AutofillCounter::AutofillResult result(
+        &counter, test_case.num_suggestions, test_case.num_credit_cards,
         test_case.num_addresses);
 
     SCOPED_TRACE(base::StringPrintf(
diff --git a/chrome/browser/browsing_data/cache_counter.cc b/chrome/browser/browsing_data/cache_counter.cc
index 1a9022d..5a5890c 100644
--- a/chrome/browser/browsing_data/cache_counter.cc
+++ b/chrome/browser/browsing_data/cache_counter.cc
@@ -9,14 +9,17 @@
 #include "net/base/net_errors.h"
 
 CacheCounter::CacheCounter(Profile* profile)
-    : BrowsingDataCounter(browsing_data::prefs::kDeleteCache),
-      profile_(profile),
+    : profile_(profile),
       pending_(false),
       weak_ptr_factory_(this) {}
 
 CacheCounter::~CacheCounter() {
 }
 
+const char* CacheCounter::GetPrefName() const {
+  return browsing_data::prefs::kDeleteCache;
+}
+
 void CacheCounter::Count() {
   // TODO(msramek): StoragePartitionHttpCacheDataRemover currently does not
   // implement counting for subsets of cache, only for the entire cache. Thus,
diff --git a/chrome/browser/browsing_data/cache_counter.h b/chrome/browser/browsing_data/cache_counter.h
index 45c67ae..b8963ef 100644
--- a/chrome/browser/browsing_data/cache_counter.h
+++ b/chrome/browser/browsing_data/cache_counter.h
@@ -21,6 +21,8 @@
   // Used only for testing.
   bool Pending();
 
+  const char* GetPrefName() const override;
+
  private:
   Profile* profile_;
   bool pending_;
diff --git a/chrome/browser/browsing_data/downloads_counter.cc b/chrome/browser/browsing_data/downloads_counter.cc
index 320a670..e5591a1 100644
--- a/chrome/browser/browsing_data/downloads_counter.cc
+++ b/chrome/browser/browsing_data/downloads_counter.cc
@@ -10,12 +10,15 @@
 #include "content/public/browser/download_manager.h"
 
 DownloadsCounter::DownloadsCounter(Profile* profile)
-    : BrowsingDataCounter(browsing_data::prefs::kDeleteDownloadHistory),
-      profile_(profile) {}
+    : profile_(profile) {}
 
 DownloadsCounter::~DownloadsCounter() {
 }
 
+const char* DownloadsCounter::GetPrefName() const {
+  return browsing_data::prefs::kDeleteDownloadHistory;
+}
+
 void DownloadsCounter::Count() {
   content::DownloadManager* download_manager =
       content::BrowserContext::GetDownloadManager(profile_);
diff --git a/chrome/browser/browsing_data/downloads_counter.h b/chrome/browser/browsing_data/downloads_counter.h
index 29217f16..f3bd762 100644
--- a/chrome/browser/browsing_data/downloads_counter.h
+++ b/chrome/browser/browsing_data/downloads_counter.h
@@ -16,12 +16,12 @@
   explicit DownloadsCounter(Profile* profile);
   ~DownloadsCounter() override;
 
+  const char* GetPrefName() const override;
+
  private:
   // BrowsingDataRemover implementation.
   void Count() override;
 
-  const std::string pref_name_;
-
   Profile* profile_;
 
   DISALLOW_COPY_AND_ASSIGN(DownloadsCounter);
diff --git a/chrome/browser/browsing_data/history_counter_browsertest.cc b/chrome/browser/browsing_data/history_counter_browsertest.cc
index 1366efa..93f1d7d1 100644
--- a/chrome/browser/browsing_data/history_counter_browsertest.cc
+++ b/chrome/browser/browsing_data/history_counter_browsertest.cc
@@ -2,13 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/browsing_data/history_counter.h"
+#include "components/browsing_data/core/counters/history_counter.h"
 
+#include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/run_loop.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history/web_history_service_factory.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/ui/browser.h"
 #include "components/browser_sync/browser/profile_sync_service.h"
@@ -38,14 +41,19 @@
 
   void SetUpOnMainThread() override {
     time_ = base::Time::Now();
-    service_ = HistoryServiceFactory::GetForProfileWithoutCreating(
+    history_service_ = HistoryServiceFactory::GetForProfileWithoutCreating(
         browser()->profile());
+    fake_web_history_service_.reset(new history::FakeWebHistoryService(
+        ProfileOAuth2TokenServiceFactory::GetForProfile(browser()->profile()),
+        SigninManagerFactory::GetForProfile(browser()->profile()),
+        browser()->profile()->GetRequestContext()));
+
     SetHistoryDeletionPref(true);
     SetDeletionPeriodPref(browsing_data::EVERYTHING);
   }
 
   void AddVisit(const std::string url) {
-    service_->AddPage(GURL(url), time_, history::SOURCE_BROWSED);
+    history_service_->AddPage(GURL(url), time_, history::SOURCE_BROWSED);
   }
 
   const base::Time& GetCurrentTime() {
@@ -86,8 +94,9 @@
     finished_ = result->Finished();
 
     if (finished_) {
-      HistoryCounter::HistoryResult* history_result =
-          static_cast<HistoryCounter::HistoryResult*>(result.get());
+      browsing_data::HistoryCounter::HistoryResult* history_result =
+          static_cast<browsing_data::HistoryCounter::HistoryResult*>(
+              result.get());
 
       local_result_ = history_result->Value();
       has_synced_visits_ = history_result->has_synced_visits();
@@ -111,9 +120,27 @@
     CountingFinishedSinceLastAsked();
   }
 
+  history::WebHistoryService* GetFakeWebHistoryService(Profile* profile,
+                                                       bool check_sync_status) {
+    // |check_sync_status| is true when the factory should check if
+    // history sync is enabled.
+    if (!check_sync_status ||
+        WebHistoryServiceFactory::GetForProfile(profile)) {
+      return fake_web_history_service_.get();
+    }
+    return nullptr;
+  }
+
+  history::WebHistoryService* GetRealWebHistoryService(Profile* profile) {
+    return WebHistoryServiceFactory::GetForProfile(profile);
+  }
+
+  history::HistoryService* GetHistoryService() { return history_service_; }
+
  private:
   std::unique_ptr<base::RunLoop> run_loop_;
-  history::HistoryService* service_;
+  history::HistoryService* history_service_;
+  std::unique_ptr<history::FakeWebHistoryService> fake_web_history_service_;
   base::Time time_;
 
   bool finished_;
@@ -147,7 +174,13 @@
 
   Profile* profile = browser()->profile();
 
-  HistoryCounter counter(profile);
+  browsing_data::HistoryCounter counter(
+      GetHistoryService(),
+      base::Bind(&HistoryCounterTest::GetRealWebHistoryService,
+                 base::Unretained(this),
+                 base::Unretained(profile)),
+      ProfileSyncServiceFactory::GetForProfile(profile));
+
   counter.Init(profile->GetPrefs(), base::Bind(&HistoryCounterTest::Callback,
                                                base::Unretained(this)));
   counter.Restart();
@@ -165,7 +198,13 @@
 
   Profile* profile = browser()->profile();
 
-  HistoryCounter counter(profile);
+  browsing_data::HistoryCounter counter(
+      GetHistoryService(),
+      base::Bind(&HistoryCounterTest::GetRealWebHistoryService,
+                 base::Unretained(this),
+                 base::Unretained(profile)),
+      ProfileSyncServiceFactory::GetForProfile(profile));
+
   counter.Init(profile->GetPrefs(), base::Bind(&HistoryCounterTest::Callback,
                                                base::Unretained(this)));
   SetHistoryDeletionPref(true);
@@ -182,7 +221,13 @@
 
   Profile* profile = browser()->profile();
 
-  HistoryCounter counter(profile);
+  browsing_data::HistoryCounter counter(
+      GetHistoryService(),
+      base::Bind(&HistoryCounterTest::GetRealWebHistoryService,
+                 base::Unretained(this),
+                 base::Unretained(profile)),
+      ProfileSyncServiceFactory::GetForProfile(profile));
+
   counter.Init(profile->GetPrefs(), base::Bind(&HistoryCounterTest::Callback,
                                                base::Unretained(this)));
   counter.Restart();
@@ -216,7 +261,13 @@
 
   Profile* profile = browser()->profile();
 
-  HistoryCounter counter(profile);
+  browsing_data::HistoryCounter counter(
+      GetHistoryService(),
+      base::Bind(&HistoryCounterTest::GetRealWebHistoryService,
+                 base::Unretained(this),
+                 base::Unretained(profile)),
+      ProfileSyncServiceFactory::GetForProfile(profile));
+
   counter.Init(profile->GetPrefs(), base::Bind(&HistoryCounterTest::Callback,
                                                base::Unretained(this)));
 
@@ -247,17 +298,21 @@
   // for testing.
   Profile* profile = browser()->profile();
 
-  std::unique_ptr<history::FakeWebHistoryService> service(
-      new history::FakeWebHistoryService(
-          ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
-          SigninManagerFactory::GetForProfile(profile),
-          profile->GetRequestContext()));
+  browsing_data::HistoryCounter counter(
+      GetHistoryService(),
+      base::Bind(&HistoryCounterTest::GetFakeWebHistoryService,
+                 base::Unretained(this),
+                 base::Unretained(profile),
+                 false),
+      ProfileSyncServiceFactory::GetForProfile(profile));
 
-  HistoryCounter counter(profile);
-  counter.SetWebHistoryServiceForTesting(service.get());
   counter.Init(profile->GetPrefs(), base::Bind(&HistoryCounterTest::Callback,
                                                base::Unretained(this)));
 
+  history::FakeWebHistoryService* service =
+    static_cast<history::FakeWebHistoryService*>(GetFakeWebHistoryService(
+        profile, false));
+
   // No entries locally and no entries in Sync.
   service->SetupFakeResponse(true /* success */, net::HTTP_OK);
   counter.Restart();
@@ -289,7 +344,7 @@
   // To err on the safe side, if the server request fails, we assume that there
   // might be some items on the server.
   service->SetupFakeResponse(true /* success */,
-                             net::HTTP_INTERNAL_SERVER_ERROR);
+                                              net::HTTP_INTERNAL_SERVER_ERROR);
   counter.Restart();
   WaitForCounting();
   EXPECT_EQ(0u, GetLocalResult());
@@ -297,7 +352,7 @@
 
   // Same when the entire query fails.
   service->SetupFakeResponse(false /* success */,
-                             net::HTTP_INTERNAL_SERVER_ERROR);
+                                              net::HTTP_INTERNAL_SERVER_ERROR);
   counter.Restart();
   WaitForCounting();
   EXPECT_EQ(0u, GetLocalResult());
@@ -332,13 +387,15 @@
   Profile* profile = GetProfile(kFirstProfileIndex);
 
   // Set up the fake web history service and the counter.
-  std::unique_ptr<history::FakeWebHistoryService> web_history_service(
-      new history::FakeWebHistoryService(
-          ProfileOAuth2TokenServiceFactory::GetForProfile(browser()->profile()),
-          SigninManagerFactory::GetForProfile(browser()->profile()),
-          browser()->profile()->GetRequestContext()));
-  HistoryCounter counter(profile);
-  counter.SetWebHistoryServiceForTesting(web_history_service.get());
+
+  browsing_data::HistoryCounter counter(
+      GetHistoryService(),
+      base::Bind(&HistoryCounterTest::GetFakeWebHistoryService,
+                 base::Unretained(this),
+                 base::Unretained(profile),
+                 true),
+      sync_service);
+
   counter.Init(profile->GetPrefs(), base::Bind(&HistoryCounterTest::Callback,
                                                base::Unretained(this)));
 
diff --git a/chrome/browser/browsing_data/hosted_apps_counter.cc b/chrome/browser/browsing_data/hosted_apps_counter.cc
index 2a05fee6..54f727de 100644
--- a/chrome/browser/browsing_data/hosted_apps_counter.cc
+++ b/chrome/browser/browsing_data/hosted_apps_counter.cc
@@ -14,11 +14,14 @@
 #include "extensions/common/extension.h"
 
 HostedAppsCounter::HostedAppsCounter(Profile* profile)
-    : BrowsingDataCounter(browsing_data::prefs::kDeleteHostedAppsData),
-      profile_(profile) {}
+    : profile_(profile) {}
 
 HostedAppsCounter::~HostedAppsCounter() {}
 
+const char* HostedAppsCounter::GetPrefName() const {
+  return browsing_data::prefs::kDeleteHostedAppsData;
+}
+
 void HostedAppsCounter::Count() {
   int count = 0;
   std::vector<std::string> names;
diff --git a/chrome/browser/browsing_data/hosted_apps_counter.h b/chrome/browser/browsing_data/hosted_apps_counter.h
index 2ca7c32..79d7f1e 100644
--- a/chrome/browser/browsing_data/hosted_apps_counter.h
+++ b/chrome/browser/browsing_data/hosted_apps_counter.h
@@ -34,6 +34,8 @@
   explicit HostedAppsCounter(Profile* profile);
   ~HostedAppsCounter() override;
 
+  const char* GetPrefName() const override;
+
  private:
   // BrowsingDataCounter:
   void Count() override;
diff --git a/chrome/browser/browsing_data/media_licenses_counter.cc b/chrome/browser/browsing_data/media_licenses_counter.cc
index 89236b8..a7802bdc 100644
--- a/chrome/browser/browsing_data/media_licenses_counter.cc
+++ b/chrome/browser/browsing_data/media_licenses_counter.cc
@@ -52,12 +52,15 @@
 }
 
 MediaLicensesCounter::MediaLicensesCounter(Profile* profile)
-    : BrowsingDataCounter(browsing_data::prefs::kDeleteMediaLicenses),
-      profile_(profile),
+    : profile_(profile),
       weak_ptr_factory_(this) {}
 
 MediaLicensesCounter::~MediaLicensesCounter() {}
 
+const char* MediaLicensesCounter::GetPrefName() const {
+  return browsing_data::prefs::kDeleteMediaLicenses;
+}
+
 void MediaLicensesCounter::Count() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   scoped_refptr<storage::FileSystemContext> filesystem_context =
diff --git a/chrome/browser/browsing_data/media_licenses_counter.h b/chrome/browser/browsing_data/media_licenses_counter.h
index 419f3dc..8465622 100644
--- a/chrome/browser/browsing_data/media_licenses_counter.h
+++ b/chrome/browser/browsing_data/media_licenses_counter.h
@@ -38,6 +38,8 @@
   explicit MediaLicensesCounter(Profile* profile);
   ~MediaLicensesCounter() override;
 
+  const char* GetPrefName() const override;
+
  private:
   Profile* profile_;
 
diff --git a/chrome/browser/browsing_data/passwords_counter_browsertest.cc b/chrome/browser/browsing_data/passwords_counter_browsertest.cc
index a5be7ff..acb82190 100644
--- a/chrome/browser/browsing_data/passwords_counter_browsertest.cc
+++ b/chrome/browser/browsing_data/passwords_counter_browsertest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/browsing_data/passwords_counter.h"
+#include "components/browsing_data/core/counters/passwords_counter.h"
 
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
@@ -136,7 +136,8 @@
   AddLogin("https://www.chrome.com", "user2", false);
 
   Profile* profile = browser()->profile();
-  PasswordsCounter counter(profile);
+  browsing_data::PasswordsCounter counter(PasswordStoreFactory::GetForProfile(
+      profile, ServiceAccessType::EXPLICIT_ACCESS));
   counter.Init(profile->GetPrefs(), base::Bind(&PasswordsCounterTest::Callback,
                                                base::Unretained(this)));
   counter.Restart();
@@ -152,7 +153,9 @@
   AddLogin("https://www.chrome.com", "user3", true);
 
   Profile* profile = browser()->profile();
-  PasswordsCounter counter(profile);
+  browsing_data::PasswordsCounter counter(PasswordStoreFactory::GetForProfile(
+      profile, ServiceAccessType::EXPLICIT_ACCESS));
+
   counter.Init(profile->GetPrefs(), base::Bind(&PasswordsCounterTest::Callback,
                                                base::Unretained(this)));
   counter.Restart();
@@ -169,7 +172,8 @@
   AddLogin("https://www.chrome.com", "user", false);
 
   Profile* profile = browser()->profile();
-  PasswordsCounter counter(profile);
+  browsing_data::PasswordsCounter counter(PasswordStoreFactory::GetForProfile(
+      profile, ServiceAccessType::EXPLICIT_ACCESS));
   counter.Init(profile->GetPrefs(), base::Bind(&PasswordsCounterTest::Callback,
                                                base::Unretained(this)));
   SetPasswordsDeletionPref(true);
@@ -185,7 +189,8 @@
   AddLogin("https://www.google.com", "user", false);
 
   Profile* profile = browser()->profile();
-  PasswordsCounter counter(profile);
+  browsing_data::PasswordsCounter counter(PasswordStoreFactory::GetForProfile(
+      profile, ServiceAccessType::EXPLICIT_ACCESS));
   counter.Init(profile->GetPrefs(), base::Bind(&PasswordsCounterTest::Callback,
                                                base::Unretained(this)));
   counter.Restart();
@@ -199,7 +204,8 @@
   AddLogin("https://www.google.com", "user", false);
 
   Profile* profile = browser()->profile();
-  PasswordsCounter counter(profile);
+  browsing_data::PasswordsCounter counter(PasswordStoreFactory::GetForProfile(
+      profile, ServiceAccessType::EXPLICIT_ACCESS));
   counter.Init(profile->GetPrefs(), base::Bind(&PasswordsCounterTest::Callback,
                                                base::Unretained(this)));
   counter.Restart();
@@ -228,7 +234,8 @@
   AddLogin("https://www.chrome.com", "user", false);
 
   Profile* profile = browser()->profile();
-  PasswordsCounter counter(profile);
+  browsing_data::PasswordsCounter counter(PasswordStoreFactory::GetForProfile(
+      profile, ServiceAccessType::EXPLICIT_ACCESS));
   counter.Init(profile->GetPrefs(), base::Bind(&PasswordsCounterTest::Callback,
                                                base::Unretained(this)));
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index d871442..1ef0931 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -131,6 +131,7 @@
 #include "components/rappor/rappor_utils.h"
 #include "components/security_interstitials/core/ssl_error_ui.h"
 #include "components/signin/core/common/profile_management_switches.h"
+#include "components/spellcheck/common/spellcheck_switches.h"
 #include "components/startup_metric_utils/browser/startup_metric_host_impl.h"
 #include "components/subresource_filter/content/browser/subresource_filter_navigation_throttle.h"
 #include "components/translate/core/common/translate_switches.h"
@@ -1627,7 +1628,7 @@
       switches::kDisableJavaScriptHarmonyShipping,
       switches::kDisableNewBookmarkApps,
 #if defined(ENABLE_SPELLCHECK) && defined(OS_ANDROID)
-      switches::kEnableAndroidSpellChecker,
+      spellcheck::switches::kEnableAndroidSpellChecker,
 #endif
       switches::kEnableBenchmarking,
       switches::kEnableNaCl,
diff --git a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
index 5df6a5a..a26e6a0 100644
--- a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
@@ -228,6 +228,39 @@
   EXPECT_EQ("button", speech_monitor_.GetNextUtterance());
 }
 
+IN_PROC_BROWSER_TEST_F(LoggedInSpokenFeedbackTest, NavigateNotificationCenter) {
+  EnableChromeVox();
+
+  EXPECT_TRUE(PerformAcceleratorAction(ash::SHOW_MESSAGE_CENTER_BUBBLE));
+
+  // Wait for it to say "Notification Center, window".
+  while ("Notification Center, window" != speech_monitor_.GetNextUtterance()) {
+  }
+
+  // Tab until we get to the Do Not Disturb button.
+  SendKeyPress(ui::VKEY_TAB);
+  do {
+    std::string ut = speech_monitor_.GetNextUtterance();
+
+    if (ut == "Do not disturb")
+      break;
+    else if (ut == "Button")
+      SendKeyPress(ui::VKEY_TAB);
+  } while (true);
+  EXPECT_EQ("Button", speech_monitor_.GetNextUtterance());
+  EXPECT_EQ("Not pressed", speech_monitor_.GetNextUtterance());
+
+  SendKeyPress(ui::VKEY_SPACE);
+  EXPECT_EQ("Do not disturb", speech_monitor_.GetNextUtterance());
+  EXPECT_EQ("Button", speech_monitor_.GetNextUtterance());
+  EXPECT_EQ("Pressed", speech_monitor_.GetNextUtterance());
+
+  SendKeyPress(ui::VKEY_SPACE);
+  EXPECT_EQ("Do not disturb", speech_monitor_.GetNextUtterance());
+  EXPECT_EQ("Button", speech_monitor_.GetNextUtterance());
+  EXPECT_EQ("Not pressed", speech_monitor_.GetNextUtterance());
+}
+
 //
 // Spoken feedback tests in both a logged in browser window and guest mode.
 //
diff --git a/chrome/browser/chromeos/arc/arc_auth_service.cc b/chrome/browser/chromeos/arc/arc_auth_service.cc
index c20b7401..cfa5038 100644
--- a/chrome/browser/chromeos/arc/arc_auth_service.cc
+++ b/chrome/browser/chromeos/arc/arc_auth_service.cc
@@ -6,8 +6,8 @@
 
 #include <utility>
 
-#include "ash/shelf/shelf_delegate.h"
-#include "ash/shell.h"
+#include "ash/common/shelf/shelf_delegate.h"
+#include "ash/common/wm_shell.h"
 #include "base/auto_reset.h"
 #include "base/bind.h"
 #include "base/command_line.h"
@@ -82,8 +82,10 @@
 ash::ShelfDelegate* GetShelfDelegate() {
   if (g_shelf_delegate_for_testing)
     return g_shelf_delegate_for_testing;
-  if (ash::Shell::HasInstance())
-    return ash::Shell::GetInstance()->GetShelfDelegate();
+  if (ash::WmShell::HasInstance()) {
+    DCHECK(ash::WmShell::Get()->shelf_delegate());
+    return ash::WmShell::Get()->shelf_delegate();
+  }
   return nullptr;
 }
 
diff --git a/chrome/browser/chromeos/arc/arc_process.cc b/chrome/browser/chromeos/arc/arc_process.cc
index 53f127d..02aaa02 100644
--- a/chrome/browser/chromeos/arc/arc_process.cc
+++ b/chrome/browser/chromeos/arc/arc_process.cc
@@ -4,19 +4,33 @@
 
 #include "chrome/browser/chromeos/arc/arc_process.h"
 
+#include <utility>
+
 namespace arc {
 
 ArcProcess::ArcProcess(base::ProcessId nspid,
                        base::ProcessId pid,
                        const std::string& process_name,
-                       mojom::ProcessState process_state)
+                       mojom::ProcessState process_state,
+                       bool is_focused,
+                       int64_t last_activity_time)
     : nspid_(nspid),
       pid_(pid),
       process_name_(process_name),
-      process_state_(process_state) {}
+      process_state_(process_state),
+      is_focused_(is_focused),
+      last_activity_time_(last_activity_time) {}
 
 ArcProcess::~ArcProcess() = default;
 
+// Sort by (process_state, last_activity_time) pair.
+// Smaller process_state value means higher priority as defined in Android.
+// Larger last_activity_time means more recently used.
+bool ArcProcess::operator<(const ArcProcess& rhs) const {
+  return std::make_pair(process_state(), -last_activity_time()) <
+         std::make_pair(rhs.process_state(), -rhs.last_activity_time());
+}
+
 ArcProcess::ArcProcess(ArcProcess&& other) = default;
 ArcProcess& ArcProcess::operator=(ArcProcess&& other) = default;
 
diff --git a/chrome/browser/chromeos/arc/arc_process.h b/chrome/browser/chromeos/arc/arc_process.h
index 01562a2..81a6e89b 100644
--- a/chrome/browser/chromeos/arc/arc_process.h
+++ b/chrome/browser/chromeos/arc/arc_process.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_CHROMEOS_ARC_ARC_PROCESS_H_
 #define CHROME_BROWSER_CHROMEOS_ARC_ARC_PROCESS_H_
 
+#include <stdint.h>
 #include <string>
 #include <vector>
 
@@ -18,16 +19,23 @@
   ArcProcess(base::ProcessId nspid,
              base::ProcessId pid,
              const std::string& process_name,
-             mojom::ProcessState process_state);
+             mojom::ProcessState process_state,
+             bool is_focused,
+             int64_t last_activity_time);
   ~ArcProcess();
 
   ArcProcess(ArcProcess&& other);
   ArcProcess& operator=(ArcProcess&& other);
 
+  // Sort by descending importance.
+  bool operator<(const ArcProcess& rhs) const;
+
   base::ProcessId nspid() const { return nspid_; }
   base::ProcessId pid() const { return pid_; }
   const std::string& process_name() const { return process_name_; }
   mojom::ProcessState process_state() const { return process_state_; }
+  bool is_focused() const { return is_focused_; }
+  int64_t last_activity_time() const { return last_activity_time_; }
   std::vector<std::string>& packages() { return packages_; }
   const std::vector<std::string>& packages() const { return packages_; }
 
@@ -36,6 +44,12 @@
   base::ProcessId pid_;
   std::string process_name_;
   mojom::ProcessState process_state_;
+  // If the Android app is the focused window.
+  bool is_focused_;
+  // A monotonic timer recording the last time this process was active.
+  // Milliseconds since Android boot. This info is passed from Android
+  // ActivityManagerService via IPC.
+  int64_t last_activity_time_;
   std::vector<std::string> packages_;
 
   DISALLOW_COPY_AND_ASSIGN(ArcProcess);
diff --git a/chrome/browser/chromeos/arc/arc_process_service.cc b/chrome/browser/chromeos/arc/arc_process_service.cc
index b84ddaf..aa8ec507 100644
--- a/chrome/browser/chromeos/arc/arc_process_service.cc
+++ b/chrome/browser/chromeos/arc/arc_process_service.cc
@@ -172,7 +172,8 @@
     // In case the process already dies so couldn't find corresponding pid.
     if (it != nspid_to_pid_.end() && it->second != kNullProcessId) {
       ArcProcess arc_process(entry->pid, it->second, entry->process_name,
-                             entry->process_state);
+                             entry->process_state, entry->is_focused,
+                             entry->last_activity_time);
       // |entry->packages| is provided only when process.mojom's verion is >=4.
       if (entry->packages) {
         for (const auto& package : entry->packages) {
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_browsertest.cc b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_browsertest.cc
index bd3d1d2..4ff24e6 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_browsertest.cc
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_browsertest.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 
+#include "ash/common/wm_shell.h"
 #include "ash/desktop_background/desktop_background_controller.h"
 #include "ash/desktop_background/desktop_background_controller_observer.h"
 #include "ash/desktop_background/desktop_background_controller_test_api.h"
@@ -118,6 +119,9 @@
   void LogIn(const AccountId& account_id, const std::string& user_id_hash) {
     user_manager::UserManager::Get()->UserLoggedIn(account_id, user_id_hash,
                                                    false);
+    // Adding a secondary display creates a shelf on that display, which
+    // assumes a shelf on the primary display if the user was logged in.
+    ash::Shell::GetInstance()->CreateShelf();
     WaitAsyncWallpaperLoadStarted();
   }
 
@@ -130,6 +134,11 @@
         user_manager::UserManager::Get()->FindUserAndModify(account_id);
     user_manager::UserManager::Get()->ChangeUserChildStatus(
         user, true /* is_child */);
+    // TODO(jamescook): For some reason creating the shelf here (which is what
+    // would happen in normal login) causes the child wallpaper tests to fail
+    // with the wallpaper having alpha. This looks like the wallpaper is mid-
+    // animation, but happens even if animations are disabled. Something is
+    // wrong with how these tests simulate login.
   }
 
   int LoadedWallpapers() {
diff --git a/chrome/browser/devtools/device/android_device_manager.cc b/chrome/browser/devtools/device/android_device_manager.cc
index 8a1130d..eb12488 100644
--- a/chrome/browser/devtools/device/android_device_manager.cc
+++ b/chrome/browser/devtools/device/android_device_manager.cc
@@ -489,6 +489,7 @@
 void AndroidDeviceManager::HandlerThread::StopThread(
     base::Thread* thread) {
   thread->Stop();
+  delete thread;
 }
 
 AndroidDeviceManager::HandlerThread::~HandlerThread() {
diff --git a/chrome/browser/extensions/bookmark_app_helper.cc b/chrome/browser/extensions/bookmark_app_helper.cc
index 2e70bda..c70be49 100644
--- a/chrome/browser/extensions/bookmark_app_helper.cc
+++ b/chrome/browser/extensions/bookmark_app_helper.cc
@@ -72,8 +72,8 @@
 #endif  // defined(OS_WIN)
 
 #if defined(USE_ASH)
-#include "ash/shelf/shelf_delegate.h"
-#include "ash/shell.h"
+#include "ash/common/shelf/shelf_delegate.h"
+#include "ash/common/wm_shell.h"
 #endif
 
 namespace {
@@ -718,7 +718,9 @@
   web_app::CreateShortcuts(web_app::SHORTCUT_CREATION_BY_USER,
                            creation_locations, current_profile, extension);
 #else
-  ash::Shell::GetInstance()->GetShelfDelegate()->PinAppWithID(extension->id());
+  ash::ShelfDelegate* shelf_delegate = ash::WmShell::Get()->shelf_delegate();
+  DCHECK(shelf_delegate);
+  shelf_delegate->PinAppWithID(extension->id());
 #endif  // !defined(USE_ASH)
 #endif  // !defined(OS_MACOSX)
 
diff --git a/chrome/browser/memory/tab_manager.cc b/chrome/browser/memory/tab_manager.cc
index a90170d..d8ae87d8 100644
--- a/chrome/browser/memory/tab_manager.cc
+++ b/chrome/browser/memory/tab_manager.cc
@@ -727,7 +727,8 @@
 }
 
 // static
-bool TabManager::CompareTabStats(TabStats first, TabStats second) {
+bool TabManager::CompareTabStats(const TabStats& first,
+                                 const TabStats& second) {
   // Being currently selected is most important to protect.
   if (first.is_selected != second.is_selected)
     return first.is_selected;
diff --git a/chrome/browser/memory/tab_manager.h b/chrome/browser/memory/tab_manager.h
index 53793ad..0f750d35 100644
--- a/chrome/browser/memory/tab_manager.h
+++ b/chrome/browser/memory/tab_manager.h
@@ -124,7 +124,6 @@
   // thread.
   TabStatsList GetUnsortedTabStats();
 
-  // Add/remove observers.
   void AddObserver(TabManagerObserver* observer);
   void RemoveObserver(TabManagerObserver* observer);
 
@@ -138,6 +137,10 @@
   // Sets/clears the auto-discardable state of the tab.
   void SetTabAutoDiscardableState(content::WebContents* contents, bool state);
 
+  // Returns true if |first| is considered less desirable to be killed than
+  // |second|.
+  static bool CompareTabStats(const TabStats& first, const TabStats& second);
+
  private:
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, AutoDiscardable);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, CanOnlyDiscardOnce);
@@ -260,10 +263,6 @@
   // creating one if needed.
   WebContentsData* GetWebContentsData(content::WebContents* contents) const;
 
-  // Returns true if |first| is considered less desirable to be killed than
-  // |second|.
-  static bool CompareTabStats(TabStats first, TabStats second);
-
   // Returns either the system's clock or the test clock. See |test_tick_clock_|
   // for more details.
   base::TimeTicks NowTicks() const;
diff --git a/chrome/browser/memory/tab_manager_delegate_chromeos.cc b/chrome/browser/memory/tab_manager_delegate_chromeos.cc
index 5ffbd0b..f2c3a075 100644
--- a/chrome/browser/memory/tab_manager_delegate_chromeos.cc
+++ b/chrome/browser/memory/tab_manager_delegate_chromeos.cc
@@ -76,83 +76,85 @@
                           base::CompareCase::SENSITIVE);
 }
 
-bool IsArcWindowInForeground() {
-  auto activation_client = GetActivationClient();
-  return activation_client && IsArcWindow(activation_client->GetActiveWindow());
-}
-
-int AppStateToPriority(
-    const arc::mojom::ProcessState& process_state) {
-  // Logic copied from Android:
-  // frameworks/base/core/java/android/app/ActivityManager.java
-  // Note that ProcessState enumerates from most important (lower value) to
-  // least important (higher value), while ProcessPriority enumerates the
-  // opposite.
-  if (process_state >= arc::mojom::ProcessState::HOME) {
-    return ProcessPriority::ANDROID_BACKGROUND;
-  } else if (process_state >= arc::mojom::ProcessState::SERVICE) {
-    return ProcessPriority::ANDROID_SERVICE;
-  } else if (process_state >= arc::mojom::ProcessState::HEAVY_WEIGHT) {
-    return ProcessPriority::ANDROID_CANT_SAVE_STATE;
-  } else if (process_state >= arc::mojom::ProcessState::IMPORTANT_BACKGROUND) {
-    return ProcessPriority::ANDROID_PERCEPTIBLE;
-  } else if (process_state >= arc::mojom::ProcessState::IMPORTANT_FOREGROUND) {
-    return ProcessPriority::ANDROID_VISIBLE;
-  } else if (process_state >= arc::mojom::ProcessState::TOP_SLEEPING) {
-    return ProcessPriority::ANDROID_TOP_SLEEPING;
-  } else if (process_state >= arc::mojom::ProcessState::FOREGROUND_SERVICE) {
-    return ProcessPriority::ANDROID_FOREGROUND_SERVICE;
-  } else if (process_state >= arc::mojom::ProcessState::TOP) {
-    return IsArcWindowInForeground() ?
-        ProcessPriority::ANDROID_TOP :
-        ProcessPriority::ANDROID_TOP_INACTIVE;
-  } else if (process_state >= arc::mojom::ProcessState::PERSISTENT) {
-    return ProcessPriority::ANDROID_PERSISTENT;
-  }
-  return ProcessPriority::ANDROID_NON_EXISTS;
-}
-
-int TabStatsToPriority(const TabStats& tab) {
-  if (tab.is_selected)
-    return ProcessPriority::CHROME_SELECTED;
-
-  int priority = 0;
-
-  if (tab.is_app) {
-    priority = ProcessPriority::CHROME_APP;
-  } else if (tab.is_internal_page) {
-    priority = ProcessPriority::CHROME_INTERNAL;
-  } else {
-    priority = ProcessPriority::CHROME_NORMAL;
-  }
-  if (tab.is_pinned)
-    priority |= ProcessPriority::CHROME_PINNED;
-  if (tab.is_media)
-    priority |= ProcessPriority::CHROME_MEDIA;
-  if (tab.has_form_entry)
-    priority |= ProcessPriority::CHROME_CANT_SAVE_STATE;
-
-  return priority;
-}
-
 bool IsArcMemoryManagementEnabled() {
   return base::FeatureList::IsEnabled(features::kArcMemoryManagement);
 }
 
 }  // namespace
 
+std::ostream& operator<<(std::ostream& os, const ProcessType& type) {
+  switch (type) {
+    case ProcessType::FOCUSED_APP:
+      return os << "FOCUSED_APP/FOCUSED_TAB";
+    case ProcessType::VISIBLE_APP:
+      return os << "VISIBLE_APP";
+    case ProcessType::BACKGROUND_APP:
+      return os << "BACKGROUND_APP";
+    case ProcessType::BACKGROUND_TAB:
+      return os << "BACKGROUND_TAB";
+    case ProcessType::UNKNOWN_TYPE:
+      return os << "UNKNOWN_TYPE";
+    default:
+      return os << "NOT_IMPLEMENTED_ERROR";
+  }
+  return os;
+}
+
+// TabManagerDelegate::Candidate implementation.
 std::ostream& operator<<(
     std::ostream& out, const TabManagerDelegate::Candidate& candidate) {
-  if (candidate.is_arc_app) {
-    out << "app " << candidate.app->pid()
-        << " (" << candidate.app->process_name() << ")";
-  } else {
-    out << "tab " << candidate.tab->renderer_handle;
+  if (candidate.app()) {
+    out << "app " << candidate.app()->pid() << " ("
+        << candidate.app()->process_name() << ")"
+        << ", process_state " << candidate.app()->process_state()
+        << ", is_focused " << candidate.app()->is_focused()
+        << ", lastActivityTime " << candidate.app()->last_activity_time();
+  } else if (candidate.tab()) {
+    out << "tab " << candidate.tab()->renderer_handle;
   }
-  out << " with priority " << candidate.priority;
+  out << ", process_type " << candidate.process_type();
   return out;
 }
 
+TabManagerDelegate::Candidate& TabManagerDelegate::Candidate::operator=(
+    TabManagerDelegate::Candidate&& other) {
+  tab_ = other.tab_;
+  app_ = other.app_;
+  process_type_ = other.process_type_;
+  return *this;
+}
+
+bool TabManagerDelegate::Candidate::operator<(
+    const TabManagerDelegate::Candidate& rhs) const {
+  if (process_type() != rhs.process_type())
+    return process_type() < rhs.process_type();
+  if (app() && rhs.app())
+    return *app() < *rhs.app();
+  if (tab() && rhs.tab())
+    return TabManager::CompareTabStats(*tab(), *rhs.tab());
+  // Impossible case. If app and tab are mixed in one process type, favor
+  // apps.
+  NOTREACHED() << "Undefined comparison between apps and tabs";
+  return app();
+}
+
+ProcessType TabManagerDelegate::Candidate::GetProcessTypeInternal() const {
+  if (app()) {
+    if (app()->is_focused())
+      return ProcessType::FOCUSED_APP;
+    if (app()->process_state() == arc::mojom::ProcessState::TOP)
+      return ProcessType::VISIBLE_APP;
+    return ProcessType::BACKGROUND_APP;
+  }
+  if (tab()) {
+    if (tab()->is_selected)
+      return ProcessType::FOCUSED_TAB;
+    return ProcessType::BACKGROUND_TAB;
+  }
+  NOTREACHED() << "Unexpected process type";
+  return ProcessType::UNKNOWN_TYPE;
+}
+
 // Holds the info of a newly focused tab or app window. The focused process is
 // set to highest priority (lowest OOM score), but not immediately. To avoid
 // redundant settings the OOM score adjusting only happens after a timeout. If
@@ -405,6 +407,10 @@
   arc_process_instance_version_ = 0;
 }
 
+// TODO(cylee): Remove this function if Android process OOM score settings
+// is moved back to Android.
+// For example, negotiate non-overlapping OOM score ranges so Chrome and Android
+// can set OOM score for processes in their own world.
 void TabManagerDelegate::OnWindowActivated(
     aura::client::ActivationChangeObserver::ActivationReason reason,
     aura::Window* gained_active,
@@ -598,32 +604,37 @@
 }
 
 // Excludes persistent ARC apps, but still preserves active chrome tabs and
-// top ARC apps. The latter ones should not be killed by TabManager since
-// we still want to adjust their oom_score_adj.
+// focused ARC apps. The latter ones should not be killed by TabManager here,
+// but we want to adjust their oom_score_adj.
 // static
 std::vector<TabManagerDelegate::Candidate>
 TabManagerDelegate::GetSortedCandidates(
     const TabStatsList& tab_list,
     const std::vector<arc::ArcProcess>& arc_processes) {
+  static constexpr char kAppLauncherProcessName[] =
+      "org.chromium.arc.applauncher";
 
   std::vector<Candidate> candidates;
   candidates.reserve(tab_list.size() + arc_processes.size());
 
   for (const auto& tab : tab_list) {
-    candidates.push_back(Candidate(&tab, TabStatsToPriority(tab)));
+    candidates.emplace_back(&tab);
   }
 
   for (const auto& app : arc_processes) {
-    Candidate candidate(&app, AppStateToPriority(app.process_state()));
-    // Skip persistent android processes since we should never kill them.
-    // Also don't ajust OOM score so their score remains min oom_score_adj.
-    if (candidate.priority >= ProcessPriority::ANDROID_PERSISTENT)
+    // Skip persistent android processes since they should never be killed here.
+    // Neither do we set their OOM scores so their score remains minimum.
+    //
+    // AppLauncher is treated specially in ARC++. For example it is taken
+    // as the dummy foreground app from Android's point of view when the focused
+    // window is not an Android app. We prefer never kill it.
+    if (app.process_state() <= arc::mojom::ProcessState::PERSISTENT_UI ||
+        app.process_name() == kAppLauncherProcessName)
       continue;
-    candidates.push_back(candidate);
+    candidates.emplace_back(&app);
   }
 
   // Sort candidates according to priority.
-  // TODO(cylee): Missing LRU property. Fix it when apps has the information.
   std::sort(candidates.begin(), candidates.end());
 
   return candidates;
@@ -648,37 +659,41 @@
     const std::vector<arc::ArcProcess>& arc_processes) {
 
   VLOG(2) << "LowMemoryKilleImpl";
-  std::vector<TabManagerDelegate::Candidate> candidates =
+  const std::vector<TabManagerDelegate::Candidate> candidates =
       GetSortedCandidates(tab_list, arc_processes);
 
   int target_memory_to_free_kb = mem_stat_->TargetMemoryToFreeKB();
-  for (const auto& entry : candidates) {
+  // Kill processes until the estimated amount of freed memory is sufficient to
+  // bring the system memory back to a normal level.
+  // The list is sorted by descending importance, so we go through the list
+  // backwards.
+  for (auto it = candidates.rbegin(); it != candidates.rend(); ++it) {
     VLOG(3) << "Target memory to free: " << target_memory_to_free_kb << " KB";
     // Never kill selected tab or Android foreground app, regardless whether
     // they're in the active window. Since the user experience would be bad.
-    if ((!entry.is_arc_app &&
-         entry.priority >= ProcessPriority::CHROME_SELECTED) ||
-        (entry.is_arc_app &&
-         entry.priority >= ProcessPriority::ANDROID_TOP_INACTIVE)) {
-      VLOG(2) << "Skipped killing " << entry;
+    ProcessType process_type = it->process_type();
+    if (process_type == ProcessType::VISIBLE_APP ||
+        process_type == ProcessType::FOCUSED_APP ||
+        process_type == ProcessType::FOCUSED_TAB) {
+      VLOG(2) << "Skipped killing " << *it;
       continue;
     }
-    if (entry.is_arc_app) {
-        int estimated_memory_freed_kb =
-            mem_stat_->EstimatedMemoryFreedKB(entry.app->pid());
-        if (KillArcProcess(entry.app->nspid())) {
-          target_memory_to_free_kb -= estimated_memory_freed_kb;
-          uma_->ReportKill(estimated_memory_freed_kb);
-          VLOG(2) << "Killed " << entry;
-        }
-    } else {
-      int64_t tab_id = entry.tab->tab_contents_id;
+    if (it->app()) {
       int estimated_memory_freed_kb =
-          mem_stat_->EstimatedMemoryFreedKB(entry.tab->renderer_handle);
+          mem_stat_->EstimatedMemoryFreedKB(it->app()->pid());
+      if (KillArcProcess(it->app()->nspid())) {
+        target_memory_to_free_kb -= estimated_memory_freed_kb;
+        uma_->ReportKill(estimated_memory_freed_kb);
+        VLOG(2) << "Killed " << *it;
+      }
+    } else {
+      int64_t tab_id = it->tab()->tab_contents_id;
+      int estimated_memory_freed_kb =
+          mem_stat_->EstimatedMemoryFreedKB(it->tab()->renderer_handle);
       if (KillTab(tab_id)) {
         target_memory_to_free_kb -= estimated_memory_freed_kb;
         uma_->ReportKill(estimated_memory_freed_kb);
-        VLOG(2) << "Killed " << entry;
+        VLOG(2) << "Killed " << *it;
       }
     }
     if (target_memory_to_free_kb < 0)
@@ -690,7 +705,7 @@
     const TabStatsList& tab_list,
     const std::vector<arc::ArcProcess>& arc_processes) {
   // Least important first.
-  auto candidates = GetSortedCandidates(tab_list, arc_processes);
+  const auto candidates = GetSortedCandidates(tab_list, arc_processes);
 
   // Now we assign priorities based on the sorted list. We're assigning
   // priorities in the range of kLowestRendererOomScore to
@@ -710,10 +725,9 @@
   // Find some pivot point. For now processes with priority >= CHROME_INTERNAL
   // are prone to be affected by LRU change. Taking them as "high priority"
   // processes.
-  auto lower_priority_part = candidates.rend();
-  // Iterate in reverse order since the list is sorted by least importance.
-  for (auto it = candidates.rbegin(); it != candidates.rend(); ++it) {
-    if (it->priority < ProcessPriority::CHROME_INTERNAL) {
+  auto lower_priority_part = candidates.end();
+  for (auto it = candidates.begin(); it != candidates.end(); ++it) {
+    if (it->process_type() >= ProcessType::BACKGROUND_APP) {
       lower_priority_part = it;
       break;
     }
@@ -722,13 +736,12 @@
   ProcessScoreMap new_map;
 
   // Higher priority part.
-  DistributeOomScoreInRange(candidates.rbegin(), lower_priority_part,
+  DistributeOomScoreInRange(candidates.begin(), lower_priority_part,
                             chrome::kLowestRendererOomScore, range_middle,
                             &new_map);
   // Lower priority part.
-  DistributeOomScoreInRange(lower_priority_part, candidates.rend(),
-                            range_middle, chrome::kHighestRendererOomScore,
-                            &new_map);
+  DistributeOomScoreInRange(lower_priority_part, candidates.end(), range_middle,
+                            chrome::kHighestRendererOomScore, &new_map);
   base::AutoLock oom_score_autolock(oom_score_lock_);
   oom_score_map_.swap(new_map);
 }
@@ -762,8 +775,8 @@
 }
 
 void TabManagerDelegate::DistributeOomScoreInRange(
-    std::vector<TabManagerDelegate::Candidate>::reverse_iterator rbegin,
-    std::vector<TabManagerDelegate::Candidate>::reverse_iterator rend,
+    std::vector<TabManagerDelegate::Candidate>::const_iterator begin,
+    std::vector<TabManagerDelegate::Candidate>::const_iterator end,
     int range_begin,
     int range_end,
     ProcessScoreMap* new_map) {
@@ -774,27 +787,27 @@
   // Though there might be duplicate process handles, it doesn't matter to
   // overestimate the number of processes here since the we don't need to
   // use up the full range.
-  int num = (rend - rbegin);
+  int num = (end - begin);
   const float priority_increment =
       static_cast<float>(range_end - range_begin) / num;
 
   float priority = range_begin;
-  for (auto cur = rbegin; cur != rend; ++cur) {
+  for (auto cur = begin; cur != end; ++cur) {
     int score = static_cast<int>(priority + 0.5f);
-    if (cur->is_arc_app) {
+    if (cur->app()) {
       // Use pid as map keys so it's globally unique.
-      (*new_map)[cur->app->pid()] = score;
+      (*new_map)[cur->app()->pid()] = score;
       int cur_app_pid_score = 0;
       {
         base::AutoLock oom_score_autolock(oom_score_lock_);
-        cur_app_pid_score = oom_score_map_[cur->app->pid()];
+        cur_app_pid_score = oom_score_map_[cur->app()->pid()];
       }
       if (cur_app_pid_score != score) {
         VLOG(3) << "Set OOM score " << score << " for " << *cur;
-        SetOomScoreAdjForApp(cur->app->nspid(), score);
+        SetOomScoreAdjForApp(cur->app()->nspid(), score);
       }
     } else {
-      base::ProcessHandle process_handle = cur->tab->renderer_handle;
+      base::ProcessHandle process_handle = cur->tab()->renderer_handle;
       // 1. tab_list contains entries for already-discarded tabs. If the PID
       // (renderer_handle) is zero, we don't need to adjust the oom_score.
       // 2. Only add unseen process handle so if there's multiple tab maps to
diff --git a/chrome/browser/memory/tab_manager_delegate_chromeos.h b/chrome/browser/memory/tab_manager_delegate_chromeos.h
index dbd6d46..0b4fa54f 100644
--- a/chrome/browser/memory/tab_manager_delegate_chromeos.h
+++ b/chrome/browser/memory/tab_manager_delegate_chromeos.h
@@ -11,6 +11,7 @@
 
 #include "base/containers/hash_tables.h"
 #include "base/gtest_prod_util.h"
+#include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/process/process.h"
@@ -20,6 +21,7 @@
 #include "chrome/browser/memory/tab_stats.h"
 #include "chrome/browser/ui/browser_list_observer.h"
 #include "components/arc/arc_bridge_service.h"
+#include "components/arc/common/process.mojom.h"
 #include "components/arc/instance_holder.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
@@ -27,47 +29,19 @@
 
 namespace memory {
 
-// The importance of tabs/apps. The lower the value, the more likely a process
-// is to be selected on memory pressure. The values is additive, for example,
-// one tab could be of value (CHROME_NORMAL | CHROME_PINNED).
-// TODO(cylee): Refactor this CL so the prioritize logic is unified in
-// TabManager.
-enum ProcessPriority {
-  // Processes on Android side which generally don't have an app window, and
-  // possibly be auto relaunched if killed.
-  ANDROID_BACKGROUND = 1,
-  ANDROID_SERVICE = 1 << 1,
-  ANDROID_CANT_SAVE_STATE = 1 << 2,
-  ANDROID_PERCEPTIBLE = 1 << 3,
-  ANDROID_VISIBLE = 1 << 4,
-  ANDROID_TOP_SLEEPING = 1 << 5,
-  ANDROID_FOREGROUND_SERVICE = 1 << 6,
-  ANDROID_FOREGROUND = 1 << 7,
-  // A chrome window can be one of the 3 exclusive types below:
-  // internal page, normal page, or chrome app.
-  CHROME_INTERNAL = 1 << 8,
-  CHROME_NORMAL = 1 << 9,
-  CHROME_APP = 1 << 10,
-  // An android app which is on top of screen from Android's point of view,
-  // but the app window is inactive from Chrome OS's point of view.
-  // Give it a higher priority then normal chrome tab since it could not be
-  // reloaded if killed.
-  ANDROID_TOP_INACTIVE = CHROME_APP,
-  // A chrome tab could have following 3 additional attributes
-  // (not exclusive).
-  CHROME_PINNED = 1 << 11,
-  CHROME_MEDIA = 1 << 12,
-  CHROME_CANT_SAVE_STATE = 1 << 13,
-  // The highest level of priority. Either a selected tab or an Android app in
-  // an active window. In theory there should be at most one process with this
-  // priority at a time, but at the time of writing Chrome couldn't get Android
-  // window stack info yet so there may be multiple Android apps be token as on
-  // top of screen for now.
-  CHROME_SELECTED = 1 << 14,
-  ANDROID_TOP = CHROME_SELECTED,
+// Possible types of Apps/Tabs processes. From most important to least
+// important.
+enum class ProcessType {
+  // There can be only one process having process type "FOCUSED_APP"
+  // or "FOCUSED_TAB" in the system at any give time (i.e., The focused window
+  // could be either a chrome window or an Android app. But not both.
+  FOCUSED_APP = 1,
+  FOCUSED_TAB = FOCUSED_APP,
 
-  ANDROID_PERSISTENT = 1 << 30,
-  ANDROID_NON_EXISTS = 0x7FFFFFFF
+  VISIBLE_APP = 2,
+  BACKGROUND_TAB = 3,
+  BACKGROUND_APP = 4,
+  UNKNOWN_TYPE = 5,
 };
 
 // The Chrome OS TabManagerDelegate is responsible for keeping the
@@ -134,7 +108,7 @@
   FRIEND_TEST_ALL_PREFIXES(TabManagerDelegateTest, KillMultipleProcesses);
   FRIEND_TEST_ALL_PREFIXES(TabManagerDelegateTest, SetOomScoreAdj);
 
-  struct Candidate;
+  class Candidate;
   class FocusedProcess;
   class UmaReporter;
 
@@ -152,7 +126,7 @@
   // Cache OOM scores in memory.
   typedef base::hash_map<base::ProcessHandle, int> ProcessScoreMap;
 
-  // Get the list of candidates to kill, sorted by reversed importance.
+  // Get the list of candidates to kill, sorted by descending importance.
   static std::vector<Candidate> GetSortedCandidates(
       const TabStatsList& tab_list,
       const std::vector<arc::ArcProcess>& arc_processes);
@@ -190,8 +164,8 @@
   // distributed evenly in [|range_begin|, |range_end|).
   // The new score is set in |new_map|.
   void DistributeOomScoreInRange(
-      std::vector<TabManagerDelegate::Candidate>::reverse_iterator rbegin,
-      std::vector<TabManagerDelegate::Candidate>::reverse_iterator rend,
+      std::vector<TabManagerDelegate::Candidate>::const_iterator begin,
+      std::vector<TabManagerDelegate::Candidate>::const_iterator end,
       int range_begin,
       int range_end,
       ProcessScoreMap* new_map);
@@ -236,23 +210,39 @@
 };
 
 // On ARC enabled machines, either a tab or an app could be a possible
-// victim of low memory kill process. This is a helper struct which holds a
+// victim of low memory kill process. This is a helper class which holds a
 // pointer to an app or a tab (but not both) to facilitate prioritizing the
 // victims.
-struct TabManagerDelegate::Candidate {
-  Candidate(const TabStats* _tab, int _priority)
-      : tab(_tab), priority(_priority), is_arc_app(false) {}
-  Candidate(const arc::ArcProcess* _app, int _priority)
-      : app(_app), priority(_priority), is_arc_app(true) {}
+class TabManagerDelegate::Candidate {
+ public:
+  explicit Candidate(const TabStats* tab)
+      : tab_(tab), app_(nullptr), process_type_(GetProcessTypeInternal()) {
+    DCHECK(tab_);
+  }
+  explicit Candidate(const arc::ArcProcess* app)
+      : tab_(nullptr), app_(app), process_type_(GetProcessTypeInternal()) {
+    DCHECK(app_);
+  }
 
-  bool operator<(const Candidate& rhs) const { return priority < rhs.priority; }
+  // Move-only class.
+  Candidate(Candidate&&) = default;
+  Candidate& operator=(Candidate&& other);
 
-  union {
-    const TabStats* tab;
-    const arc::ArcProcess* app;
-  };
-  int priority;
-  bool is_arc_app;
+  // Higher priority first.
+  bool operator<(const Candidate& rhs) const;
+
+  const TabStats* tab() const { return tab_; }
+  const arc::ArcProcess* app() const { return app_; }
+  ProcessType process_type() const { return process_type_; }
+
+ private:
+  // Derive process type for this candidate. Used to initialize |process_type_|.
+  ProcessType GetProcessTypeInternal() const;
+
+  const TabStats* tab_;
+  const arc::ArcProcess* app_;
+  ProcessType process_type_;
+  DISALLOW_COPY_AND_ASSIGN(Candidate);
 };
 
 // A thin wrapper over library process_metric.h to get memory status so unit
diff --git a/chrome/browser/memory/tab_manager_delegate_chromeos_unittest.cc b/chrome/browser/memory/tab_manager_delegate_chromeos_unittest.cc
index 929f37c9..e064d19 100644
--- a/chrome/browser/memory/tab_manager_delegate_chromeos_unittest.cc
+++ b/chrome/browser/memory/tab_manager_delegate_chromeos_unittest.cc
@@ -28,52 +28,23 @@
 
 namespace memory {
 
+using TabManagerDelegateTest = testing::Test;
+
 namespace {
-
-const char kExoShellSurfaceWindowName[] = "ExoShellSurface";
-const char kArcProcessNamePrefix[] = "org.chromium.arc.";
-
+constexpr bool kIsFocused = true;
+constexpr bool kNotFocused = false;
 }  // namespace
 
-class TabManagerDelegateTest : public ash::test::AshTestBase {
- public:
-  TabManagerDelegateTest() : application_id_(kArcProcessNamePrefix) {}
-  ~TabManagerDelegateTest() override {}
-
-  void SetUp() override {
-    AshTestBase::SetUp();
-
-    arc_window_ = CreateTestWindowInShellWithId(0);
-    arc_window_->SetName(kExoShellSurfaceWindowName);
-    exo::ShellSurface::SetApplicationId(arc_window_,
-                                        &application_id_);
-  }
-
- protected:
-  void ActivateArcWindow() {
-    GetActivationClient()->ActivateWindow(arc_window_);
-  }
-  void DeactivateArcWindow() {
-    GetActivationClient()->DeactivateWindow(arc_window_);
-  }
-
- private:
-  aura::client::ActivationClient* GetActivationClient() {
-    return aura::client::GetActivationClient(
-        ash::Shell::GetPrimaryRootWindow());
-  }
-
-  aura::Window* arc_window_;
-  std::string application_id_;
-};
-
 TEST_F(TabManagerDelegateTest, CandidatesSorted) {
   std::vector<arc::ArcProcess> arc_processes;
-  arc_processes.emplace_back(1, 10, "top", arc::mojom::ProcessState::TOP);
-  arc_processes.emplace_back(2, 20, "foreground",
-                             arc::mojom::ProcessState::FOREGROUND_SERVICE);
-  arc_processes.emplace_back(3, 30, "service",
-                             arc::mojom::ProcessState::SERVICE);
+  arc_processes.emplace_back(1, 10, "focused", arc::mojom::ProcessState::TOP,
+                             kIsFocused, 100);
+  arc_processes.emplace_back(2, 20, "visible1", arc::mojom::ProcessState::TOP,
+                             kNotFocused, 200);
+  arc_processes.emplace_back(
+      3, 30, "service", arc::mojom::ProcessState::SERVICE, kNotFocused, 500);
+  arc_processes.emplace_back(4, 40, "visible2", arc::mojom::ProcessState::TOP,
+                             kNotFocused, 150);
 
   TabStats tab1, tab2, tab3, tab4, tab5;
   tab1.tab_contents_id = 100;
@@ -97,55 +68,28 @@
 
   std::vector<TabManagerDelegate::Candidate> candidates;
 
-  // Case 1: ARC window in the foreground.
-  ActivateArcWindow();
   candidates = TabManagerDelegate::GetSortedCandidates(
           tab_list, arc_processes);
-  EXPECT_EQ(8U, candidates.size());
+  EXPECT_EQ(9U, candidates.size());
 
-  EXPECT_EQ("service", candidates[0].app->process_name());
-  EXPECT_EQ("foreground", candidates[1].app->process_name());
-  // internal page.
-  EXPECT_EQ(200, candidates[2].tab->tab_contents_id);
+  // focused app.
+  EXPECT_EQ("focused", candidates[0].app()->process_name());
+  // visible app 1, last_activity_time larger than visible app 2.
+  EXPECT_EQ("visible1", candidates[1].app()->process_name());
+  // visible app 2, last_activity_time less than visible app 1.
+  EXPECT_EQ("visible2", candidates[2].app()->process_name());
+  // pinned and media.
+  EXPECT_EQ(300, candidates[3].tab()->tab_contents_id);
+  // media.
+  EXPECT_EQ(400, candidates[4].tab()->tab_contents_id);
+  // pinned.
+  EXPECT_EQ(100, candidates[5].tab()->tab_contents_id);
   // chrome app.
-  EXPECT_EQ(500, candidates[3].tab->tab_contents_id);
-  // pinned.
-  EXPECT_EQ(100, candidates[4].tab->tab_contents_id);
-  // media.
-  EXPECT_EQ(400, candidates[5].tab->tab_contents_id);
-  // pinned and media.
-  EXPECT_EQ(300, candidates[6].tab->tab_contents_id);
-  // ARC window is the active window, so top app has highest priority.
-  EXPECT_EQ("top", candidates[7].app->process_name());
-
-  // Case 2: ARC window in the background.
-  DeactivateArcWindow();
-  candidates = TabManagerDelegate::GetSortedCandidates(
-          tab_list, arc_processes);
-  EXPECT_EQ(8U, candidates.size());
-
-  EXPECT_EQ("service", candidates[0].app->process_name());
-  EXPECT_EQ("foreground", candidates[1].app->process_name());
+  EXPECT_EQ(500, candidates[6].tab()->tab_contents_id);
   // internal page.
-  EXPECT_EQ(200, candidates[2].tab->tab_contents_id);
-
-  // Chrome app and android app are tied, so both orders are correct.
-  if (candidates[3].is_arc_app) {
-    EXPECT_EQ("top", candidates[3].app->process_name());
-    // chrome app.
-    EXPECT_EQ(500, candidates[4].tab->tab_contents_id);
-  } else {
-    // chrome app.
-    EXPECT_EQ(500, candidates[3].tab->tab_contents_id);
-    EXPECT_EQ("top", candidates[4].app->process_name());
-  }
-
-  // pinned.
-  EXPECT_EQ(100, candidates[5].tab->tab_contents_id);
-  // media.
-  EXPECT_EQ(400, candidates[6].tab->tab_contents_id);
-  // pinned and media.
-  EXPECT_EQ(300, candidates[7].tab->tab_contents_id);
+  EXPECT_EQ(200, candidates[7].tab()->tab_contents_id);
+  // background service.
+  EXPECT_EQ("service", candidates[8].app()->process_name());
 }
 
 class MockTabManagerDelegate : public TabManagerDelegate {
@@ -226,13 +170,15 @@
   arc::FakeArcBridgeService fake_arc_bridge_service;
   MockTabManagerDelegate tab_manager_delegate;
 
-  ActivateArcWindow();
   std::vector<arc::ArcProcess> arc_processes;
-  arc_processes.emplace_back(1, 10, "top", arc::mojom::ProcessState::TOP);
-  arc_processes.emplace_back(2, 20, "foreground",
-                             arc::mojom::ProcessState::FOREGROUND_SERVICE);
-  arc_processes.emplace_back(3, 30, "service",
-                             arc::mojom::ProcessState::SERVICE);
+  arc_processes.emplace_back(1, 10, "focused", arc::mojom::ProcessState::TOP,
+                             kIsFocused, 100);
+  arc_processes.emplace_back(2, 20, "visible1", arc::mojom::ProcessState::TOP,
+                             kNotFocused, 200);
+  arc_processes.emplace_back(
+      3, 30, "service", arc::mojom::ProcessState::SERVICE, kNotFocused, 500);
+  arc_processes.emplace_back(4, 40, "visible2", arc::mojom::ProcessState::TOP,
+                             kNotFocused, 150);
 
   TabStats tab1, tab2, tab3, tab4, tab5;
   tab1.is_pinned = true;
@@ -253,27 +199,29 @@
   TabStatsList tab_list = {tab1, tab2, tab3, tab4, tab5};
 
   // Sorted order:
-  // app "service"     pid: 30  oom_socre_adj: 825
-  // app "foreground"  pid: 20  oom_score_adj: 650
-  // tab2              pid: 11  oom_socre_adj: 417
-  // tab5              pid: 12  oom_score_adj: 358
-  // tab1              pid: 11  oom_socre_adj: 417
-  // tab4              pid: 12  oom_socre_adj: 358
-  // tab3              pid: 12  oom_score_adj: 358
-  // app "top"         pid: 10  oom_score_adj: 300
+  // app "focused"     pid: 10
+  // app "visible1"    pid: 20
+  // app "visible2"    pid: 40
+  // tab3              pid: 12
+  // tab4              pid: 12
+  // tab1              pid: 11
+  // tab5              pid: 12
+  // tab2              pid: 11
+  // app "service"     pid: 30
   tab_manager_delegate.AdjustOomPrioritiesImpl(tab_list, arc_processes);
   auto& oom_score_map = tab_manager_delegate.oom_score_map_;
 
-  EXPECT_EQ(5U, oom_score_map.size());
+  EXPECT_EQ(6U, oom_score_map.size());
 
   // Higher priority part.
   EXPECT_EQ(300, oom_score_map[10]);
-  EXPECT_EQ(358, oom_score_map[12]);
-  EXPECT_EQ(417, oom_score_map[11]);
+  EXPECT_EQ(344, oom_score_map[20]);
+  EXPECT_EQ(388, oom_score_map[40]);
+  EXPECT_EQ(431, oom_score_map[12]);
+  EXPECT_EQ(475, oom_score_map[11]);
 
   // Lower priority part.
-  EXPECT_EQ(650, oom_score_map[20]);
-  EXPECT_EQ(825, oom_score_map[30]);
+  EXPECT_EQ(650, oom_score_map[30]);
 }
 
 TEST_F(TabManagerDelegateTest, KillMultipleProcesses) {
@@ -285,14 +233,15 @@
   // Instantiate the mock instance.
   MockTabManagerDelegate tab_manager_delegate(memory_stat);
 
-  ActivateArcWindow();
-
   std::vector<arc::ArcProcess> arc_processes;
-  arc_processes.emplace_back(10001, 100, "top", arc::mojom::ProcessState::TOP);
-  arc_processes.emplace_back(10002, 200, "foreground",
-                             arc::mojom::ProcessState::FOREGROUND_SERVICE);
-  arc_processes.emplace_back(10003, 300, "service",
-                             arc::mojom::ProcessState::SERVICE);
+  arc_processes.emplace_back(1, 10, "focused", arc::mojom::ProcessState::TOP,
+                             kIsFocused, 100);
+  arc_processes.emplace_back(2, 20, "visible1", arc::mojom::ProcessState::TOP,
+                             kNotFocused, 200);
+  arc_processes.emplace_back(
+      3, 30, "service", arc::mojom::ProcessState::SERVICE, kNotFocused, 500);
+  arc_processes.emplace_back(4, 40, "visible2", arc::mojom::ProcessState::TOP,
+                             kNotFocused, 150);
 
   TabStats tab1, tab2, tab3, tab4, tab5;
   tab1.is_pinned = true;
@@ -318,36 +267,40 @@
   TabStatsList tab_list = {tab1, tab2, tab3, tab4, tab5};
 
   // Sorted order:
-  // app "service"     pid: 30
-  // app "foreground"  pid: 20
-  // tab2              pid: 11
-  // tab5              pid: 12
-  // tab1              pid: 11
-  // tab4              pid: 12
-  // tab3              pid: 12
-  // app "top"         pid: 10
+  // app "focused"     pid: 10  nspid 1
+  // app "visible1"    pid: 20  nspid 2
+  // app "visible2"    pid: 40  nspid 4
+  // tab3              pid: 12  tab_contents_id 3
+  // tab4              pid: 12  tab_contents_id 4
+  // tab1              pid: 11  tab_contents_id 1
+  // tab5              pid: 12  tab_contents_id 5
+  // tab2              pid: 11  tab_contents_id 2
+  // app "service"     pid: 30  nspid 3
   memory_stat->SetTargetMemoryToFreeKB(250000);
-  // The 3 entities to be killed.
-  memory_stat->SetProcessPss(300, 10000);
-  memory_stat->SetProcessPss(200, 200000);
-  memory_stat->SetProcessPss(11, 50000);
+  // Entities to be killed.
+  memory_stat->SetProcessPss(30, 10000);
+  memory_stat->SetProcessPss(11, 200000);
+  memory_stat->SetProcessPss(12, 30000);
   // Should not be used.
-  memory_stat->SetProcessPss(100, 30000);
-  memory_stat->SetProcessPss(12, 100000);
+  memory_stat->SetProcessPss(40, 50000);
+  memory_stat->SetProcessPss(20, 30000);
+  memory_stat->SetProcessPss(10, 100000);
 
   tab_manager_delegate.LowMemoryKillImpl(tab_list, arc_processes);
 
   auto killed_arc_processes = tab_manager_delegate.GetKilledArcProcesses();
   auto killed_tabs = tab_manager_delegate.GetKilledTabs();
 
-  EXPECT_EQ(2U, killed_arc_processes.size());
-  EXPECT_EQ(1U, killed_tabs.size());
-
-  // nspid.
-  EXPECT_EQ(10003, killed_arc_processes[0]);
-  EXPECT_EQ(10002, killed_arc_processes[1]);
-  // tab content id.
+  // Killed apps and their nspid.
+  EXPECT_EQ(1U, killed_arc_processes.size());
+  EXPECT_EQ(3, killed_arc_processes[0]);
+  // Killed tabs and their content id.
+  // Note that process with pid 11 is counted twice. But so far I don't have a
+  // good way to estimate the memory freed if multiple tabs share one process.
+  EXPECT_EQ(3U, killed_tabs.size());
   EXPECT_EQ(2, killed_tabs[0]);
+  EXPECT_EQ(5, killed_tabs[1]);
+  EXPECT_EQ(1, killed_tabs[2]);
 }
 
 }  // namespace memory
diff --git a/chrome/browser/password_manager/credential_manager_browsertest.cc b/chrome/browser/password_manager/credential_manager_browsertest.cc
index 388d764..9660f51e 100644
--- a/chrome/browser/password_manager/credential_manager_browsertest.cc
+++ b/chrome/browser/password_manager/credential_manager_browsertest.cc
@@ -92,6 +92,7 @@
       RenderViewHost(),
       "navigator.credentials.get({password: true})"
       ".then(cred => window.location = '/password/done.html')"));
+  WaitForPasswordStore();
   ASSERT_EQ(password_manager::ui::CREDENTIAL_REQUEST_STATE,
             PasswordsModelDelegateFromWebContents(WebContents())->GetState());
   PasswordsModelDelegateFromWebContents(WebContents())->ChooseCredential(
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 21a3a858..6f860b84 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -509,6 +509,9 @@
   { key::kArcEnabled,
     prefs::kArcEnabled,
     base::Value::TYPE_BOOLEAN },
+  { key::kArcBackupRestoreEnabled,
+    prefs::kArcBackupRestoreEnabled,
+    base::Value::TYPE_BOOLEAN },
 #endif  // defined(OS_CHROMEOS)
 
 // Metrics reporting is controlled by a platform specific policy for ChromeOS
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index 45228c4..446d632 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -3957,6 +3957,36 @@
   TearDownTest();
 }
 
+// Test ArcBackupRestoreEnabled policy.
+IN_PROC_BROWSER_TEST_F(ArcPolicyTest, ArcBackupRestoreEnabled) {
+  SetUpTest();
+
+  const PrefService* const pref = browser()->profile()->GetPrefs();
+
+  // ARC Backup & Restore is switched on by default.
+  EXPECT_TRUE(pref->GetBoolean(prefs::kArcBackupRestoreEnabled));
+  EXPECT_FALSE(pref->IsManagedPreference(prefs::kArcBackupRestoreEnabled));
+
+  // Disable ARC Backup & Restore.
+  PolicyMap policies;
+  policies.Set(key::kArcBackupRestoreEnabled, POLICY_LEVEL_MANDATORY,
+               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+               base::MakeUnique<base::FundamentalValue>(false), nullptr);
+  UpdateProviderPolicy(policies);
+  EXPECT_FALSE(pref->GetBoolean(prefs::kArcBackupRestoreEnabled));
+  EXPECT_TRUE(pref->IsManagedPreference(prefs::kArcBackupRestoreEnabled));
+
+  // Enable ARC Backup & Restore.
+  policies.Set(key::kArcBackupRestoreEnabled, POLICY_LEVEL_MANDATORY,
+               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+               base::MakeUnique<base::FundamentalValue>(true), nullptr);
+  UpdateProviderPolicy(policies);
+  EXPECT_TRUE(pref->GetBoolean(prefs::kArcBackupRestoreEnabled));
+  EXPECT_TRUE(pref->IsManagedPreference(prefs::kArcBackupRestoreEnabled));
+
+  TearDownTest();
+}
+
 namespace {
 const char kTestUser1[] = "test1@domain.com";
 }  // anonymous namespace
diff --git a/chrome/browser/resources/settings/animation/animation_group.html b/chrome/browser/resources/settings/animation/animation_group.html
new file mode 100644
index 0000000..d2ee19ed
--- /dev/null
+++ b/chrome/browser/resources/settings/animation/animation_group.html
@@ -0,0 +1,2 @@
+<link rel="import" href="chrome://resources/html/cr.html">
+<script src="/animation/animation_group.js"></script>
diff --git a/chrome/browser/resources/settings/animation/animation_group.js b/chrome/browser/resources/settings/animation/animation_group.js
new file mode 100644
index 0000000..7e731efa
--- /dev/null
+++ b/chrome/browser/resources/settings/animation/animation_group.js
@@ -0,0 +1,49 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('settings.animation', function() {
+  'use strict';
+
+  /**
+   * An AnimationGroup manages a set of animations, handles any styling setup or
+   * cleanup, and provides a Promise for chaining actions on finish or cancel.
+   * This abstracts out all these details so UI elements can simply create an
+   * object rather than individually track the state of every element, style and
+   * web animation. AnimationGroups may compose web animations and other
+   * AnimationGroups.
+   * @interface
+   */
+  function AnimationGroup() {}
+
+  AnimationGroup.prototype = {
+    /**
+     * Sets up and plays the animation(s).
+     * @return {!Promise} Convenient reference to |finished| for chaining.
+     */
+    play: assertNotReached,
+
+    /**
+     * If animations are still playing, immediately finishes them and resolves
+     * |finished| with the |true| value.
+     */
+    finish: assertNotReached,
+
+    /**
+     * If animations are still playing, immediately cancels them and resolves
+     * |finished| with the |false| value.
+     */
+    cancel: assertNotReached,
+
+    /**
+     * Resolved with a success value once the AnimationGroup finishes (true) or
+     * is canceled (false).
+     * @type {?Promise<boolean>}
+     */
+    finished: null,
+  };
+
+  return {
+    AnimationGroup: AnimationGroup,
+  };
+});
diff --git a/chrome/browser/resources/settings/animation/compiled_resources2.gyp b/chrome/browser/resources/settings/animation/compiled_resources2.gyp
index 89d95422..45a628db2 100644
--- a/chrome/browser/resources/settings/animation/compiled_resources2.gyp
+++ b/chrome/browser/resources/settings/animation/compiled_resources2.gyp
@@ -12,5 +12,12 @@
       ],
       'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
     },
+    {
+      'target_name': 'animation_group',
+      'dependencies': [
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+      ],
+      'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+    },
   ],
 }
diff --git a/chrome/browser/resources/settings/internet_page/network_property_list.html b/chrome/browser/resources/settings/internet_page/network_property_list.html
index 1a8ba70..38ed738 100644
--- a/chrome/browser/resources/settings/internet_page/network_property_list.html
+++ b/chrome/browser/resources/settings/internet_page/network_property_list.html
@@ -6,63 +6,41 @@
 
 <dom-module name="network-property-list">
   <template>
-    <style>
-      span {
-        -webkit-margin-end: 5px;
-        margin-bottom: 5px;
-      }
-
-      cr-policy-network-indicator {
-        margin-bottom: 5px;
-      }
-
-      span.fill {
-        border-bottom-color: grey;
-        border-bottom-style: dotted;
-        border-width: thin;
-        min-width: 20px;
-      }
-
+    <style include="settings-shared">
       paper-input-container {
-        -webkit-margin-start: 5px;
-        margin-bottom: -3px;
-        margin-top: -12px;
-      }
-
-      #outerDiv {
-        padding: 5px 0;
+        --paper-input-container-input: {
+          color: var(--paper-grey-600);
+          font-size: 100%;
+          font-weight: 400;
+        };
+        margin-bottom: -12px;
+        margin-top: -8px;
       }
     </style>
-    <div id="outerDiv" class="layout horizontal">
-      <div class="layout vertical">
-        <template is="dom-repeat" items="[[fields]]">
-          <div class="layout horizontal"
-              hidden$="[[!showProperty_(propertyDict, editFieldTypes, item)]]">
-            <span>[[getPropertyLabel_(item)]]</span>
-            <span class="flex fill"></span>
-          </div>
-        </template>
+    <template is="dom-repeat" items="[[fields]]"
+        filter="[[computeFilter_(propertyDict, editFieldTypes)]]">
+      <div class="settings-box two-line single-column">
+        <!-- Propety label -->
+        <div>[[getPropertyLabel_(item)]]</div>
+        <!-- Uneditable property value -->
+        <div class="layout horizontal"
+            hidden$="[[isEditable_(propertyDict, editFieldTypes, item, '')]]">
+          <div class="secondary">[[getPropertyValue_(propertyDict, item)]]</div>
+          <cr-policy-network-indicator property="[[propertyDict]]">
+          </cr-policy-network-indicator>
+        </div>
+        <!-- Editable string property value -->
+        <div class="layout horizontal" hidden$="[[!isEditable_(
+            propertyDict, editFieldTypes, item, 'String')]]">
+          <paper-input-container no-label-float>
+            <input class="embedded-input" id="[[item]]" is="iron-input"
+                value="[[getPropertyValue_(propertyDict, item)]]"
+                on-blur="onValueChange_">
+          </paper-input-container>
+        </div>
+        <!-- TODO(stevenjb): Support non-string types -->
       </div>
-      <div class="layout vertical">
-        <template is="dom-repeat" items="[[fields]]">
-          <div class="layout horizontal"
-              hidden$="[[!showNoEdit_(propertyDict, editFieldTypes, item)]]">
-            <span>[[getPropertyValue_(propertyDict, item)]]</span>
-            <cr-policy-network-indicator property="[[propertyDict]]">
-            </cr-policy-network-indicator>
-          </div>
-          <div class="layout horizontal" hidden$=
-               "[[!showEdit_(propertyDict, editFieldTypes, item, 'String')]]">
-            <paper-input-container no-label-float>
-              <input id="[[item]]" is="iron-input"
-                  value="[[getPropertyValue_(propertyDict, item)]]"
-                  on-blur="onValueChange_">
-            </paper-input-container>
-          </div>
-          <!-- TODO(stevenjb): Support non-string types. -->
-        </template>
-      </div>
-    </div>
+    </template>
   </template>
   <script src="network_property_list.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/internet_page/network_property_list.js b/chrome/browser/resources/settings/internet_page/network_property_list.js
index b988501a..cec3e7b 100644
--- a/chrome/browser/resources/settings/internet_page/network_property_list.js
+++ b/chrome/browser/resources/settings/internet_page/network_property_list.js
@@ -4,11 +4,8 @@
 
 /**
  * @fileoverview Polymer element for displaying a list of network properties
- * in a list in the format:
- *    Key1.........Value1
- *    KeyTwo.......ValueTwo
- * This also supports editing fields inline for fields listed in editFieldTypes:
- *    KeyThree....._________
+ * in a list. This also supports editing fields inline for fields listed in
+ * editFieldTypes.
  * TODO(stevenjb): Translate the keys and (where appropriate) values.
  */
 Polymer({
@@ -21,9 +18,7 @@
      * The dictionary containing the properties to display.
      * @type {!Object|undefined}
      */
-    propertyDict: {
-      type: Object
-    },
+    propertyDict: {type: Object},
 
     /**
      * Fields to display.
@@ -31,7 +26,7 @@
      */
     fields: {
       type: Array,
-      value: function() { return []; }
+      value: function() { return []; },
     },
 
     /**
@@ -45,7 +40,7 @@
      */
     editFieldTypes: {
       type: Object,
-      value: function() { return {}; }
+      value: function() { return {}; },
     },
   },
 
@@ -63,8 +58,8 @@
     var curValue = this.get(field, this.propertyDict);
     if (typeof curValue == 'object') {
       // Extract the property from an ONC managed dictionary.
-      curValue =
-          CrOnc.getActiveValue(/** @type {!CrOnc.ManagedProperty} */(curValue));
+      curValue = CrOnc.getActiveValue(
+          /** @type {!CrOnc.ManagedProperty} */ (curValue));
     }
     var newValue = event.target.value;
     if (newValue == curValue)
@@ -94,8 +89,20 @@
   },
 
   /**
+   * Generates a filter function dependent on propertyDict and editFieldTypes.
    * @param {!Object} propertyDict
-   * @param {!Object} editFieldTypes The editFieldTypes object.
+   * @param {!Object} editFieldTypes
+   * @private
+   */
+  computeFilter_(propertyDict, editFieldTypes) {
+    return function(key) {
+      return this.showProperty_(propertyDict, editFieldTypes, key);
+    }.bind(this);
+  },
+
+  /**
+   * @param {!Object} propertyDict
+   * @param {!Object} editFieldTypes
    * @param {string} key The property key.
    * @return {boolean} Whether or not to show the property. Editable properties
    *     are always shown.
@@ -111,37 +118,17 @@
    * @param {!Object} propertyDict
    * @param {!Object} editFieldTypes The editFieldTypes object.
    * @param {string} key The property key.
-   * @return {boolean} True if |key| exists in |propertiesDict| and is not
-   *     editable.
-   * @private
-   */
-  showNoEdit_: function(propertyDict, editFieldTypes, key) {
-    if (!this.hasPropertyValue_(propertyDict, key))
-      return false;
-    var property = /** @type {!CrOnc.ManagedProperty|undefined} */(
-      this.get(key, propertyDict));
-    if (this.isNetworkPolicyEnforced(property))
-      return true;
-    return !editFieldTypes[key];
-  },
-
-  /**
-   * @param {!Object} propertyDict
-   * @param {!Object} editFieldTypes The editFieldTypes object.
-   * @param {string} key The property key.
    * @param {string} type The field type.
-   * @return {boolean} True if |key| exists in |propertyDict| and is of editable
-   *     type |type|.
+   * @return {boolean}
    * @private
    */
-  showEdit_: function(propertyDict, editFieldTypes, key, type) {
-    if (!this.hasPropertyValue_(propertyDict, key))
-      return false;
-    var property = /** @type {!CrOnc.ManagedProperty|undefined} */(
+  isEditable_: function(propertyDict, editFieldTypes, key, type) {
+    var property = /** @type {!CrOnc.ManagedProperty|undefined} */ (
         this.get(key, propertyDict));
     if (this.isNetworkPolicyEnforced(property))
       return false;
-    return editFieldTypes[key] == type;
+    var editType = editFieldTypes[key];
+    return editType !== undefined && (type == '' || editType == type);
   },
 
   /**
@@ -157,11 +144,11 @@
     if (typeof value == 'object') {
       // Extract the property from an ONC managed dictionary
       value =
-          CrOnc.getActiveValue(/** @type {!CrOnc.ManagedProperty} */(value));
+          CrOnc.getActiveValue(/** @type {!CrOnc.ManagedProperty} */ (value));
     }
     // TODO(stevenjb): Localize.
     if (typeof value == 'number' || typeof value == 'boolean')
       return value.toString();
-    return /** @type {string} */(value);
+    return /** @type {string} */ (value);
   },
 });
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index 7a22727..c0baa63 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -78,6 +78,12 @@
       <structure name="IDR_SETTINGS_ANIMATION_ANIMATION_JS"
                  file="animation/animation.js"
                  type="chrome_html" />
+      <structure name="IDR_SETTINGS_ANIMATION_ANIMATION_GROUP_HTML"
+                 file="animation/animation_group.html"
+                 type="chrome_html" />
+      <structure name="IDR_SETTINGS_ANIMATION_ANIMATION_GROUP_JS"
+                 file="animation/animation_group.js"
+                 type="chrome_html" />
       <structure name="IDR_SETTINGS_CR_SETTINGS_ANIMATED_PAGES_HTML"
                  file="settings_page/settings_animated_pages.html"
                  type="chrome_html" />
diff --git a/chrome/browser/resources/snippets_internals.html b/chrome/browser/resources/snippets_internals.html
index a054312..b8bb937 100644
--- a/chrome/browser/resources/snippets_internals.html
+++ b/chrome/browser/resources/snippets_internals.html
@@ -106,12 +106,12 @@
     </div>
   </div>
   
-  <div id="discarded-snippets">
-    <h2>Discarded snippets <span class="detail">(click for details)</span></h2>
+  <div id="dismissed-snippets">
+    <h2>Dismissed snippets <span class="detail">(click for details)</span></h2>
     <table class="section-details">
       <tr jsselect="list" style="display:none">
         <td class="title-link">
-          <span class="discarded-snippet-title" jsvalues="hidden-id:id">
+          <span class="dismissed-snippet-title" jsvalues="hidden-id:id">
               <span jscontent="title"></span> &gt;&gt;</span>
           <div jsvalues="id:id" class="snippet-detail hidden">
             <table>
@@ -140,9 +140,9 @@
             </table>
           </div>
     </table>
-    <div class="detail" id="discarded-snippets-empty"></div>
+    <div class="detail" id="dismissed-snippets-empty"></div>
     <div class="forms">
-      <input id="discarded-snippets-clear" type="submit" value="Clear list">
+      <input id="dismissed-snippets-clear" type="submit" value="Clear list">
     </div>
   </div>
   
@@ -205,8 +205,8 @@
     <div class="forms">
       <input id="submit-clear-cached-suggestions" type="submit"
           value="Clear cached suggestions">
-      <input id="submit-clear-discarded-suggestions" type="submit"
-          value="Clear discarded suggestions">
+      <input id="submit-clear-dismissed-suggestions" type="submit"
+          value="Clear dismissed suggestions">
     </div>
   </div>
 </div>
diff --git a/chrome/browser/resources/snippets_internals.js b/chrome/browser/resources/snippets_internals.js
index aab4b35..79c9ad14 100644
--- a/chrome/browser/resources/snippets_internals.js
+++ b/chrome/browser/resources/snippets_internals.js
@@ -33,8 +33,8 @@
       event.preventDefault();
     });
 
-    $('discarded-snippets-clear').addEventListener('click', function(event) {
-      chrome.send('clearDiscarded');
+    $('dismissed-snippets-clear').addEventListener('click', function(event) {
+      chrome.send('clearDismissed');
       event.preventDefault();
     });
 
@@ -44,9 +44,9 @@
       event.preventDefault();
     });
 
-    $('submit-clear-discarded-suggestions')
+    $('submit-clear-dismissed-suggestions')
         .addEventListener('click', function(event) {
-      chrome.send('clearDiscardedSuggestions');
+      chrome.send('clearDismissedSuggestions');
       event.preventDefault();
     });
 
@@ -79,9 +79,9 @@
     displayList(snippets, 'snippets', 'snippet-title');
   }
 
-  function receiveDiscardedSnippets(discardedSnippets) {
-    displayList(discardedSnippets, 'discarded-snippets',
-                'discarded-snippet-title');
+  function receiveDismissedSnippets(dismissedSnippets) {
+    displayList(dismissedSnippets, 'dismissed-snippets',
+                'dismissed-snippet-title');
   }
 
   function receiveContentSuggestions(categoriesList) {
@@ -150,7 +150,7 @@
     receiveProperty: receiveProperty,
     receiveHosts: receiveHosts,
     receiveSnippets: receiveSnippets,
-    receiveDiscardedSnippets: receiveDiscardedSnippets,
+    receiveDismissedSnippets: receiveDismissedSnippets,
     receiveContentSuggestions: receiveContentSuggestions,
     receiveJson: receiveJson,
   };
diff --git a/chrome/browser/spellchecker/feedback_sender.cc b/chrome/browser/spellchecker/feedback_sender.cc
index 879358d..5c6832b 100644
--- a/chrome/browser/spellchecker/feedback_sender.cc
+++ b/chrome/browser/spellchecker/feedback_sender.cc
@@ -45,11 +45,11 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "chrome/browser/spellchecker/word_trimmer.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/spellcheck_common.h"
 #include "chrome/common/spellcheck_marker.h"
 #include "chrome/common/spellcheck_messages.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
+#include "components/spellcheck/common/spellcheck_switches.h"
 #include "content/public/browser/render_process_host.h"
 #include "crypto/random.h"
 #include "crypto/secure_hash.h"
@@ -183,7 +183,7 @@
   if (base::FieldTrialList::FindFullName(kFeedbackFieldTrialName) ==
           kFeedbackFieldTrialEnabledGroupName &&
       base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableSpellingFeedbackFieldTrial)) {
+          spellcheck::switches::kEnableSpellingFeedbackFieldTrial)) {
     return "v2-internal";
   }
   return "v2";
@@ -206,10 +206,10 @@
   // TODO(rouslan): Remove the command-line switch when testing is complete.
   // http://crbug.com/247726
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kSpellingServiceFeedbackUrl)) {
+          spellcheck::switches::kSpellingServiceFeedbackUrl)) {
     feedback_service_url_ =
         GURL(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-            switches::kSpellingServiceFeedbackUrl));
+            spellcheck::switches::kSpellingServiceFeedbackUrl));
   }
 }
 
@@ -348,10 +348,10 @@
   // TODO(rouslan): Remove the command-line switch when testing is complete.
   // http://crbug.com/247726
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kSpellingServiceFeedbackIntervalSeconds)) {
+          spellcheck::switches::kSpellingServiceFeedbackIntervalSeconds)) {
     base::StringToInt(
         base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-            switches::kSpellingServiceFeedbackIntervalSeconds),
+            spellcheck::switches::kSpellingServiceFeedbackIntervalSeconds),
         &interval_seconds);
     if (interval_seconds < kMinIntervalSeconds)
       interval_seconds = kMinIntervalSeconds;
diff --git a/chrome/browser/spellchecker/feedback_sender_unittest.cc b/chrome/browser/spellchecker/feedback_sender_unittest.cc
index 5b42081..ed68bc41 100644
--- a/chrome/browser/spellchecker/feedback_sender_unittest.cc
+++ b/chrome/browser/spellchecker/feedback_sender_unittest.cc
@@ -17,11 +17,11 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/spellcheck_common.h"
 #include "chrome/common/spellcheck_marker.h"
 #include "chrome/common/spellcheck_result.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/spellcheck/common/spellcheck_switches.h"
 #include "components/variations/entropy_provider.h"
 #include "content/public/test/test_browser_thread.h"
 #include "net/url_request/test_url_fetcher_factory.h"
@@ -111,7 +111,7 @@
     // The command-line switch is temporary.
     // TODO(rouslan): Remove the command-line switch. http://crbug.com/247726
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        switches::kEnableSpellingFeedbackFieldTrial);
+        spellcheck::switches::kEnableSpellingFeedbackFieldTrial);
     feedback_.reset(new MockFeedbackSender(NULL, kLanguage, kCountry));
     feedback_->StartFeedbackCollection();
   }
diff --git a/chrome/browser/spellchecker/spellcheck_platform_android.cc b/chrome/browser/spellchecker/spellcheck_platform_android.cc
index 1017943..832c6e0 100644
--- a/chrome/browser/spellchecker/spellcheck_platform_android.cc
+++ b/chrome/browser/spellchecker/spellcheck_platform_android.cc
@@ -6,7 +6,7 @@
 
 #include "base/callback.h"
 #include "base/command_line.h"
-#include "chrome/common/chrome_switches.h"
+#include "components/spellcheck/common/spellcheck_switches.h"
 
 namespace spellcheck_platform {
 
@@ -19,7 +19,7 @@
 
 bool SpellCheckerAvailable() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kEnableAndroidSpellChecker);
+      spellcheck::switches::kEnableAndroidSpellChecker);
 }
 
 bool SpellCheckerProvidesPanel() {
diff --git a/chrome/browser/ui/ash/app_list/app_list_controller_ash.cc b/chrome/browser/ui/ash/app_list/app_list_controller_ash.cc
index 07611d07..b348d1a 100644
--- a/chrome/browser/ui/ash/app_list/app_list_controller_ash.cc
+++ b/chrome/browser/ui/ash/app_list/app_list_controller_ash.cc
@@ -4,8 +4,9 @@
 
 #include "chrome/browser/ui/ash/app_list/app_list_controller_ash.h"
 
+#include "ash/common/shelf/shelf_delegate.h"
+#include "ash/common/wm_shell.h"
 #include "ash/metrics/task_switch_metrics_recorder.h"
-#include "ash/shelf/shelf_delegate.h"
 #include "ash/shell.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
@@ -39,21 +40,21 @@
 }
 
 bool AppListControllerDelegateAsh::IsAppPinned(const std::string& app_id) {
-  return ash::Shell::GetInstance()->GetShelfDelegate()->IsAppPinned(app_id);
+  return ash::WmShell::Get()->shelf_delegate()->IsAppPinned(app_id);
 }
 
 bool AppListControllerDelegateAsh::IsAppOpen(const std::string& app_id) const {
   ash::ShelfID id =
-      ash::Shell::GetInstance()->GetShelfDelegate()->GetShelfIDForAppID(app_id);
+      ash::WmShell::Get()->shelf_delegate()->GetShelfIDForAppID(app_id);
   return id && ChromeLauncherController::instance()->IsOpen(id);
 }
 
 void AppListControllerDelegateAsh::PinApp(const std::string& app_id) {
-  ash::Shell::GetInstance()->GetShelfDelegate()->PinAppWithID(app_id);
+  ash::WmShell::Get()->shelf_delegate()->PinAppWithID(app_id);
 }
 
 void AppListControllerDelegateAsh::UnpinApp(const std::string& app_id) {
-  ash::Shell::GetInstance()->GetShelfDelegate()->UnpinAppWithID(app_id);
+  ash::WmShell::Get()->shelf_delegate()->UnpinAppWithID(app_id);
 }
 
 AppListControllerDelegate::Pinnable AppListControllerDelegateAsh::GetPinnable(
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc
index 8d8f328..738f7211 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.cc
@@ -5,13 +5,13 @@
 
 #include <string>
 
+#include "ash/common/shelf/shelf_delegate.h"
 #include "ash/common/wm/maximize_mode/maximize_mode_controller.h"
 #include "ash/common/wm/window_state.h"
 #include "ash/common/wm_lookup.h"
 #include "ash/common/wm_shell.h"
 #include "ash/display/display_manager.h"
 #include "ash/display/screen_orientation_controller_chromeos.h"
-#include "ash/shelf/shelf_delegate.h"
 #include "ash/shelf/shelf_util.h"
 #include "ash/shell.h"
 #include "ash/wm/window_state_aura.h"
diff --git a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
index ecda73f..6a70516 100644
--- a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
@@ -6,10 +6,10 @@
 
 #include <vector>
 
+#include "ash/common/shelf/shelf_delegate.h"
 #include "ash/common/shelf/shelf_model.h"
-#include "ash/shelf/shelf_delegate.h"
+#include "ash/common/wm_shell.h"
 #include "ash/shelf/shelf_util.h"
-#include "ash/shell.h"
 #include "ash/wm/window_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
@@ -72,11 +72,6 @@
 }
 
 void BrowserShortcutLauncherItemController::UpdateBrowserItemState() {
-  // The shell will not be available for win7_aura unittests like
-  // ChromeLauncherControllerTest.BrowserMenuGeneration.
-  if (!ash::Shell::HasInstance())
-    return;
-
   // Determine the new browser's active state and change if necessary.
   int browser_index =
       shelf_model_->GetItemIndexForType(ash::TYPE_BROWSER_SHORTCUT);
@@ -338,9 +333,8 @@
     return false;
 
   // v1 App popup windows with a valid app id have their own icon.
-  if (browser->is_app() &&
-      browser->is_type_popup() &&
-      ash::Shell::GetInstance()->GetShelfDelegate()->GetShelfIDForAppID(
+  if (browser->is_app() && browser->is_type_popup() &&
+      ash::WmShell::Get()->shelf_delegate()->GetShelfIDForAppID(
           web_app::GetExtensionIdFromApplicationName(browser->app_name())) > 0)
     return false;
 
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.h
index 55d1e5a4..17dd0c7 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.h
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl.h
@@ -8,10 +8,10 @@
 #include <list>
 #include <memory>
 
+#include "ash/common/shelf/shelf_delegate.h"
 #include "ash/common/shelf/shelf_model_observer.h"
 #include "ash/common/shelf/shelf_types.h"
 #include "ash/display/window_tree_host_manager.h"
-#include "ash/shelf/shelf_delegate.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "build/build_config.h"
diff --git a/chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.cc b/chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.cc
index e043c80d..eba8e08 100644
--- a/chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.cc
@@ -4,9 +4,9 @@
 
 #include "chrome/browser/ui/ash/launcher/extension_app_window_launcher_controller.h"
 
-#include "ash/shelf/shelf_delegate.h"
+#include "ash/common/shelf/shelf_delegate.h"
+#include "ash/common/wm_shell.h"
 #include "ash/shelf/shelf_util.h"
-#include "ash/shell.h"
 #include "ash/wm/window_util.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
@@ -163,8 +163,7 @@
     } else if (app_shelf_id == app_id) {
       // show_in_shelf in false and not a panel
       shelf_id =
-          ash::Shell::GetInstance()->GetShelfDelegate()->GetShelfIDForAppID(
-              app_id);
+          ash::WmShell::Get()->shelf_delegate()->GetShelfIDForAppID(app_id);
       // Check if the shelf_id corresponds to an already opened
       // showInShelf=true window that has the same app_id. The current
       // showInShelf=false window should not fold under this shelf item,
diff --git a/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_controller.mm b/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_controller.mm
index e2858cd..05c6cb3 100644
--- a/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_controller.mm
+++ b/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_controller.mm
@@ -597,7 +597,14 @@
 - (id)imageBrowser:(IKImageBrowserView*)browser itemAtIndex:(NSUInteger)index {
   DesktopMediaID::Type sourceType = [self sourceTypeForBrowser:browser];
   NSMutableArray* items = [self itemSetForType:sourceType];
-  return [items objectAtIndex:index];
+  DesktopMediaPickerItem* item = [items objectAtIndex:index];
+
+  // For screen source, if there is only one source, we can omit the label
+  // "Entire Screen", because it is redundant with tab label "Your Entire
+  // Screen".
+  [item setTitleHidden:browser == screenBrowser_ && [items count] == 1];
+
+  return item;
 }
 
 #pragma mark IKImageBrowserDelegate
@@ -681,10 +688,17 @@
       [[DesktopMediaPickerItem alloc] initWithSourceId:source.id
                                               imageUID:++lastImageUID_
                                             imageTitle:imageTitle]);
+
   [items insertObject:item atIndex:index];
   [browser reloadData];
-  if (sourceType == DesktopMediaID::TYPE_WEB_CONTENTS)
+  if (sourceType == DesktopMediaID::TYPE_WEB_CONTENTS) {
+    // Memorizing selection.
     [self setTabBrowserIndex:selectedIndex];
+  } else if (sourceType == DesktopMediaID::TYPE_SCREEN && [items count] == 1) {
+    // Preselect the first screen source.
+    [browser setSelectionIndexes:[NSIndexSet indexSetWithIndex:0]
+            byExtendingSelection:NO];
+  }
 
   NSString* autoselectSource = base::SysUTF8ToNSString(
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
diff --git a/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_controller_unittest.mm b/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_controller_unittest.mm
index 7b268ed..53c0bc20 100644
--- a/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_controller_unittest.mm
@@ -167,22 +167,21 @@
 TEST_F(DesktopMediaPickerControllerTest, ClickShareScreen) {
   [controller_ showWindow:nil];
   ChangeType(DesktopMediaID::TYPE_SCREEN);
+
+  EXPECT_FALSE([[controller_ shareButton] isEnabled]);
   AddScreen(0);
   screen_list_->SetSourceThumbnail(0);
+  // First screen will be automatically selected.
+  EXPECT_TRUE([[controller_ shareButton] isEnabled]);
+
   AddScreen(1);
   screen_list_->SetSourceThumbnail(1);
 
   EXPECT_EQ(2U, [[controller_ screenItems] count]);
-  EXPECT_FALSE([[controller_ shareButton] isEnabled]);
-
-  NSIndexSet* index_set = [NSIndexSet indexSetWithIndex:1];
-  [[controller_ screenBrowser] setSelectionIndexes:index_set
-                              byExtendingSelection:NO];
-  EXPECT_TRUE([[controller_ shareButton] isEnabled]);
 
   [[controller_ shareButton] performClick:nil];
   EXPECT_TRUE(WaitForCallback());
-  EXPECT_EQ(screen_list_->GetSource(1).id, source_reported_);
+  EXPECT_EQ(screen_list_->GetSource(0).id, source_reported_);
 }
 
 TEST_F(DesktopMediaPickerControllerTest, ClickShareWindow) {
@@ -407,3 +406,21 @@
   selected_index = [[browser selectedRowIndexes] firstIndex];
   EXPECT_EQ(1, [[items objectAtIndex:selected_index] sourceID].id);
 }
+
+TEST_F(DesktopMediaPickerControllerTest, SingleScreenNoLabel) {
+  [controller_ showWindow:nil];
+  ChangeType(DesktopMediaID::TYPE_SCREEN);
+
+  NSArray* items = [controller_ screenItems];
+
+  AddScreen(0);
+  screen_list_->SetSourceThumbnail(0);
+  EXPECT_EQ(1U, [items count]);
+  EXPECT_EQ(nil, [[items objectAtIndex:0] imageTitle]);
+
+  AddScreen(1);
+  screen_list_->SetSourceThumbnail(1);
+  EXPECT_EQ(2U, [items count]);
+  EXPECT_NE(nil, [[items objectAtIndex:0] imageTitle]);
+  EXPECT_NE(nil, [[items objectAtIndex:1] imageTitle]);
+}
diff --git a/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_item.h b/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_item.h
index 55191ef..e1cca7f 100644
--- a/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_item.h
+++ b/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_item.h
@@ -20,8 +20,11 @@
   base::scoped_nsobject<NSString> imageTitle_;
   base::scoped_nsobject<NSImage> image_;
   NSUInteger imageVersion_;
+  BOOL titleHidden_;
 }
 
+@property(assign, nonatomic) BOOL titleHidden;
+
 // Designated initializer.
 // |sourceID| is the corresponding source's ID as provided by the media list.
 // |imageUID| is a unique number in the context of the |IKImageBrowserView|
diff --git a/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_item.mm b/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_item.mm
index 45b9145e..470e99e 100644
--- a/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_item.mm
+++ b/chrome/browser/ui/cocoa/media_picker/desktop_media_picker_item.mm
@@ -10,6 +10,8 @@
 
 @implementation DesktopMediaPickerItem
 
+@synthesize titleHidden = titleHidden_;
+
 - (id)initWithSourceId:(content::DesktopMediaID)sourceID
               imageUID:(int)imageUID
             imageTitle:(NSString*)imageTitle {
@@ -45,7 +47,7 @@
 }
 
 - (NSString*)imageTitle {
-  return imageTitle_.get();
+  return titleHidden_ ? nil : imageTitle_;
 }
 
 - (NSUInteger)imageVersion {
diff --git a/chrome/browser/ui/search/instant_controller.cc b/chrome/browser/ui/search/instant_controller.cc
index 2045725..8dd31cc5 100644
--- a/chrome/browser/ui/search/instant_controller.cc
+++ b/chrome/browser/ui/search/instant_controller.cc
@@ -25,10 +25,6 @@
 #include "content/public/browser/web_contents.h"
 #include "url/gurl.h"
 
-// Macro used for logging debug events. |message| should be a std::string.
-#define LOG_INSTANT_DEBUG_EVENT(controller, message) \
-    controller->LogDebugEvent(message)
-
 namespace {
 
 bool IsContentsFrom(const InstantTab* page,
@@ -72,22 +68,25 @@
 
 bool InstantController::SubmitQuery(const base::string16& search_terms,
                                     const EmbeddedSearchRequestParams& params) {
-  if (instant_tab_ && instant_tab_->supports_instant() &&
-      search_mode_.is_origin_search()) {
-    // Use |instant_tab_| to run the query if we're already on a search results
-    // page. (NOTE: in particular, we do not send the query to NTPs.)
-    SearchTabHelper::FromWebContents(instant_tab_->web_contents())->Submit(
-        search_terms, params);
-    instant_tab_->web_contents()->Focus();
-    EnsureSearchTermsAreSet(instant_tab_->web_contents(), search_terms);
-    return true;
-  }
-  return false;
+  if (!instant_tab_ || !instant_tab_->web_contents())
+    return false;
+
+  SearchTabHelper* search_tab =
+      SearchTabHelper::FromWebContents(instant_tab_->web_contents());
+  if (!search_tab->SupportsInstant() || !search_mode_.is_origin_search())
+    return false;
+
+  // Use |instant_tab_| to run the query if we're already on a search results
+  // page. (NOTE: in particular, we do not send the query to NTPs.)
+  search_tab->Submit(search_terms, params);
+  instant_tab_->web_contents()->Focus();
+  EnsureSearchTermsAreSet(instant_tab_->web_contents(), search_terms);
+  return true;
 }
 
 void InstantController::SearchModeChanged(const SearchMode& old_mode,
                                           const SearchMode& new_mode) {
-  LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf(
+  LogDebugEvent(base::StringPrintf(
       "SearchModeChanged: [origin:mode] %d:%d to %d:%d", old_mode.origin,
       old_mode.mode, new_mode.origin, new_mode.mode));
 
@@ -96,7 +95,7 @@
 }
 
 void InstantController::ActiveTabChanged() {
-  LOG_INSTANT_DEBUG_EVENT(this, "ActiveTabChanged");
+  LogDebugEvent("ActiveTabChanged");
   ResetInstantTab();
 }
 
@@ -114,14 +113,6 @@
   debug_events_.clear();
 }
 
-Profile* InstantController::profile() const {
-  return browser_->profile();
-}
-
-InstantTab* InstantController::instant_tab() const {
-  return instant_tab_.get();
-}
-
 void InstantController::InstantSupportChanged(
     InstantSupportState instant_support) {
   // Handle INSTANT_SUPPORT_YES here because InstantTab is not hooked up to the
@@ -136,7 +127,7 @@
 void InstantController::InstantSupportDetermined(
     const content::WebContents* contents,
     bool supports_instant) {
-  DCHECK(IsContentsFrom(instant_tab(), contents));
+  DCHECK(IsContentsFrom(instant_tab_.get(), contents));
 
   if (!supports_instant)
     base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
@@ -151,7 +142,7 @@
 void InstantController::InstantTabAboutToNavigateMainFrame(
     const content::WebContents* contents,
     const GURL& url) {
-  DCHECK(IsContentsFrom(instant_tab(), contents));
+  DCHECK(IsContentsFrom(instant_tab_.get(), contents));
 
   // The Instant tab navigated.  Send it the data it needs to display
   // properly.
@@ -162,8 +153,8 @@
   if (!search_mode_.is_origin_default()) {
     content::WebContents* active_tab = browser_->GetActiveWebContents();
     if (!instant_tab_ || active_tab != instant_tab_->web_contents()) {
-      instant_tab_.reset(new InstantTab(this));
-      instant_tab_->Init(active_tab);
+      instant_tab_.reset(new InstantTab(this, active_tab));
+      instant_tab_->Init();
       UpdateInfoForInstantTab();
     }
   } else {
@@ -183,5 +174,5 @@
 }
 
 InstantService* InstantController::GetInstantService() const {
-  return InstantServiceFactory::GetForProfile(profile());
+  return InstantServiceFactory::GetForProfile(browser_->profile());
 }
diff --git a/chrome/browser/ui/search/instant_controller.h b/chrome/browser/ui/search/instant_controller.h
index 165152c..49522646 100644
--- a/chrome/browser/ui/search/instant_controller.h
+++ b/chrome/browser/ui/search/instant_controller.h
@@ -57,9 +57,9 @@
   // Instant search results page.
   void ActiveTabChanged();
 
-  // Adds a new event to |debug_events_| and also DVLOG's it. Ensures that
-  // |debug_events_| doesn't get too large.
-  void LogDebugEvent(const std::string& info) const;
+  // Used by BrowserInstantController to notify InstantController about the
+  // instant support change event for the active web contents.
+  void InstantSupportChanged(InstantSupportState instant_support);
 
   // Resets list of debug events.
   void ClearDebugEvents();
@@ -69,16 +69,6 @@
     return debug_events_;
   }
 
-  // Used by BrowserInstantController to notify InstantController about the
-  // instant support change event for the active web contents.
-  void InstantSupportChanged(InstantSupportState instant_support);
-
- protected:
-  // Accessors are made protected for testing purposes.
-  virtual InstantTab* instant_tab() const;
-
-  virtual Profile* profile() const;
-
  private:
   friend class InstantExtendedManualTest;
   friend class InstantTestBase;
@@ -111,6 +101,10 @@
   void InstantTabAboutToNavigateMainFrame(const content::WebContents* contents,
                                           const GURL& url) override;
 
+  // Adds a new event to |debug_events_| and also DVLOG's it. Ensures that
+  // |debug_events_| doesn't get too large.
+  void LogDebugEvent(const std::string& info) const;
+
   // If the active tab is an Instant search results page, sets |instant_tab_| to
   // point to it. Else, deletes any existing |instant_tab_|.
   void ResetInstantTab();
diff --git a/chrome/browser/ui/search/instant_tab.cc b/chrome/browser/ui/search/instant_tab.cc
index dea3f31..9e47432f 100644
--- a/chrome/browser/ui/search/instant_tab.cc
+++ b/chrome/browser/ui/search/instant_tab.cc
@@ -10,8 +10,10 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 
-InstantTab::Delegate::~Delegate() {
-}
+InstantTab::Delegate::~Delegate() {}
+
+InstantTab::InstantTab(Delegate* delegate, content::WebContents* web_contents)
+    : delegate_(delegate), pending_web_contents_(web_contents) {}
 
 InstantTab::~InstantTab() {
   if (web_contents()) {
@@ -20,27 +22,13 @@
   }
 }
 
-bool InstantTab::supports_instant() const {
-  return web_contents() &&
-      SearchTabHelper::FromWebContents(web_contents())->SupportsInstant();
-}
-
-bool InstantTab::IsLocal() const {
-  return web_contents() &&
-      web_contents()->GetURL() == GURL(chrome::kChromeSearchLocalNtpUrl);
-}
-
-InstantTab::InstantTab(Delegate* delegate)
-    : delegate_(delegate) {
-}
-
-void InstantTab::Init(content::WebContents* new_web_contents) {
-  ClearContents();
-
-  if (!new_web_contents)
+void InstantTab::Init() {
+  if (!pending_web_contents_)
     return;
 
-  Observe(new_web_contents);
+  Observe(pending_web_contents_);
+  pending_web_contents_ = nullptr;
+
   SearchModel* model =
       SearchTabHelper::FromWebContents(web_contents())->model();
   model->AddObserver(this);
@@ -54,13 +42,12 @@
     content::RenderFrameHost* render_frame_host,
     const GURL& url,
     ui::PageTransition /* transition_type */) {
-  if (!render_frame_host->GetParent()) {
+  if (!render_frame_host->GetParent())
     delegate_->InstantTabAboutToNavigateMainFrame(web_contents(), url);
-  }
 }
 
 void InstantTab::ModelChanged(const SearchModel::State& old_state,
-                               const SearchModel::State& new_state) {
+                              const SearchModel::State& new_state) {
   if (old_state.instant_support != new_state.instant_support)
     InstantSupportDetermined(new_state.instant_support == INSTANT_SUPPORT_YES);
 }
@@ -69,15 +56,12 @@
   delegate_->InstantSupportDetermined(web_contents(), supports_instant);
 
   // If the page doesn't support Instant, stop listening to it.
-  if (!supports_instant)
-    ClearContents();
-}
+  if (!supports_instant) {
+    if (web_contents()) {
+      SearchTabHelper::FromWebContents(web_contents())->model()->RemoveObserver(
+          this);
+    }
 
-void InstantTab::ClearContents() {
-  if (web_contents()) {
-    SearchTabHelper::FromWebContents(web_contents())->model()->RemoveObserver(
-        this);
+    Observe(nullptr);
   }
-
-  Observe(NULL);
 }
diff --git a/chrome/browser/ui/search/instant_tab.h b/chrome/browser/ui/search/instant_tab.h
index c77f21c..866fb5a5 100644
--- a/chrome/browser/ui/search/instant_tab.h
+++ b/chrome/browser/ui/search/instant_tab.h
@@ -22,7 +22,7 @@
 // InstantTab is used to exchange messages with a page that implements the
 // Instant/Embedded Search API (http://dev.chromium.org/embeddedsearch).
 class InstantTab : public content::WebContentsObserver,
-                    public SearchModelObserver {
+                   public SearchModelObserver {
  public:
   // InstantTab calls its delegate in response to messages received from the
   // page. Each method is called with the |contents| corresponding to the page
@@ -43,34 +43,15 @@
     virtual ~Delegate();
   };
 
-  explicit InstantTab(Delegate* delegate);
+  explicit InstantTab(Delegate* delegate, content::WebContents* web_contents);
 
   ~InstantTab() override;
 
-  // Sets |web_contents| as the page to communicate with. |web_contents| may be
-  // NULL, which effectively stops all communication.
-  void Init(content::WebContents* web_contents);
-
-  // Returns true if the page is known to support the Instant API. This starts
-  // out false, and is set to true whenever we get any message from the page.
-  // Once true, it never becomes false (the page isn't expected to drop API
-  // support suddenly).
-  bool supports_instant() const;
-
-  // Returns true if the page is the local NTP (i.e. its URL is
-  // chrome::kChromeSearchLocalNTPURL).
-  bool IsLocal() const;
+  // Sets up communication with the WebContents passed to the constructor. Does
+  // nothing if that WebContents was null. Should not be called more than once.
+  void Init();
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(InstantTabTest, IsLocal);
-  FRIEND_TEST_ALL_PREFIXES(InstantTabTest,
-                           DetermineIfPageSupportsInstant_Local);
-  FRIEND_TEST_ALL_PREFIXES(InstantTabTest,
-                           DetermineIfPageSupportsInstant_NonLocal);
-  FRIEND_TEST_ALL_PREFIXES(InstantTabTest,
-                           PageURLDoesntBelongToInstantRenderer);
-  FRIEND_TEST_ALL_PREFIXES(InstantTabTest, PageSupportsInstant);
-
   // Overridden from content::WebContentsObserver:
   void DidCommitProvisionalLoadForFrame(
       content::RenderFrameHost* render_frame_host,
@@ -81,13 +62,12 @@
   void ModelChanged(const SearchModel::State& old_state,
                     const SearchModel::State& new_state) override;
 
-  // Update the status of Instant support.
   void InstantSupportDetermined(bool supports_instant);
 
-  void ClearContents();
-
   Delegate* const delegate_;
 
+  content::WebContents* pending_web_contents_;
+
   DISALLOW_COPY_AND_ASSIGN(InstantTab);
 };
 
diff --git a/chrome/browser/ui/search/instant_tab_unittest.cc b/chrome/browser/ui/search/instant_tab_unittest.cc
index 97d92e0..06c0b3b 100644
--- a/chrome/browser/ui/search/instant_tab_unittest.cc
+++ b/chrome/browser/ui/search/instant_tab_unittest.cc
@@ -35,17 +35,9 @@
   MOCK_METHOD2(InstantSupportDetermined,
                void(const content::WebContents* contents,
                     bool supports_instant));
-  MOCK_METHOD1(InstantTabRenderProcessGone,
-               void(const content::WebContents* contents));
   MOCK_METHOD2(InstantTabAboutToNavigateMainFrame,
                void(const content::WebContents* contents,
                     const GURL& url));
-  MOCK_METHOD5(NavigateToURL,
-               void(const content::WebContents* contents,
-                    const GURL& url,
-                    ui::PageTransition transition,
-                    WindowOpenDisposition disposition,
-                    bool is_search_type));
 };
 
 }  // namespace
@@ -54,8 +46,8 @@
  public:
   void SetUp() override;
 
-  bool MessageWasSent(uint32_t id) {
-    return process()->sink().GetFirstMessageMatching(id) != NULL;
+  SearchTabHelper* search_tab() {
+    return SearchTabHelper::FromWebContents(web_contents());
   }
 
   std::unique_ptr<InstantTab> page;
@@ -67,36 +59,22 @@
   SearchTabHelper::CreateForWebContents(web_contents());
 }
 
-TEST_F(InstantTabTest, IsLocal) {
-  page.reset(new InstantTab(&delegate));
-  EXPECT_FALSE(page->supports_instant());
-  EXPECT_FALSE(page->IsLocal());
-  page->Init(web_contents());
-  NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
-  EXPECT_TRUE(page->IsLocal());
-  NavigateAndCommit(GURL("http://example.com"));
-  EXPECT_FALSE(page->IsLocal());
-}
-
 TEST_F(InstantTabTest, DetermineIfPageSupportsInstant_Local) {
-  page.reset(new InstantTab(&delegate));
-  EXPECT_FALSE(page->supports_instant());
-  page->Init(web_contents());
+  page.reset(new InstantTab(&delegate, web_contents()));
+  EXPECT_FALSE(search_tab()->SupportsInstant());
+  page->Init();
   NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
-  EXPECT_TRUE(page->IsLocal());
-  EXPECT_CALL(delegate, InstantSupportDetermined(web_contents(), true))
-      .Times(1);
+  EXPECT_CALL(delegate, InstantSupportDetermined(web_contents(), true));
   SearchTabHelper::FromWebContents(web_contents())->
       DetermineIfPageSupportsInstant();
-  EXPECT_TRUE(page->supports_instant());
+  EXPECT_TRUE(search_tab()->SupportsInstant());
 }
 
 TEST_F(InstantTabTest, DetermineIfPageSupportsInstant_NonLocal) {
-  page.reset(new InstantTab(&delegate));
-  EXPECT_FALSE(page->supports_instant());
-  page->Init(web_contents());
+  page.reset(new InstantTab(&delegate, web_contents()));
+  EXPECT_FALSE(search_tab()->SupportsInstant());
+  page->Init();
   NavigateAndCommit(GURL("chrome-search://foo/bar"));
-  EXPECT_FALSE(page->IsLocal());
   process()->sink().ClearMessages();
   SearchTabHelper::FromWebContents(web_contents())->
       DetermineIfPageSupportsInstant();
@@ -107,34 +85,32 @@
 }
 
 TEST_F(InstantTabTest, PageURLDoesntBelongToInstantRenderer) {
-  page.reset(new InstantTab(&delegate));
-  EXPECT_FALSE(page->supports_instant());
+  page.reset(new InstantTab(&delegate, web_contents()));
+  EXPECT_FALSE(search_tab()->SupportsInstant());
   NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
-  page->Init(web_contents());
+  page->Init();
 
   // Navigate to a page URL that doesn't belong to Instant renderer.
   // SearchTabHelper::DeterminerIfPageSupportsInstant() should return
   // immediately without dispatching any message to the renderer.
   NavigateAndCommit(GURL("http://www.example.com"));
-  EXPECT_FALSE(page->IsLocal());
   process()->sink().ClearMessages();
-  EXPECT_CALL(delegate, InstantSupportDetermined(web_contents(), false))
-      .Times(1);
+  EXPECT_CALL(delegate, InstantSupportDetermined(web_contents(), false));
 
   SearchTabHelper::FromWebContents(web_contents())->
       DetermineIfPageSupportsInstant();
   const IPC::Message* message = process()->sink().GetFirstMessageMatching(
       ChromeViewMsg_DetermineIfPageSupportsInstant::ID);
   ASSERT_TRUE(message == NULL);
-  EXPECT_FALSE(page->supports_instant());
+  EXPECT_FALSE(search_tab()->SupportsInstant());
 }
 
 // Test to verify that ChromeViewMsg_DetermineIfPageSupportsInstant message
 // reply handler updates the instant support state in InstantTab.
 TEST_F(InstantTabTest, PageSupportsInstant) {
-  page.reset(new InstantTab(&delegate));
-  EXPECT_FALSE(page->supports_instant());
-  page->Init(web_contents());
+  page.reset(new InstantTab(&delegate, web_contents()));
+  EXPECT_FALSE(search_tab()->SupportsInstant());
+  page->Init();
   NavigateAndCommit(GURL("chrome-search://foo/bar"));
   process()->sink().ClearMessages();
   SearchTabHelper::FromWebContents(web_contents())->
@@ -144,8 +120,7 @@
   ASSERT_TRUE(message != NULL);
   EXPECT_EQ(web_contents()->GetRoutingID(), message->routing_id());
 
-  EXPECT_CALL(delegate, InstantSupportDetermined(web_contents(), true))
-      .Times(1);
+  EXPECT_CALL(delegate, InstantSupportDetermined(web_contents(), true));
 
   // Assume the page supports instant. Invoke the message reply handler to make
   // sure the InstantTab is notified about the instant support state.
@@ -153,5 +128,5 @@
       web_contents()->GetController().GetLastCommittedEntry();
   EXPECT_TRUE(entry);
   SearchTabHelper::FromWebContents(web_contents())->InstantSupportChanged(true);
-  EXPECT_TRUE(page->supports_instant());
+  EXPECT_TRUE(search_tab()->SupportsInstant());
 }
diff --git a/chrome/browser/ui/search/search_ipc_router.cc b/chrome/browser/ui/search/search_ipc_router.cc
index 8ad4e0f9..59fc198 100644
--- a/chrome/browser/ui/search/search_ipc_router.cc
+++ b/chrome/browser/ui/search/search_ipc_router.cc
@@ -54,14 +54,6 @@
   Send(new ChromeViewMsg_HistorySyncCheckResult(routing_id(), sync_history));
 }
 
-void SearchIPCRouter::SetPromoInformation(bool is_app_launcher_enabled) {
-  if (!policy_->ShouldSendSetPromoInformation())
-    return;
-
-  Send(new ChromeViewMsg_SearchBoxPromoInformation(routing_id(),
-                                                   is_app_launcher_enabled));
-}
-
 void SearchIPCRouter::SetDisplayInstantResults() {
   if (!policy_->ShouldSendSetDisplayInstantResults())
     return;
diff --git a/chrome/browser/ui/search/search_ipc_router.h b/chrome/browser/ui/search/search_ipc_router.h
index 462ac45..971b5df5 100644
--- a/chrome/browser/ui/search/search_ipc_router.h
+++ b/chrome/browser/ui/search/search_ipc_router.h
@@ -100,7 +100,6 @@
     virtual bool ShouldProcessPasteIntoOmnibox(bool is_active_tab) = 0;
     virtual bool ShouldProcessChromeIdentityCheck() = 0;
     virtual bool ShouldProcessHistorySyncCheck() = 0;
-    virtual bool ShouldSendSetPromoInformation() = 0;
     virtual bool ShouldSendSetDisplayInstantResults() = 0;
     virtual bool ShouldSendSetSuggestionToPrefetch() = 0;
     virtual bool ShouldSendSetInputInProgress(bool is_active_tab) = 0;
@@ -130,9 +129,6 @@
   // Tells the renderer whether the user syncs history.
   void SendHistorySyncCheckResult(bool sync_history);
 
-  // Tells the renderer information it needs to display promos.
-  void SetPromoInformation(bool is_app_launcher_enabled);
-
   // Tells the renderer whether to display the Instant results.
   void SetDisplayInstantResults();
 
diff --git a/chrome/browser/ui/search/search_ipc_router_policy_impl.cc b/chrome/browser/ui/search/search_ipc_router_policy_impl.cc
index ed5cd2e..c2b75a56 100644
--- a/chrome/browser/ui/search/search_ipc_router_policy_impl.cc
+++ b/chrome/browser/ui/search/search_ipc_router_policy_impl.cc
@@ -55,10 +55,6 @@
   return !is_incognito_ && search::IsInstantNTP(web_contents_);
 }
 
-bool SearchIPCRouterPolicyImpl::ShouldSendSetPromoInformation() {
-  return !is_incognito_ && search::IsInstantNTP(web_contents_);
-}
-
 bool SearchIPCRouterPolicyImpl::ShouldSendSetDisplayInstantResults() {
   return !is_incognito_;
 }
diff --git a/chrome/browser/ui/search/search_ipc_router_policy_impl.h b/chrome/browser/ui/search/search_ipc_router_policy_impl.h
index 08bd8764..4c4a4e78 100644
--- a/chrome/browser/ui/search/search_ipc_router_policy_impl.h
+++ b/chrome/browser/ui/search/search_ipc_router_policy_impl.h
@@ -30,7 +30,6 @@
   bool ShouldProcessPasteIntoOmnibox(bool is_active_tab) override;
   bool ShouldProcessChromeIdentityCheck() override;
   bool ShouldProcessHistorySyncCheck() override;
-  bool ShouldSendSetPromoInformation() override;
   bool ShouldSendSetDisplayInstantResults() override;
   bool ShouldSendSetSuggestionToPrefetch() override;
   bool ShouldSendSetInputInProgress(bool is_active_tab) override;
diff --git a/chrome/browser/ui/search/search_ipc_router_policy_unittest.cc b/chrome/browser/ui/search/search_ipc_router_policy_unittest.cc
index 5572ab7..a4bbf234 100644
--- a/chrome/browser/ui/search/search_ipc_router_policy_unittest.cc
+++ b/chrome/browser/ui/search/search_ipc_router_policy_unittest.cc
@@ -52,17 +52,6 @@
   EXPECT_FALSE(GetSearchIPCRouterPolicy()->ShouldProcessFocusOmnibox(true));
 }
 
-TEST_F(SearchIPCRouterPolicyTest, SendSetPromoInformation) {
-  NavigateAndCommitActiveTab(GURL(chrome::kChromeSearchLocalNtpUrl));
-  EXPECT_TRUE(GetSearchIPCRouterPolicy()->ShouldSendSetPromoInformation());
-}
-
-TEST_F(SearchIPCRouterPolicyTest, DoNotSendSetPromoInformation) {
-  // Send promo information only if the underlying page is an InstantNTP.
-  NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
-  EXPECT_FALSE(GetSearchIPCRouterPolicy()->ShouldSendSetPromoInformation());
-}
-
 TEST_F(SearchIPCRouterPolicyTest, ProcessDeleteMostVisitedItem) {
   NavigateAndCommitActiveTab(GURL(chrome::kChromeSearchLocalNtpUrl));
   EXPECT_TRUE(GetSearchIPCRouterPolicy()->ShouldProcessDeleteMostVisitedItem());
@@ -165,7 +154,6 @@
   SearchIPCRouter::Policy* router_policy = GetSearchIPCRouterPolicy();
   EXPECT_FALSE(router_policy->ShouldSendSetSuggestionToPrefetch());
   EXPECT_FALSE(router_policy->ShouldSendSetDisplayInstantResults());
-  EXPECT_FALSE(router_policy->ShouldSendSetPromoInformation());
   EXPECT_FALSE(router_policy->ShouldSendThemeBackgroundInfo());
   EXPECT_FALSE(router_policy->ShouldSendMostVisitedItems());
   EXPECT_FALSE(router_policy->ShouldSendSetInputInProgress(true));
diff --git a/chrome/browser/ui/search/search_ipc_router_unittest.cc b/chrome/browser/ui/search/search_ipc_router_unittest.cc
index 38ee413..b7a16ae 100644
--- a/chrome/browser/ui/search/search_ipc_router_unittest.cc
+++ b/chrome/browser/ui/search/search_ipc_router_unittest.cc
@@ -76,7 +76,6 @@
   MOCK_METHOD1(ShouldProcessPasteIntoOmnibox, bool(bool));
   MOCK_METHOD0(ShouldProcessChromeIdentityCheck, bool());
   MOCK_METHOD0(ShouldProcessHistorySyncCheck, bool());
-  MOCK_METHOD0(ShouldSendSetPromoInformation, bool());
   MOCK_METHOD0(ShouldSendSetDisplayInstantResults, bool());
   MOCK_METHOD0(ShouldSendSetSuggestionToPrefetch, bool());
   MOCK_METHOD1(ShouldSendSetInputInProgress, bool(bool));
@@ -554,28 +553,6 @@
       contents->GetRoutingID(), GetSearchIPCRouterSeqNo(), text));
 }
 
-TEST_F(SearchIPCRouterTest, SendSetPromoInformationMsg) {
-  NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
-  SetupMockDelegateAndPolicy();
-  MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
-  EXPECT_CALL(*policy, ShouldSendSetPromoInformation()).Times(1)
-      .WillOnce(testing::Return(true));
-
-  GetSearchIPCRouter().SetPromoInformation(true);
-  EXPECT_TRUE(MessageWasSent(ChromeViewMsg_SearchBoxPromoInformation::ID));
-}
-
-TEST_F(SearchIPCRouterTest, DoNotSendSetPromoInformationMsg) {
-  NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
-  SetupMockDelegateAndPolicy();
-  MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
-  EXPECT_CALL(*policy, ShouldSendSetPromoInformation()).Times(1)
-      .WillOnce(testing::Return(false));
-
-  GetSearchIPCRouter().SetPromoInformation(false);
-  EXPECT_FALSE(MessageWasSent(ChromeViewMsg_SearchBoxPromoInformation::ID));
-}
-
 TEST_F(SearchIPCRouterTest,
        SendSetDisplayInstantResultsMsg_EnableInstantOnResultsPage) {
   ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc
index 01e6a7e..c0890f5df 100644
--- a/chrome/browser/ui/search/search_tab_helper.cc
+++ b/chrome/browser/ui/search/search_tab_helper.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/search/search.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
-#include "chrome/browser/ui/app_list/app_list_util.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/browser/ui/omnibox/clipboard_utils.h"
@@ -264,11 +263,6 @@
   return model_.mode().is_origin_search();
 }
 
-void SearchTabHelper::RenderViewCreated(
-    content::RenderViewHost* render_view_host) {
-  ipc_router_.SetPromoInformation(IsAppLauncherEnabled());
-}
-
 void SearchTabHelper::DidStartNavigationToPendingEntry(
     const GURL& url,
     content::NavigationController::ReloadType /* reload_type */) {
diff --git a/chrome/browser/ui/search/search_tab_helper.h b/chrome/browser/ui/search/search_tab_helper.h
index 164bd76..9b27ab04 100644
--- a/chrome/browser/ui/search/search_tab_helper.h
+++ b/chrome/browser/ui/search/search_tab_helper.h
@@ -139,7 +139,6 @@
   explicit SearchTabHelper(content::WebContents* web_contents);
 
   // Overridden from contents::WebContentsObserver:
-  void RenderViewCreated(content::RenderViewHost* render_view_host) override;
   void DidStartNavigationToPendingEntry(
       const GURL& url,
       content::NavigationController::ReloadType reload_type) override;
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc
index a2e406b..9cd8b76 100644
--- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc
+++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc
@@ -21,8 +21,8 @@
 #include "ui/views/widget/widget.h"
 
 #if defined(USE_ASH)
-#include "ash/shelf/shelf_delegate.h"
-#include "ash/shell.h"
+#include "ash/common/shelf/shelf_delegate.h"
+#include "ash/common/wm_shell.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_util.h"
 #endif
 
@@ -90,7 +90,7 @@
 #if defined(USE_ASH)
   if (pin_to_shelf_button_ && unpin_from_shelf_button_) {
     bool is_pinned =
-        !ash::Shell::GetInstance()->GetShelfDelegate()->IsAppPinned(app_->id());
+        !ash::WmShell::Get()->shelf_delegate()->IsAppPinned(app_->id());
     pin_to_shelf_button_->SetVisible(is_pinned);
     unpin_from_shelf_button_->SetVisible(!is_pinned);
 
@@ -152,8 +152,7 @@
 #if defined(USE_ASH)
 void AppInfoFooterPanel::SetPinnedToShelf(bool value) {
   DCHECK(CanSetPinnedToShelf());
-  ash::ShelfDelegate* shelf_delegate =
-      ash::Shell::GetInstance()->GetShelfDelegate();
+  ash::ShelfDelegate* shelf_delegate = ash::WmShell::Get()->shelf_delegate();
   DCHECK(shelf_delegate);
   if (value)
     shelf_delegate->PinAppWithID(app_->id());
@@ -166,7 +165,7 @@
 
 bool AppInfoFooterPanel::CanSetPinnedToShelf() const {
   // Non-Ash platforms don't have a shelf.
-  if (!ash::Shell::HasInstance())
+  if (!ash::WmShell::HasInstance())
     return false;
 
   // The Chrome app can't be unpinned, and extensions can't be pinned.
diff --git a/chrome/browser/ui/webui/snippets_internals_message_handler.cc b/chrome/browser/ui/webui/snippets_internals_message_handler.cc
index 640488d..fe11f4f 100644
--- a/chrome/browser/ui/webui/snippets_internals_message_handler.cc
+++ b/chrome/browser/ui/webui/snippets_internals_message_handler.cc
@@ -35,7 +35,7 @@
 std::unique_ptr<base::DictionaryValue> PrepareSnippet(
     const ntp_snippets::NTPSnippet& snippet,
     int index,
-    bool discarded) {
+    bool dismissed) {
   std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue);
   entry->SetString("snippetId", snippet.id());
   entry->SetString("title", snippet.title());
@@ -50,8 +50,8 @@
   entry->SetString("salientImageUrl", snippet.salient_image_url().spec());
   entry->SetDouble("score", snippet.score());
 
-  if (discarded)
-    entry->SetString("id", "discarded-snippet-" + base::IntToString(index));
+  if (dismissed)
+    entry->SetString("id", "dismissed-snippet-" + base::IntToString(index));
   else
     entry->SetString("id", "snippet-" + base::IntToString(index));
 
@@ -183,8 +183,8 @@
                              base::Unretained(this)));
 
   web_ui()->RegisterMessageCallback(
-      "clearDiscarded",
-      base::Bind(&SnippetsInternalsMessageHandler::HandleClearDiscarded,
+      "clearDismissed",
+      base::Bind(&SnippetsInternalsMessageHandler::HandleClearDismissed,
                  base::Unretained(this)));
 
   web_ui()->RegisterMessageCallback(
@@ -193,9 +193,9 @@
                  base::Unretained(this)));
 
   web_ui()->RegisterMessageCallback(
-      "clearDiscardedSuggestions",
+      "clearDismissedSuggestions",
       base::Bind(
-          &SnippetsInternalsMessageHandler::HandleClearDiscardedSuggestions,
+          &SnippetsInternalsMessageHandler::HandleClearDismissedSuggestions,
           base::Unretained(this)));
 }
 
@@ -214,12 +214,12 @@
   ntp_snippets_service_->ClearCachedSuggestionsForDebugging();
 }
 
-void SnippetsInternalsMessageHandler::HandleClearDiscarded(
+void SnippetsInternalsMessageHandler::HandleClearDismissed(
     const base::ListValue* args) {
   DCHECK_EQ(0u, args->GetSize());
 
-  ntp_snippets_service_->ClearDiscardedSuggestionsForDebugging();
-  SendDiscardedSnippets();
+  ntp_snippets_service_->ClearDismissedSuggestionsForDebugging();
+  SendDismissedSnippets();
 }
 
 void SnippetsInternalsMessageHandler::HandleDownload(
@@ -245,11 +245,11 @@
   content_suggestions_service_->ClearCachedSuggestionsForDebugging();
 }
 
-void SnippetsInternalsMessageHandler::HandleClearDiscardedSuggestions(
+void SnippetsInternalsMessageHandler::HandleClearDismissedSuggestions(
     const base::ListValue* args) {
   DCHECK_EQ(0u, args->GetSize());
 
-  content_suggestions_service_->ClearDiscardedSuggestionsForDebugging();
+  content_suggestions_service_->ClearDismissedSuggestionsForDebugging();
 }
 
 void SnippetsInternalsMessageHandler::SendAllContent() {
@@ -284,7 +284,7 @@
              ntp_snippets_service_->snippets_fetcher()->fetch_url().spec());
 
   SendSnippets();
-  SendDiscardedSnippets();
+  SendDismissedSnippets();
   SendContentSuggestions();
 }
 
@@ -307,17 +307,17 @@
     SendString("hosts-status", "Finished: " + status);
 }
 
-void SnippetsInternalsMessageHandler::SendDiscardedSnippets() {
+void SnippetsInternalsMessageHandler::SendDismissedSnippets() {
   std::unique_ptr<base::ListValue> snippets_list(new base::ListValue);
 
   int index = 0;
-  for (const auto& snippet : ntp_snippets_service_->discarded_snippets())
+  for (const auto& snippet : ntp_snippets_service_->dismissed_snippets())
     snippets_list->Append(PrepareSnippet(*snippet, index++, true));
 
   base::DictionaryValue result;
   result.Set("list", std::move(snippets_list));
   web_ui()->CallJavascriptFunctionUnsafe(
-      "chrome.SnippetsInternals.receiveDiscardedSnippets", result);
+      "chrome.SnippetsInternals.receiveDismissedSnippets", result);
 }
 
 void SnippetsInternalsMessageHandler::SendHosts() {
diff --git a/chrome/browser/ui/webui/snippets_internals_message_handler.h b/chrome/browser/ui/webui/snippets_internals_message_handler.h
index f633ec63..f53199c 100644
--- a/chrome/browser/ui/webui/snippets_internals_message_handler.h
+++ b/chrome/browser/ui/webui/snippets_internals_message_handler.h
@@ -47,14 +47,14 @@
 
   void HandleRefreshContent(const base::ListValue* args);
   void HandleClear(const base::ListValue* args);
-  void HandleClearDiscarded(const base::ListValue* args);
+  void HandleClearDismissed(const base::ListValue* args);
   void HandleDownload(const base::ListValue* args);
   void HandleClearCachedSuggestions(const base::ListValue* args);
-  void HandleClearDiscardedSuggestions(const base::ListValue* args);
+  void HandleClearDismissedSuggestions(const base::ListValue* args);
 
   void SendAllContent();
   void SendSnippets();
-  void SendDiscardedSnippets();
+  void SendDismissedSnippets();
   void SendHosts();
   void SendContentSuggestions();
   void SendBoolean(const std::string& name, bool value);
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 083752f..ac960038 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -61,8 +61,6 @@
       'browser/browser_about_handler.h',
       'browser/browser_shutdown.cc',
       'browser/browser_shutdown.h',
-      'browser/browsing_data/autofill_counter.cc',
-      'browser/browsing_data/autofill_counter.h',
       'browser/browsing_data/browsing_data_appcache_helper.cc',
       'browser/browsing_data/browsing_data_appcache_helper.h',
       'browser/browsing_data/browsing_data_cache_storage_helper.cc',
@@ -105,16 +103,12 @@
       'browser/browsing_data/cookies_tree_model.h',
       'browser/browsing_data/downloads_counter.cc',
       'browser/browsing_data/downloads_counter.h',
-      'browser/browsing_data/history_counter.cc',
-      'browser/browsing_data/history_counter.h',
       'browser/browsing_data/local_data_container.cc',
       'browser/browsing_data/local_data_container.h',
       'browser/browsing_data/media_licenses_counter.cc',
       'browser/browsing_data/media_licenses_counter.h',
       'browser/browsing_data/origin_filter_builder.cc',
       'browser/browsing_data/origin_filter_builder.h',
-      'browser/browsing_data/passwords_counter.cc',
-      'browser/browsing_data/passwords_counter.h',
       'browser/browsing_data/registrable_domain_filter_builder.cc',
       'browser/browsing_data/site_data_size_collector.cc',
       'browser/browsing_data/site_data_size_collector.h',
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi
index 23bd2a7c..fddaa7b5 100644
--- a/chrome/chrome_common.gypi
+++ b/chrome/chrome_common.gypi
@@ -410,6 +410,7 @@
             '<(DEPTH)/components/components.gyp:password_manager_content_mojo_bindings',
             '<(DEPTH)/components/components.gyp:password_manager_core_common',
             '<(DEPTH)/components/components.gyp:signin_core_common',
+            '<(DEPTH)/components/components.gyp:spellcheck_common',
             '<(DEPTH)/components/components.gyp:translate_content_common',
             '<(DEPTH)/components/components.gyp:visitedlink_common',
             '<(DEPTH)/extensions/extensions.gyp:extensions_common_constants',
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 99ba806..4940149a 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -997,32 +997,6 @@
 // resulted in a browser startup.
 const char kWinJumplistAction[]             = "win-jumplist-action";
 
-#if defined(ENABLE_SPELLCHECK)
-#if defined(OS_ANDROID)
-// Enables use of the Android spellchecker.
-const char kEnableAndroidSpellChecker[] = "enable-android-spellchecker";
-#endif  // defined(OS_ANDROID)
-
-// Enables participation in the field trial for user feedback to spelling
-// service.
-const char kEnableSpellingFeedbackFieldTrial[] =
-    "enable-spelling-feedback-field-trial";
-
-// Specifies the number of seconds between sending batches of feedback to
-// spelling service. The default is 30 minutes. The minimum is 5 seconds. This
-// switch is for temporary testing only.
-// TODO(rouslan): Remove this flag when feedback testing is complete. Revisit by
-// August 2013.
-const char kSpellingServiceFeedbackIntervalSeconds[] =
-    "spelling-service-feedback-interval-seconds";
-
-// Specifies the URL where spelling service feedback data will be sent instead
-// of the default URL. This switch is for temporary testing only.
-// TODO(rouslan): Remove this flag when feedback testing is complete. Revisit by
-// August 2013.
-const char kSpellingServiceFeedbackUrl[] = "spelling-service-feedback-url";
-#endif  // defined(ENABLE_SPELLCHECK)
-
 #if defined(GOOGLE_CHROME_BUILD)
 // Shows a Google icon next to context menu items powered by Google services.
 const char kEnableGoogleBrandedContextMenu[] =
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 04b1f40..55b53114 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -266,15 +266,6 @@
 extern const char kWinHttpProxyResolver[];
 extern const char kWinJumplistAction[];
 
-#if defined(ENABLE_SPELLCHECK)
-#if defined(OS_ANDROID)
-extern const char kEnableAndroidSpellChecker[];
-#endif  // defined(OS_ANDROID)
-extern const char kEnableSpellingFeedbackFieldTrial[];
-extern const char kSpellingServiceFeedbackIntervalSeconds[];
-extern const char kSpellingServiceFeedbackUrl[];
-#endif  // defined(ENABLE_SPELLCHECK)
-
 #if defined(GOOGLE_CHROME_BUILD)
 extern const char kEnableGoogleBrandedContextMenu[];
 #endif  // defined(GOOGLE_CHROME_BUILD)
diff --git a/chrome/common/extensions/api/networking_private/networking_private_crypto.cc b/chrome/common/extensions/api/networking_private/networking_private_crypto.cc
index 289899c..ea6c301 100644
--- a/chrome/common/extensions/api/networking_private/networking_private_crypto.cc
+++ b/chrome/common/extensions/api/networking_private/networking_private_crypto.cc
@@ -51,8 +51,7 @@
     const std::string& signature,
     const std::string& data,
     const std::string& connected_mac) {
-  base::Time::Exploded now;
-  base::Time::Now().UTCExplode(&now);
+  base::Time now = base::Time::Now();
   return VerifyCredentialsAtTime(certificate, intermediate_certificates,
                                  signature, data, connected_mac, now);
 }
@@ -63,7 +62,7 @@
     const std::string& signature,
     const std::string& data,
     const std::string& connected_mac,
-    const base::Time::Exploded& time) {
+    const base::Time& time) {
   static const char kErrorPrefix[] = "Device verification failed. ";
 
   std::vector<std::string> headers;
@@ -97,7 +96,8 @@
 
   std::unique_ptr<cast_crypto::CertVerificationContext> verification_context;
   if (!cast_crypto::VerifyDeviceCert(certs, time, &verification_context,
-                                     &unused_policy)) {
+                                     &unused_policy, nullptr,
+                                     cast_crypto::CRLPolicy::CRL_OPTIONAL)) {
     LOG(ERROR) << kErrorPrefix << "Failed verifying cast device cert";
     return false;
   }
diff --git a/chrome/common/extensions/api/networking_private/networking_private_crypto.h b/chrome/common/extensions/api/networking_private/networking_private_crypto.h
index c29241ea..fd1475b3 100644
--- a/chrome/common/extensions/api/networking_private/networking_private_crypto.h
+++ b/chrome/common/extensions/api/networking_private/networking_private_crypto.h
@@ -35,7 +35,7 @@
     const std::string& signature,
     const std::string& data,
     const std::string& connected_mac,
-    const base::Time::Exploded& time);
+    const base::Time& time);
 
 // Encrypt |data| with |public_key|. |public_key| is a DER-encoded
 // RSAPublicKey. |data| is some string of bytes that is smaller than the
diff --git a/chrome/common/extensions/api/networking_private/networking_private_crypto_unittest.cc b/chrome/common/extensions/api/networking_private/networking_private_crypto_unittest.cc
index b57bbd0..ac2c981 100644
--- a/chrome/common/extensions/api/networking_private/networking_private_crypto_unittest.cc
+++ b/chrome/common/extensions/api/networking_private/networking_private_crypto_unittest.cc
@@ -118,16 +118,21 @@
   static const char kBadHotspotBssid[] = "bad bssid";
 
   // April 1, 2016
-  base::Time::Exploded time = {0};
-  time.year = 2016;
-  time.month = 4;
-  time.day_of_month = 1;
+  base::Time::Exploded time_exploded = {0};
+  time_exploded.year = 2016;
+  time_exploded.month = 4;
+  time_exploded.day_of_month = 1;
+  base::Time time;
+  ASSERT_TRUE(base::Time::FromUTCExploded(time_exploded, &time));
 
   // September 1, 2035
-  base::Time::Exploded expired_time = {0};
-  expired_time.year = 2035;
-  expired_time.month = 9;
-  expired_time.day_of_month = 1;
+  base::Time::Exploded expired_time_exploded = {0};
+  expired_time_exploded.year = 2035;
+  expired_time_exploded.month = 9;
+  expired_time_exploded.day_of_month = 1;
+  base::Time expired_time;
+  ASSERT_TRUE(
+      base::Time::FromUTCExploded(expired_time_exploded, &expired_time));
 
   std::string unsigned_data = std::string(std::begin(kData), std::end(kData));
   std::string signed_data =
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h
index 6ff500ef..333da21 100644
--- a/chrome/common/render_messages.h
+++ b/chrome/common/render_messages.h
@@ -223,9 +223,6 @@
 IPC_MESSAGE_ROUTED1(ChromeViewMsg_SearchBoxMostVisitedItemsChanged,
                     std::vector<InstantMostVisitedItem> /* items */)
 
-IPC_MESSAGE_ROUTED1(ChromeViewMsg_SearchBoxPromoInformation,
-                    bool /* is_app_launcher_enabled */)
-
 IPC_MESSAGE_ROUTED1(ChromeViewMsg_SearchBoxSetInputInProgress,
                     bool /* input_in_progress */)
 
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index 54e5eba..7f77703 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -156,6 +156,8 @@
   if (enable_spellcheck) {
     sources +=
         rebase_path(gypi_values.chrome_renderer_spellchecker_sources, ".", "..")
+    deps += [ "//components/spellcheck/common:common" ]
+
     if (!is_android) {
       deps += [ "//third_party/hunspell" ]
     }
diff --git a/chrome/renderer/DEPS b/chrome/renderer/DEPS
index 076b9fa..793869b 100644
--- a/chrome/renderer/DEPS
+++ b/chrome/renderer/DEPS
@@ -27,6 +27,7 @@
   "+components/printing/common",
   "+components/printing/renderer",
   "+components/signin/core/common",
+  "+components/spellcheck/common",
   "+components/startup_metric_utils/common",
   "+components/strings/grit",  # For generated headers
   "+components/subresource_filter/content/renderer",
diff --git a/chrome/renderer/resources/extensions/searchbox_api.js b/chrome/renderer/resources/extensions/searchbox_api.js
index cbbd2e38..04ef8a09 100644
--- a/chrome/renderer/resources/extensions/searchbox_api.js
+++ b/chrome/renderer/resources/extensions/searchbox_api.js
@@ -78,9 +78,6 @@
       this.onkeycapturechange = null;
       this.onsubmit = null;
       this.onsuggestionchange = null;
-
-      //TODO(jered): Remove this empty method when google no longer requires it.
-      this.setRestrictedValue = function() {};
     };
 
     this.newTabPage = new function() {
@@ -91,7 +88,6 @@
       native function CheckIsUserSignedInToChromeAs();
       native function CheckIsUserSyncingHistory();
       native function DeleteMostVisitedItem();
-      native function GetAppLauncherEnabled();
       native function GetMostVisitedItems();
       native function GetThemeBackgroundInfo();
       native function IsInputInProgress();
@@ -128,7 +124,6 @@
       // =======================================================================
       //                           Exported functions
       // =======================================================================
-      this.__defineGetter__('appLauncherEnabled', GetAppLauncherEnabled);
       this.__defineGetter__('isInputInProgress', IsInputInProgress);
       this.__defineGetter__('mostVisited', GetMostVisitedItemsWrapper);
       this.__defineGetter__('themeBackgroundInfo', GetThemeBackgroundInfo);
diff --git a/chrome/renderer/searchbox/searchbox.cc b/chrome/renderer/searchbox/searchbox.cc
index 5e637457..a4f64a0a 100644
--- a/chrome/renderer/searchbox/searchbox.cc
+++ b/chrome/renderer/searchbox/searchbox.cc
@@ -235,7 +235,6 @@
     : content::RenderViewObserver(render_view),
       content::RenderViewObserverTracker<SearchBox>(render_view),
     page_seq_no_(0),
-    app_launcher_enabled_(false),
     is_focused_(false),
     is_input_in_progress_(false),
     is_key_capture_enabled_(false),
@@ -372,8 +371,6 @@
     IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxFocusChanged, OnFocusChanged)
     IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxMostVisitedItemsChanged,
                         OnMostVisitedChanged)
-    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxPromoInformation,
-                        OnPromoInformationReceived)
     IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetDisplayInstantResults,
                         OnSetDisplayInstantResults)
     IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetInputInProgress,
@@ -465,10 +462,6 @@
   }
 }
 
-void SearchBox::OnPromoInformationReceived(bool is_app_launcher_enabled) {
-  app_launcher_enabled_ = is_app_launcher_enabled;
-}
-
 void SearchBox::OnSetDisplayInstantResults(bool display_instant_results) {
   display_instant_results_ = display_instant_results;
 }
diff --git a/chrome/renderer/searchbox/searchbox.h b/chrome/renderer/searchbox/searchbox.h
index 528a034..4b908a0b 100644
--- a/chrome/renderer/searchbox/searchbox.h
+++ b/chrome/renderer/searchbox/searchbox.h
@@ -129,7 +129,6 @@
   // Sends ChromeViewHostMsg_SearchBoxUndoMostVisitedDeletion to the browser.
   void UndoMostVisitedDeletion(InstantRestrictedID most_visited_item_id);
 
-  bool app_launcher_enabled() const { return app_launcher_enabled_; }
   bool is_focused() const { return is_focused_; }
   bool is_input_in_progress() const { return is_input_in_progress_; }
   bool is_key_capture_enabled() const { return is_key_capture_enabled_; }
@@ -151,7 +150,6 @@
   void OnHistorySyncCheckResult(bool sync_history);
   void OnMostVisitedChanged(
       const std::vector<InstantMostVisitedItem>& items);
-  void OnPromoInformationReceived(bool is_app_launcher_enabled);
   void OnSetDisplayInstantResults(bool display_instant_results);
   void OnSetInputInProgress(bool input_in_progress);
   void OnSetSuggestionToPrefetch(const InstantSuggestion& suggestion);
@@ -169,7 +167,6 @@
   GURL GetURLForMostVisitedItem(InstantRestrictedID item_id) const;
 
   int page_seq_no_;
-  bool app_launcher_enabled_;
   bool is_focused_;
   bool is_input_in_progress_;
   bool is_key_capture_enabled_;
diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc
index 24ad6a49..8d2a6aab 100644
--- a/chrome/renderer/searchbox/searchbox_extension.cc
+++ b/chrome/renderer/searchbox/searchbox_extension.cc
@@ -421,10 +421,6 @@
   // Focuses the omnibox.
   static void Focus(const v8::FunctionCallbackInfo<v8::Value>& args);
 
-  // Gets whether or not the app launcher is enabled.
-  static void GetAppLauncherEnabled(
-      const v8::FunctionCallbackInfo<v8::Value>& args);
-
   // Gets Most Visited Items.
   static void GetMostVisitedItems(
       const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -601,8 +597,6 @@
     return v8::FunctionTemplate::New(isolate, DeleteMostVisitedItem);
   if (name->Equals(v8::String::NewFromUtf8(isolate, "Focus")))
     return v8::FunctionTemplate::New(isolate, Focus);
-  if (name->Equals(v8::String::NewFromUtf8(isolate, "GetAppLauncherEnabled")))
-    return v8::FunctionTemplate::New(isolate, GetAppLauncherEnabled);
   if (name->Equals(v8::String::NewFromUtf8(isolate, "GetMostVisitedItems")))
     return v8::FunctionTemplate::New(isolate, GetMostVisitedItems);
   if (name->Equals(v8::String::NewFromUtf8(isolate, "GetMostVisitedItemData")))
@@ -718,16 +712,6 @@
 }
 
 // static
-void SearchBoxExtensionWrapper::GetAppLauncherEnabled(
-    const v8::FunctionCallbackInfo<v8::Value>& args) {
-  content::RenderView* render_view = GetRenderView();
-  if (!render_view) return;
-
-  args.GetReturnValue().Set(
-      SearchBox::Get(render_view)->app_launcher_enabled());
-}
-
-// static
 void SearchBoxExtensionWrapper::GetMostVisitedItems(
     const v8::FunctionCallbackInfo<v8::Value>& args) {
   content::RenderView* render_view = GetRenderView();
diff --git a/chrome/renderer/spellchecker/spellcheck.cc b/chrome/renderer/spellchecker/spellcheck.cc
index 8df94a29..8f2bfcb 100644
--- a/chrome/renderer/spellchecker/spellcheck.cc
+++ b/chrome/renderer/spellchecker/spellcheck.cc
@@ -19,12 +19,12 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/common/channel_info.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/spellcheck_common.h"
 #include "chrome/common/spellcheck_messages.h"
 #include "chrome/common/spellcheck_result.h"
 #include "chrome/renderer/spellchecker/spellcheck_language.h"
 #include "chrome/renderer/spellchecker/spellcheck_provider.h"
+#include "components/spellcheck/common/spellcheck_switches.h"
 #include "content/public/renderer/render_thread.h"
 #include "content/public/renderer/render_view.h"
 #include "content/public/renderer/render_view_visitor.h"
@@ -540,7 +540,7 @@
 bool SpellCheck::IsSpellcheckEnabled() {
 #if defined(OS_ANDROID)
   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-                 switches::kEnableAndroidSpellChecker)) {
+          spellcheck::switches::kEnableAndroidSpellChecker)) {
     return false;
   }
 #endif
diff --git a/chrome/test/chromedriver/test/test_expectations b/chrome/test/chromedriver/test/test_expectations
index 7a2518b..ebf710a8 100644
--- a/chrome/test/chromedriver/test/test_expectations
+++ b/chrome/test/chromedriver/test/test_expectations
@@ -209,6 +209,8 @@
     _OS_NEGATIVE_FILTER['android:chrome'] + [
         'CorrectEventFiringTest.testShouldFireClickEventWhenClicking',
         'CorrectEventFiringTest.testShouldFireMouseDownEventWhenClicking',
+        # https://bugs.chromium.org/p/chromedriver/issues/detail?id=998
+        'ImplicitWaitTest.testShouldImplicitlyWaitForASingleElement',
     ]
 )
 _OS_NEGATIVE_FILTER['android:chrome_beta'] = (
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 9c028fe..9cdad22 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -2524,6 +2524,15 @@
     "test_policy": { "ArcCertificatesSyncMode": 0 }
   },
 
+  "ArcBackupRestoreEnabled": {
+    "os": ["chromeos"],
+    "can_be_recommended": false,
+    "test_policy": { "ArcBackupRestoreEnabled": false },
+    "pref_mappings": [
+      { "pref": "arc.backup_restore.enabled" }
+    ]
+  },
+
   "----- Chrome OS device policies ---------------------------------------": {},
 
   "DevicePolicyRefreshRate": {
diff --git a/chrome/test/data/webui/net_internals/log_view_painter.js b/chrome/test/data/webui/net_internals/log_view_painter.js
index 7a64639b..7dc5cf7c 100644
--- a/chrome/test/data/webui/net_internals/log_view_painter.js
+++ b/chrome/test/data/webui/net_internals/log_view_painter.js
@@ -1822,7 +1822,7 @@
         'type': EventSourceType.HTTP2_SESSION
       },
       'time': '1012984638',
-      'type': EventType.HTTP2_SESSION_SYN_STREAM
+      'type': EventType.HTTP2_SESSION_SEND_HEADERS
     },
     {
       'params': {
@@ -1843,12 +1843,12 @@
         'type': EventSourceType.HTTP2_SESSION
       },
       'time': '1012992266',
-      'type': EventType.HTTP2_SESSION_SYN_REPLY
+      'type': EventType.HTTP2_SESSION_RECV_HEADERS
     }
   ];
 
   testCase.expectedText =
-    't=1338924082421 [st=   0]  HTTP2_SESSION_SYN_STREAM\n' +
+    't=1338924082421 [st=   0]  HTTP2_SESSION_SEND_HEADERS\n' +
     '                           --> flags = 1\n' +
     '                           --> :host: mail.google.com\n' +
     '                               :method: GET\n' +
@@ -1862,7 +1862,7 @@
     '                               cookie: MyMagicPony\n' +
     '                               user-agent: Mozilla/5.0\n' +
     '                           --> stream_id = 1\n' +
-    't=1338924090049 [st=7628]  HTTP2_SESSION_SYN_REPLY\n' +
+    't=1338924090049 [st=7628]  HTTP2_SESSION_RECV_HEADERS\n' +
     '                           --> flags = 0\n' +
     '                           --> :status: 204 No Content\n' +
     '                               :version: HTTP/1.1\n' +
diff --git a/chrome/test/data/webui/settings/animation_browsertest.js b/chrome/test/data/webui/settings/animation_browsertest.js
index 5ee559a..ee98da4 100644
--- a/chrome/test/data/webui/settings/animation_browsertest.js
+++ b/chrome/test/data/webui/settings/animation_browsertest.js
@@ -130,7 +130,7 @@
 
         animation.cancel();
 
-        // The promise should resolve before the finish event is scheduled.
+        // The promise should be rejected before the cancel event is scheduled.
         animation.finished.catch(function() {
           expectEquals(0, div.clientHeight);
           animation.removeEventListener('cancel', onCancelUnexpectedly);
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 5e32036..d4924a4 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -184,6 +184,7 @@
       "//components/invalidation/impl:unit_tests",
       "//components/keyed_service/content:unit_tests",
       "//components/link_header_util:unit_tests",
+      "//components/memory_coordinator/browser:unit_tests",
       "//components/memory_coordinator/child:unit_tests",
       "//components/navigation_interception:unit_tests",
       "//components/network_hints/renderer:unit_tests",
@@ -224,7 +225,7 @@
     deps += [ ":components_tests_pak_bundle_data" ]
   }
 
-  if (is_mac) {
+  if (is_mac || is_linux) {
     data_deps += [ "//content/shell:pak" ]
   }
 
diff --git a/components/OWNERS b/components/OWNERS
index 5ab266d..fcb64f4 100644
--- a/components/OWNERS
+++ b/components/OWNERS
@@ -191,6 +191,8 @@
 
 per-file signin.gypi=file://components/signin/OWNERS
 
+per-file spellcheck.gypi=file://components/spellcheck/OWNERS
+
 per-file ssl_errors.gypi=file://components/ssl_errors/OWNERS
 per-file ssl_errors_strings.grdp=file://components/ssl_errors/OWNERS
 
diff --git a/components/arc/common/process.mojom b/components/arc/common/process.mojom
index 6dd05d68..f2d365f7 100644
--- a/components/arc/common/process.mojom
+++ b/components/arc/common/process.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Next MinVersion: 5
+// Next MinVersion: 6
 
 module arc.mojom;
 
@@ -87,6 +87,13 @@
 
   // Package names running in the process.
   [MinVersion=4] array<string>? packages;
+
+  // Whether this app is focused in ARC++ multi-window environment.
+  [MinVersion=5] bool is_focused;
+
+  // Last time the process was active. Milliseconds since boot.
+  // The clock is monotonic (comes from Android System.uptimeMillis()).
+  [MinVersion=5] int64 last_activity_time;
 };
 
 interface ProcessInstance {
diff --git a/components/browsing_data.gypi b/components/browsing_data.gypi
index f30819b1..6b2e95c 100644
--- a/components/browsing_data.gypi
+++ b/components/browsing_data.gypi
@@ -10,6 +10,12 @@
       'type': 'static_library',
       'dependencies': [
         '../base/base.gyp:base',
+        'components.gyp:autofill_core_browser',
+        'components.gyp:history_core_browser',
+        'components.gyp:password_manager_core_browser',
+        'components.gyp:sync_driver',
+        'components.gyp:webdata_common',
+        'prefs/prefs.gyp:prefs',
       ],
       'include_dirs': [
         '..',
@@ -20,8 +26,14 @@
         'browsing_data/core/browsing_data_utils.h',
         'browsing_data/core/pref_names.cc',
         'browsing_data/core/pref_names.h',
+        'browsing_data/core/counters/autofill_counter.cc',
+        'browsing_data/core/counters/autofill_counter.h',
         'browsing_data/core/counters/browsing_data_counter.cc',
         'browsing_data/core/counters/browsing_data_counter.h',
+        'browsing_data/core/counters/history_counter.cc',
+        'browsing_data/core/counters/history_counter.h',
+        'browsing_data/core/counters/passwords_counter.cc',
+        'browsing_data/core/counters/passwords_counter.h',
       ],
     },
   ],
diff --git a/components/browsing_data/core/BUILD.gn b/components/browsing_data/core/BUILD.gn
index 3345896..19ae36c 100644
--- a/components/browsing_data/core/BUILD.gn
+++ b/components/browsing_data/core/BUILD.gn
@@ -10,15 +10,26 @@
   sources = [
     "browsing_data_utils.cc",
     "browsing_data_utils.h",
+    "counters/autofill_counter.cc",
+    "counters/autofill_counter.h",
     "counters/browsing_data_counter.cc",
     "counters/browsing_data_counter.h",
+    "counters/history_counter.cc",
+    "counters/history_counter.h",
+    "counters/passwords_counter.cc",
+    "counters/passwords_counter.h",
     "pref_names.cc",
     "pref_names.h",
   ]
 
   deps = [
     "//base",
+    "//components/autofill/core/browser",
+    "//components/history/core/browser",
+    "//components/password_manager/core/browser",
     "//components/prefs:prefs",
+    "//components/sync_driver",
+    "//components/webdata/common",
   ]
 }
 
diff --git a/components/browsing_data/core/DEPS b/components/browsing_data/core/DEPS
index eac0761..f04c02e 100644
--- a/components/browsing_data/core/DEPS
+++ b/components/browsing_data/core/DEPS
@@ -1,3 +1,8 @@
 include_rules = [
+  "+components/autofill/core/browser",
+  "+components/history/core/browser",
+  "+components/password_manager/core/browser",
   "+components/prefs",
+  "+components/sync_driver",
+  "+components/webdata/common",
 ]
diff --git a/chrome/browser/browsing_data/autofill_counter.cc b/components/browsing_data/core/counters/autofill_counter.cc
similarity index 78%
rename from chrome/browser/browsing_data/autofill_counter.cc
rename to components/browsing_data/core/counters/autofill_counter.cc
index 4da1de3..1d52001 100644
--- a/chrome/browser/browsing_data/autofill_counter.cc
+++ b/components/browsing_data/core/counters/autofill_counter.cc
@@ -2,24 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/browsing_data/autofill_counter.h"
+#include "components/browsing_data/core/counters/autofill_counter.h"
 
 #include <algorithm>
 #include <utility>
 #include <vector>
 
 #include "base/memory/scoped_vector.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/web_data_service_factory.h"
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/browsing_data/core/pref_names.h"
 
-AutofillCounter::AutofillCounter(Profile* profile)
-    : BrowsingDataCounter(browsing_data::prefs::kDeleteFormData),
-      profile_(profile),
-      web_data_service_(nullptr),
+namespace browsing_data {
+
+AutofillCounter::AutofillCounter(
+    scoped_refptr<autofill::AutofillWebDataService> web_data_service)
+    : web_data_service_(web_data_service),
       suggestions_query_(0),
       credit_cards_query_(0),
       addresses_query_(0),
@@ -32,11 +31,13 @@
 }
 
 void AutofillCounter::OnInitialized() {
-  web_data_service_ = WebDataServiceFactory::GetAutofillWebDataForProfile(
-      profile_, ServiceAccessType::EXPLICIT_ACCESS);
   DCHECK(web_data_service_);
 }
 
+const char* AutofillCounter::GetPrefName() const {
+  return browsing_data::prefs::kDeleteFormData;
+}
+
 void AutofillCounter::SetPeriodStartForTesting(
     const base::Time& period_start_for_testing) {
   period_start_for_testing_ = period_start_for_testing;
@@ -44,8 +45,8 @@
 
 void AutofillCounter::Count() {
   const base::Time start = period_start_for_testing_.is_null()
-      ? GetPeriodStart()
-      : period_start_for_testing_;
+                               ? GetPeriodStart()
+                               : period_start_for_testing_;
 
   CancelAllRequests();
 
@@ -78,7 +79,8 @@
 }
 
 void AutofillCounter::OnWebDataServiceRequestDone(
-    WebDataServiceBase::Handle handle, const WDTypedResult* result) {
+    WebDataServiceBase::Handle handle,
+    const WDTypedResult* result) {
   DCHECK(thread_checker_.CalledOnValidThread());
   if (!result) {
     CancelAllRequests();
@@ -86,8 +88,8 @@
   }
 
   const base::Time start = period_start_for_testing_.is_null()
-      ? GetPeriodStart()
-      : period_start_for_testing_;
+                               ? GetPeriodStart()
+                               : period_start_for_testing_;
 
   if (handle == suggestions_query_) {
     // Autocomplete suggestions.
@@ -99,8 +101,8 @@
     // Credit cards.
     DCHECK_EQ(AUTOFILL_CREDITCARDS_RESULT, result->GetType());
     const std::vector<autofill::CreditCard*> credit_cards =
-        static_cast<const WDResult<std::vector<autofill::CreditCard*>>*>(
-            result)->GetValue();
+        static_cast<const WDResult<std::vector<autofill::CreditCard*>>*>(result)
+            ->GetValue();
 
     // We own the result from this query. Make sure it will be deleted.
     ScopedVector<const autofill::CreditCard> owned_result;
@@ -119,18 +121,18 @@
     DCHECK_EQ(AUTOFILL_PROFILES_RESULT, result->GetType());
     const std::vector<autofill::AutofillProfile*> addresses =
         static_cast<const WDResult<std::vector<autofill::AutofillProfile*>>*>(
-            result)->GetValue();
+            result)
+            ->GetValue();
 
     // We own the result from this query. Make sure it will be deleted.
     ScopedVector<const autofill::AutofillProfile> owned_result;
     owned_result.assign(addresses.begin(), addresses.end());
 
-    num_addresses_ = std::count_if(
-        addresses.begin(),
-        addresses.end(),
-        [start](const autofill::AutofillProfile* address) {
-          return address->modification_date() >= start;
-        });
+    num_addresses_ =
+        std::count_if(addresses.begin(), addresses.end(),
+                      [start](const autofill::AutofillProfile* address) {
+                        return address->modification_date() >= start;
+                      });
     addresses_query_ = 0;
 
   } else {
@@ -157,15 +159,14 @@
 
 // AutofillCounter::AutofillResult ---------------------------------------------
 
-AutofillCounter::AutofillResult::AutofillResult(
-    const AutofillCounter* source,
-    ResultInt num_suggestions,
-    ResultInt num_credit_cards,
-    ResultInt num_addresses)
+AutofillCounter::AutofillResult::AutofillResult(const AutofillCounter* source,
+                                                ResultInt num_suggestions,
+                                                ResultInt num_credit_cards,
+                                                ResultInt num_addresses)
     : FinishedResult(source, num_suggestions),
       num_credit_cards_(num_credit_cards),
-      num_addresses_(num_addresses) {
-}
+      num_addresses_(num_addresses) {}
 
-AutofillCounter::AutofillResult::~AutofillResult() {
-}
+AutofillCounter::AutofillResult::~AutofillResult() {}
+
+}  // namespace browsing_data
diff --git a/chrome/browser/browsing_data/autofill_counter.h b/components/browsing_data/core/counters/autofill_counter.h
similarity index 84%
rename from chrome/browser/browsing_data/autofill_counter.h
rename to components/browsing_data/core/counters/autofill_counter.h
index bc65dc33..f183304 100644
--- a/chrome/browser/browsing_data/autofill_counter.h
+++ b/components/browsing_data/core/counters/autofill_counter.h
@@ -2,21 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_BROWSING_DATA_AUTOFILL_COUNTER_H_
-#define CHROME_BROWSER_BROWSING_DATA_AUTOFILL_COUNTER_H_
+#ifndef COMPONENTS_BROWSING_DATA_CORE_COUNTERS_AUTOFILL_COUNTER_H_
+#define COMPONENTS_BROWSING_DATA_CORE_COUNTERS_AUTOFILL_COUNTER_H_
 
 #include "base/macros.h"
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/browsing_data/core/counters/browsing_data_counter.h"
 #include "components/webdata/common/web_data_service_consumer.h"
 
-class Profile;
-
 namespace autofill {
 class AutofillWebDataService;
 }
 
+namespace browsing_data {
+
 class AutofillCounter : public browsing_data::BrowsingDataCounter,
                         public WebDataServiceConsumer {
  public:
@@ -38,7 +39,8 @@
     DISALLOW_COPY_AND_ASSIGN(AutofillResult);
   };
 
-  explicit AutofillCounter(Profile* profile);
+  explicit AutofillCounter(
+      scoped_refptr<autofill::AutofillWebDataService> web_data_service);
   ~AutofillCounter() override;
 
   // BrowsingDataCounter implementation.
@@ -49,6 +51,8 @@
     return suggestions_query_ || credit_cards_query_ || addresses_query_;
   }
 
+  const char* GetPrefName() const override;
+
   // Set the beginning of the time period for testing. AutofillTable does not
   // allow us to set time explicitly, and BrowsingDataCounter recognizes
   // only predefined time periods, out of which the lowest one is one hour.
@@ -58,7 +62,15 @@
   void SetPeriodStartForTesting(const base::Time& period_start_for_testing);
 
  private:
-  Profile* profile_;
+  void Count() override;
+
+  // WebDataServiceConsumer implementation.
+  void OnWebDataServiceRequestDone(WebDataServiceBase::Handle handle,
+                                   const WDTypedResult* result) override;
+
+  // Cancel all pending requests to AutofillWebdataService.
+  void CancelAllRequests();
+
   base::ThreadChecker thread_checker_;
 
   scoped_refptr<autofill::AutofillWebDataService> web_data_service_;
@@ -73,16 +85,9 @@
 
   base::Time period_start_for_testing_;
 
-  void Count() override;
-
-  // WebDataServiceConsumer implementation.
-  void OnWebDataServiceRequestDone(WebDataServiceBase::Handle handle,
-                                   const WDTypedResult* result) override;
-
-  // Cancel all pending requests to AutofillWebdataService.
-  void CancelAllRequests();
-
   DISALLOW_COPY_AND_ASSIGN(AutofillCounter);
 };
 
-#endif  // CHROME_BROWSER_BROWSING_DATA_AUTOFILL_COUNTER_H_
+}  // namespace browsing_data
+
+#endif  // COMPONENTS_BROWSING_DATA_CORE_COUNTERS_AUTOFILL_COUNTER_H_
diff --git a/components/browsing_data/core/counters/browsing_data_counter.cc b/components/browsing_data/core/counters/browsing_data_counter.cc
index 3aa9d51a..26a374ac 100644
--- a/components/browsing_data/core/counters/browsing_data_counter.cc
+++ b/components/browsing_data/core/counters/browsing_data_counter.cc
@@ -13,8 +13,7 @@
 
 namespace browsing_data {
 
-BrowsingDataCounter::BrowsingDataCounter(const std::string& pref_name)
-    : pref_name_(pref_name) {}
+BrowsingDataCounter::BrowsingDataCounter() {}
 
 BrowsingDataCounter::~BrowsingDataCounter() {}
 
@@ -61,10 +60,6 @@
   callback_.Run(std::move(result));
 }
 
-const std::string& BrowsingDataCounter::GetPrefName() const {
-  return pref_name_;
-}
-
 PrefService* BrowsingDataCounter::GetPrefs() const {
   return pref_service_;
 }
diff --git a/components/browsing_data/core/counters/browsing_data_counter.h b/components/browsing_data/core/counters/browsing_data_counter.h
index 5ef1239..c274936 100644
--- a/components/browsing_data/core/counters/browsing_data_counter.h
+++ b/components/browsing_data/core/counters/browsing_data_counter.h
@@ -58,14 +58,14 @@
 
   typedef base::Callback<void(std::unique_ptr<Result>)> Callback;
 
-  BrowsingDataCounter(const std::string& pref_name);
+  BrowsingDataCounter();
   virtual ~BrowsingDataCounter();
 
   // Should be called once to initialize this class.
   void Init(PrefService* pref_service, const Callback& callback);
 
   // Name of the preference associated with this counter.
-  const std::string& GetPrefName() const;
+  virtual const char* GetPrefName() const = 0;
 
   // PrefService that manages the preferences for the user profile
   // associated with this counter.
@@ -95,9 +95,6 @@
   // Count the data.
   virtual void Count() = 0;
 
-  // Name of the preference associated with this counter.
-  const std::string pref_name_;
-
   // Pointer to the PrefService that manages the preferences for the user
   // profile associated with this counter.
   PrefService* pref_service_;
diff --git a/chrome/browser/browsing_data/history_counter.cc b/components/browsing_data/core/counters/history_counter.cc
similarity index 66%
rename from chrome/browser/browsing_data/history_counter.cc
rename to components/browsing_data/core/counters/history_counter.cc
index 1a6388f2..44364153 100644
--- a/chrome/browser/browsing_data/history_counter.cc
+++ b/components/browsing_data/core/counters/history_counter.cc
@@ -2,35 +2,31 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/browsing_data/history_counter.h"
+#include "components/browsing_data/core/counters/history_counter.h"
 
 #include <limits.h>
 #include <stdint.h>
 
 #include "base/memory/ptr_util.h"
 #include "base/timer/timer.h"
-#include "chrome/browser/history/history_service_factory.h"
-#include "chrome/browser/history/web_history_service_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sync/profile_sync_service_factory.h"
-#include "components/browser_sync/browser/profile_sync_service.h"
 #include "components/browsing_data/core/pref_names.h"
-#include "components/history/core/browser/history_service.h"
-#include "components/history/core/browser/web_history_service.h"
-#include "content/public/browser/browser_thread.h"
 
 namespace {
 static const int64_t kWebHistoryTimeoutSeconds = 10;
 }
 
-HistoryCounter::HistoryCounter(Profile* profile)
-    : BrowsingDataCounter(browsing_data::prefs::kDeleteBrowsingHistory),
-      profile_(profile),
+namespace browsing_data {
+
+HistoryCounter::HistoryCounter(
+    history::HistoryService* history_service,
+    const GetUpdatedWebHistoryServiceCallback& callback,
+    sync_driver::SyncService* sync_service)
+    : history_service_(history_service),
+      web_history_service_callback_(callback),
+      sync_service_(sync_service),
       has_synced_visits_(false),
       local_counting_finished_(false),
       web_counting_finished_(false),
-      testing_web_history_service_(nullptr),
-      sync_service_(nullptr),
       history_sync_enabled_(false),
       weak_ptr_factory_(this) {}
 
@@ -40,19 +36,17 @@
 }
 
 void HistoryCounter::OnInitialized() {
-  sync_service_ = ProfileSyncServiceFactory::GetForProfile(profile_);
   if (sync_service_)
     sync_service_->AddObserver(this);
-  history_sync_enabled_ = !!WebHistoryServiceFactory::GetForProfile(profile_);
+  history_sync_enabled_ = !!web_history_service_callback_.Run();
 }
 
 bool HistoryCounter::HasTrackedTasks() {
   return cancelable_task_tracker_.HasTrackedTasks();
 }
 
-void HistoryCounter::SetWebHistoryServiceForTesting(
-    history::WebHistoryService* service) {
-  testing_web_history_service_ = service;
+const char* HistoryCounter::GetPrefName() const {
+  return browsing_data::prefs::kDeleteBrowsingHistory;
 }
 
 void HistoryCounter::Count() {
@@ -64,22 +58,15 @@
   // Count the locally stored items.
   local_counting_finished_ = false;
 
-  history::HistoryService* service = HistoryServiceFactory::GetForProfile(
-      profile_, ServiceAccessType::EXPLICIT_ACCESS);
-
-  service->GetHistoryCount(
-      GetPeriodStart(),
-      base::Time::Max(),
+  history_service_->GetHistoryCount(
+      GetPeriodStart(), base::Time::Max(),
       base::Bind(&HistoryCounter::OnGetLocalHistoryCount,
                  weak_ptr_factory_.GetWeakPtr()),
       &cancelable_task_tracker_);
 
   // If the history sync is enabled, test if there is at least one synced item.
-  // If the testing web history service is present, use that one instead.
   history::WebHistoryService* web_history =
-      testing_web_history_service_
-          ? testing_web_history_service_
-          : WebHistoryServiceFactory::GetForProfile(profile_);
+      web_history_service_callback_.Run();
 
   if (!web_history) {
     web_counting_finished_ = true;
@@ -89,9 +76,7 @@
   web_counting_finished_ = false;
 
   web_history_timeout_.Start(
-      FROM_HERE,
-      base::TimeDelta::FromSeconds(kWebHistoryTimeoutSeconds),
-      this,
+      FROM_HERE, base::TimeDelta::FromSeconds(kWebHistoryTimeoutSeconds), this,
       &HistoryCounter::OnWebHistoryTimeout);
 
   history::QueryOptions options;
@@ -99,8 +84,7 @@
   options.begin_time = GetPeriodStart();
   options.end_time = base::Time::Max();
   web_history_request_ = web_history->QueryHistory(
-      base::string16(),
-      options,
+      base::string16(), options,
       base::Bind(&HistoryCounter::OnGetWebHistoryCount,
                  weak_ptr_factory_.GetWeakPtr()));
 
@@ -141,8 +125,7 @@
   // entry in the "event" list.
   const base::ListValue* events;
   has_synced_visits_ =
-      !result ||
-      (result->GetList("event", &events) && !events->empty());
+      !result || (result->GetList("event", &events) && !events->empty());
   web_counting_finished_ = true;
   MergeResults();
 }
@@ -168,20 +151,15 @@
       new HistoryResult(this, local_result_, has_synced_visits_)));
 }
 
-HistoryCounter::HistoryResult::HistoryResult(
-    const HistoryCounter* source,
-    ResultInt value,
-    bool has_synced_visits)
-    : FinishedResult(source, value),
-      has_synced_visits_(has_synced_visits) {
-}
+HistoryCounter::HistoryResult::HistoryResult(const HistoryCounter* source,
+                                             ResultInt value,
+                                             bool has_synced_visits)
+    : FinishedResult(source, value), has_synced_visits_(has_synced_visits) {}
 
-HistoryCounter::HistoryResult::~HistoryResult() {
-}
+HistoryCounter::HistoryResult::~HistoryResult() {}
 
 void HistoryCounter::OnStateChanged() {
-  bool history_sync_enabled_new_state =
-      !!WebHistoryServiceFactory::GetForProfile(profile_);
+  bool history_sync_enabled_new_state = !!web_history_service_callback_.Run();
 
   // If the history sync was just enabled or disabled, restart the counter
   // so that we update the result accordingly.
@@ -190,3 +168,5 @@
     Restart();
   }
 }
+
+}  // namespace browsing_data
diff --git a/chrome/browser/browsing_data/history_counter.h b/components/browsing_data/core/counters/history_counter.h
similarity index 70%
rename from chrome/browser/browsing_data/history_counter.h
rename to components/browsing_data/core/counters/history_counter.h
index 1ce74cd..2b835584 100644
--- a/chrome/browser/browsing_data/history_counter.h
+++ b/components/browsing_data/core/counters/history_counter.h
@@ -2,24 +2,25 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_BROWSING_DATA_HISTORY_COUNTER_H_
-#define CHROME_BROWSER_BROWSING_DATA_HISTORY_COUNTER_H_
+#ifndef COMPONENTS_BROWSING_DATA_CORE_COUNTERS_HISTORY_COUNTER_H_
+#define COMPONENTS_BROWSING_DATA_CORE_COUNTERS_HISTORY_COUNTER_H_
 
-#include "base/memory/weak_ptr.h"
 #include "base/task/cancelable_task_tracker.h"
 #include "base/timer/timer.h"
 #include "components/browsing_data/core/counters/browsing_data_counter.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/web_history_service.h"
+#include "components/sync_driver/sync_service.h"
 #include "components/sync_driver/sync_service_observer.h"
 
-class Profile;
-
-class ProfileSyncService;
+namespace browsing_data {
 
 class HistoryCounter : public browsing_data::BrowsingDataCounter,
                        public sync_driver::SyncServiceObserver {
  public:
+  typedef base::Callback<history::WebHistoryService*()>
+      GetUpdatedWebHistoryServiceCallback;
+
   class HistoryResult : public FinishedResult {
    public:
     HistoryResult(const HistoryCounter* source,
@@ -33,7 +34,9 @@
     bool has_synced_visits_;
   };
 
-  explicit HistoryCounter(Profile* profile);
+  explicit HistoryCounter(history::HistoryService* history_service,
+                          const GetUpdatedWebHistoryServiceCallback& callback,
+                          sync_driver::SyncService* sync_service);
   ~HistoryCounter() override;
 
   void OnInitialized() override;
@@ -41,32 +44,9 @@
   // Whether there are counting tasks in progress. Only used for testing.
   bool HasTrackedTasks();
 
-  // Make the history counter use a custom WebHistoryService instance. Only
-  // used for testing.
-  void SetWebHistoryServiceForTesting(history::WebHistoryService* service);
+  const char* GetPrefName() const override;
 
  private:
-  Profile* profile_;
-
-  BrowsingDataCounter::ResultInt local_result_;
-  bool has_synced_visits_;
-
-  bool local_counting_finished_;
-  bool web_counting_finished_;
-
-  history::WebHistoryService* testing_web_history_service_;
-
-  base::CancelableTaskTracker cancelable_task_tracker_;
-  std::unique_ptr<history::WebHistoryService::Request> web_history_request_;
-  base::OneShotTimer web_history_timeout_;
-
-  base::ThreadChecker thread_checker_;
-
-  ProfileSyncService* sync_service_;
-  bool history_sync_enabled_;
-
-  base::WeakPtrFactory<HistoryCounter> weak_ptr_factory_;
-
   void Count() override;
 
   void OnGetLocalHistoryCount(history::HistoryCountResult result);
@@ -77,6 +57,31 @@
 
   // SyncServiceObserver implementation.
   void OnStateChanged() override;
+
+  history::HistoryService* history_service_;
+
+  GetUpdatedWebHistoryServiceCallback web_history_service_callback_;
+
+  sync_driver::SyncService* sync_service_;
+
+  bool has_synced_visits_;
+
+  bool local_counting_finished_;
+  bool web_counting_finished_;
+
+  base::CancelableTaskTracker cancelable_task_tracker_;
+  std::unique_ptr<history::WebHistoryService::Request> web_history_request_;
+  base::OneShotTimer web_history_timeout_;
+
+  base::ThreadChecker thread_checker_;
+
+  BrowsingDataCounter::ResultInt local_result_;
+
+  bool history_sync_enabled_;
+
+  base::WeakPtrFactory<HistoryCounter> weak_ptr_factory_;
 };
 
-#endif  // CHROME_BROWSER_BROWSING_DATA_HISTORY_COUNTER_H_
+}  // namespace browsing_data
+
+#endif  // COMPONENTS_BROWSING_DATA_CORE_COUNTERS_HISTORY_COUNTER_H_
diff --git a/chrome/browser/browsing_data/passwords_counter.cc b/components/browsing_data/core/counters/passwords_counter.cc
similarity index 61%
rename from chrome/browser/browsing_data/passwords_counter.cc
rename to components/browsing_data/core/counters/passwords_counter.cc
index 5af2174..ab60368 100644
--- a/chrome/browser/browsing_data/passwords_counter.cc
+++ b/components/browsing_data/core/counters/passwords_counter.cc
@@ -1,28 +1,30 @@
-// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// 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 "chrome/browser/browsing_data/passwords_counter.h"
-#include "chrome/browser/password_manager/password_store_factory.h"
-#include "chrome/browser/profiles/profile.h"
+#include "components/browsing_data/core/counters/passwords_counter.h"
+
 #include "components/browsing_data/core/pref_names.h"
 #include "components/password_manager/core/browser/password_store.h"
 
-PasswordsCounter::PasswordsCounter(Profile* profile)
-    : BrowsingDataCounter(browsing_data::prefs::kDeletePasswords),
-      profile_(profile) {}
+namespace browsing_data {
+
+PasswordsCounter::PasswordsCounter(
+    scoped_refptr<password_manager::PasswordStore> store) : store_(store) {}
 
 PasswordsCounter::~PasswordsCounter() {
   store_->RemoveObserver(this);
 }
 
 void PasswordsCounter::OnInitialized() {
-  store_ = PasswordStoreFactory::GetForProfile(
-      profile_, ServiceAccessType::EXPLICIT_ACCESS);
   DCHECK(store_);
   store_->AddObserver(this);
 }
 
+const char* PasswordsCounter::GetPrefName() const {
+  return browsing_data::prefs::kDeletePasswords;
+}
+
 void PasswordsCounter::Count() {
   cancelable_task_tracker()->TryCancelAll();
   // TODO(msramek): We don't actually need the logins themselves, just their
@@ -35,15 +37,15 @@
 void PasswordsCounter::OnGetPasswordStoreResults(
     ScopedVector<autofill::PasswordForm> results) {
   base::Time start = GetPeriodStart();
-  ReportResult(std::count_if(
-      results.begin(),
-      results.end(),
-      [start](const autofill::PasswordForm* form) {
-        return form->date_created >= start;
-      }));
+  ReportResult(std::count_if(results.begin(), results.end(),
+                             [start](const autofill::PasswordForm* form) {
+                               return form->date_created >= start;
+                             }));
 }
 
 void PasswordsCounter::OnLoginsChanged(
     const password_manager::PasswordStoreChangeList& changes) {
   Restart();
 }
+
+}  // namespace browsing_data
\ No newline at end of file
diff --git a/chrome/browser/browsing_data/passwords_counter.h b/components/browsing_data/core/counters/passwords_counter.h
similarity index 72%
rename from chrome/browser/browsing_data/passwords_counter.h
rename to components/browsing_data/core/counters/passwords_counter.h
index 499a502..60415fb4 100644
--- a/chrome/browser/browsing_data/passwords_counter.h
+++ b/components/browsing_data/core/counters/passwords_counter.h
@@ -1,29 +1,27 @@
-// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// 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 CHROME_BROWSER_BROWSING_DATA_PASSWORDS_COUNTER_H_
-#define CHROME_BROWSER_BROWSING_DATA_PASSWORDS_COUNTER_H_
+#ifndef COMPONENTS_BROWSING_DATA_CORE_COUNTERS_PASSWORDS_COUNTER_H_
+#define COMPONENTS_BROWSING_DATA_CORE_COUNTERS_PASSWORDS_COUNTER_H_
 
 #include "components/browsing_data/core/counters/browsing_data_counter.h"
 #include "components/password_manager/core/browser/password_store.h"
 #include "components/password_manager/core/browser/password_store_consumer.h"
 
-class Profile;
+namespace browsing_data {
 
 class PasswordsCounter : public browsing_data::BrowsingDataCounter,
                          public password_manager::PasswordStoreConsumer,
                          public password_manager::PasswordStore::Observer {
  public:
-  explicit PasswordsCounter(Profile* profile);
+  explicit PasswordsCounter(
+      scoped_refptr<password_manager::PasswordStore> store);
   ~PasswordsCounter() override;
 
+  const char* GetPrefName() const override;
+
  private:
-  base::CancelableTaskTracker cancelable_task_tracker_;
-  scoped_refptr<password_manager::PasswordStore> store_;
-
-  Profile* profile_;
-
   void OnInitialized() override;
 
   // Counting is done asynchronously in a request to PasswordStore.
@@ -37,6 +35,11 @@
       const password_manager::PasswordStoreChangeList& changes) override;
 
   void Count() override;
+
+  base::CancelableTaskTracker cancelable_task_tracker_;
+  scoped_refptr<password_manager::PasswordStore> store_;
 };
 
-#endif  // CHROME_BROWSER_BROWSING_DATA_PASSWORDS_COUNTER_H_
+}  // namespace browsing_data
+
+#endif  // COMPONENTS_BROWSING_DATA_CORE_COUNTERS_PASSWORDS_COUNTER_H_
diff --git a/components/cast_certificate.gypi b/components/cast_certificate.gypi
index dc5f8e6..fb4f26ea 100644
--- a/components/cast_certificate.gypi
+++ b/components/cast_certificate.gypi
@@ -13,15 +13,46 @@
       'dependencies': [
         '../base/base.gyp:base',
         '../net/net.gyp:net',
+        'cast_certificate_proto',
       ],
       'sources': [
         'cast_certificate/cast_cert_validator.cc',
         'cast_certificate/cast_cert_validator.h',
+        'cast_certificate/cast_crl.cc',
+        'cast_certificate/cast_crl.h',
         'cast_certificate/cast_root_ca_cert_der-inc.h',
         'cast_certificate/eureka_root_ca_der-inc.h',
       ],
     },
     {
+      'target_name': 'cast_certificate_proto',
+      'type': 'static_library',
+      'sources': [
+        'cast_certificate/proto/revocation.proto',
+      ],
+      'includes': [
+        '../build/protoc.gypi'
+      ],
+      'variables': {
+        'proto_in_dir': 'cast_certificate/proto',
+        'proto_out_dir': 'components/cast_certificate/proto',
+      },
+    },
+    {
+      'target_name': 'cast_certificate_test_proto',
+      'type': 'static_library',
+      'sources': [
+        'cast_certificate/proto/test_suite.proto',
+      ],
+      'includes': [
+        '../build/protoc.gypi'
+      ],
+      'variables': {
+        'proto_in_dir': 'cast_certificate/proto',
+        'proto_out_dir': 'components/cast_certificate/proto',
+      },
+    },
+    {
       'target_name': 'cast_certificate_test_support',
       'type': 'static_library',
       'include_dirs': [
diff --git a/components/cast_certificate/BUILD.gn b/components/cast_certificate/BUILD.gn
index 9aec5bb..19ac0d7 100644
--- a/components/cast_certificate/BUILD.gn
+++ b/components/cast_certificate/BUILD.gn
@@ -6,11 +6,14 @@
   sources = [
     "cast_cert_validator.cc",
     "cast_cert_validator.h",
+    "cast_crl.cc",
+    "cast_crl.h",
     "cast_root_ca_cert_der-inc.h",
     "eureka_root_ca_der-inc.h",
   ]
   deps = [
     "//base",
+    "//components/cast_certificate/proto",
     "//net",
   ]
 }
@@ -33,11 +36,13 @@
   testonly = true
   sources = [
     "cast_cert_validator_unittest.cc",
+    "cast_crl_unittest.cc",
   ]
   deps = [
     ":cast_certificate",
     ":test_support",
     "//base",
+    "//components/cast_certificate/proto:unittest_proto",
     "//net",
     "//testing/gtest",
   ]
diff --git a/components/cast_certificate/DEPS b/components/cast_certificate/DEPS
index 8fa9d48..12af900 100644
--- a/components/cast_certificate/DEPS
+++ b/components/cast_certificate/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
+  "+crypto",
   "+net",
 ]
diff --git a/components/cast_certificate/cast_cert_validator.cc b/components/cast_certificate/cast_cert_validator.cc
index ec1fae2e..f7c62dd 100644
--- a/components/cast_certificate/cast_cert_validator.cc
+++ b/components/cast_certificate/cast_cert_validator.cc
@@ -14,6 +14,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/singleton.h"
 #include "net/cert/internal/cert_issuer_source_static.h"
+#include "components/cast_certificate/cast_crl.h"
 #include "net/cert/internal/certificate_policies.h"
 #include "net/cert/internal/extended_key_usage.h"
 #include "net/cert/internal/parse_certificate.h"
@@ -24,6 +25,7 @@
 #include "net/cert/internal/signature_policy.h"
 #include "net/cert/internal/trust_store.h"
 #include "net/cert/internal/verify_signed_data.h"
+#include "net/der/encode_values.h"
 #include "net/der/input.h"
 
 namespace cast_certificate {
@@ -233,19 +235,6 @@
   return true;
 }
 
-// Converts a base::Time::Exploded to a net::der::GeneralizedTime.
-net::der::GeneralizedTime ConvertExplodedTime(
-    const base::Time::Exploded& exploded) {
-  net::der::GeneralizedTime result;
-  result.year = exploded.year;
-  result.month = exploded.month;
-  result.day = exploded.day_of_month;
-  result.hours = exploded.hour;
-  result.minutes = exploded.minute;
-  result.seconds = exploded.second;
-  return result;
-}
-
 // Returns the parsing options used for Cast certificates.
 net::ParseCertificateOptions GetCertParsingOptions() {
   net::ParseCertificateOptions options;
@@ -265,9 +254,11 @@
 }  // namespace
 
 bool VerifyDeviceCert(const std::vector<std::string>& certs,
-                      const base::Time::Exploded& time,
+                      const base::Time& time,
                       std::unique_ptr<CertVerificationContext>* context,
-                      CastDeviceCertPolicy* policy) {
+                      CastDeviceCertPolicy* policy,
+                      const CastCRL* crl,
+                      CRLPolicy crl_policy) {
   if (certs.empty())
     return false;
 
@@ -295,10 +286,13 @@
 
   // Do path building and RFC 5280 compatible certificate verification using the
   // two Cast trust anchors and Cast signature policy.
+  net::der::GeneralizedTime verification_time;
+  if (!net::der::EncodeTimeAsGeneralizedTime(time, &verification_time))
+    return false;
   net::CertPathBuilder::Result result;
   net::CertPathBuilder path_builder(target_cert.get(), &CastTrustStore::Get(),
-                                    signature_policy.get(),
-                                    ConvertExplodedTime(time), &result);
+                                    signature_policy.get(), verification_time,
+                                    &result);
   path_builder.AddCertIssuerSource(&intermediate_cert_issuer_source);
   net::CompletionStatus rv = path_builder.Run(base::Closure());
   DCHECK_EQ(rv, net::CompletionStatus::SYNC);
@@ -307,7 +301,25 @@
 
   // Check properties of the leaf certificate (key usage, policy), and construct
   // a CertVerificationContext that uses its public key.
-  return CheckTargetCertificate(target_cert.get(), context, policy);
+  if (!CheckTargetCertificate(target_cert.get(), context, policy))
+    return false;
+
+  // Check if a CRL is available.
+  if (!crl) {
+    if (crl_policy == CRLPolicy::CRL_REQUIRED) {
+      return false;
+    }
+  } else {
+    if (result.paths.empty() ||
+        !result.paths[result.best_result_index]->is_success())
+      return false;
+
+    if (!crl->CheckRevocation(result.paths[result.best_result_index]->path,
+                              time)) {
+      return false;
+    }
+  }
+  return true;
 }
 
 std::unique_ptr<CertVerificationContext> CertVerificationContextImplForTest(
@@ -318,13 +330,13 @@
       new CertVerificationContextImpl(net::der::Input(spki), "CommonName"));
 }
 
-bool AddTrustAnchorForTest(const uint8_t* data, size_t length) {
+bool SetTrustAnchorForTest(const std::string& cert) {
   scoped_refptr<net::ParsedCertificate> anchor(
-      net::ParsedCertificate::CreateFromCertificateData(
-          data, length, net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE,
-          GetCertParsingOptions()));
+      net::ParsedCertificate::CreateFromCertificateCopy(
+          cert, GetCertParsingOptions()));
   if (!anchor)
     return false;
+  CastTrustStore::Get().Clear();
   CastTrustStore::Get().AddTrustedCertificate(std::move(anchor));
   return true;
 }
diff --git a/components/cast_certificate/cast_cert_validator.h b/components/cast_certificate/cast_cert_validator.h
index 23378cb..be924be7 100644
--- a/components/cast_certificate/cast_cert_validator.h
+++ b/components/cast_certificate/cast_cert_validator.h
@@ -16,6 +16,8 @@
 
 namespace cast_certificate {
 
+class CastCRL;
+
 // Describes the policy for a Device certificate.
 enum class CastDeviceCertPolicy {
   // The device certificate is unrestricted.
@@ -25,6 +27,14 @@
   AUDIO_ONLY,
 };
 
+enum class CRLPolicy {
+  // Revocation is only checked if a CRL is provided.
+  CRL_OPTIONAL,
+
+  // Revocation is always checked. A missing CRL results in failure.
+  CRL_REQUIRED,
+};
+
 // An object of this type is returned by the VerifyDeviceCert function, and can
 // be used for additional certificate-related operations, using the verified
 // certificate.
@@ -58,9 +68,16 @@
 //   * |certs[1..n-1]| are intermediates certificates to use in path building.
 //     Their ordering does not matter.
 //
-// * |time| is the UTC time to use for determining if the certificate
+// * |time| is the unix timestamp to use for determining if the certificate
 //   is expired.
 //
+// * |crl| is the CRL to check for certificate revocation status.
+//   If this is a nullptr, then revocation checking is currently disabled.
+//
+// * |crl_options| is for choosing how to handle the absence of a CRL.
+//   If crl_required is set to true, then an empty |crl| input would result
+//   in a failed verification. Otherwise, |crl| is ignored if it is absent.
+//
 // Outputs:
 //
 // Returns true on success, false on failure. On success the output
@@ -72,9 +89,11 @@
 //   * |policy| is filled with an indication of the device certificate's policy
 //     (i.e. is it for audio-only devices or is it unrestricted?)
 bool VerifyDeviceCert(const std::vector<std::string>& certs,
-                      const base::Time::Exploded& time,
+                      const base::Time& time,
                       std::unique_ptr<CertVerificationContext>* context,
-                      CastDeviceCertPolicy* policy) WARN_UNUSED_RESULT;
+                      CastDeviceCertPolicy* policy,
+                      const CastCRL* crl,
+                      CRLPolicy crl_policy) WARN_UNUSED_RESULT;
 
 // Exposed only for unit-tests, not for use in production code.
 // Production code would get a context from VerifyDeviceCert().
@@ -86,13 +105,9 @@
 
 // Exposed only for testing, not for use in production code.
 //
-// Injects trusted root certificates into the CastTrustStore.
-// |data| must remain valid and not be mutated throughout the lifetime of
-// the program.
-// Warning: Using this function concurrently with VerifyDeviceCert()
-//          is not thread safe.
-bool AddTrustAnchorForTest(const uint8_t* data,
-                           size_t length) WARN_UNUSED_RESULT;
+// Replaces trusted root certificates in the CastTrustStore.
+// Returns true if successful, false if nothing is changed.
+bool SetTrustAnchorForTest(const std::string& cert) WARN_UNUSED_RESULT;
 
 }  // namespace cast_certificate
 
diff --git a/components/cast_certificate/cast_cert_validator_test_helpers.cc b/components/cast_certificate/cast_cert_validator_test_helpers.cc
index 78c092b..a854abd 100644
--- a/components/cast_certificate/cast_cert_validator_test_helpers.cc
+++ b/components/cast_certificate/cast_cert_validator_test_helpers.cc
@@ -13,10 +13,6 @@
 
 namespace testing {
 
-namespace {
-
-// Reads a file from the test data directory
-// (//src/components/test/data/cast_certificate)
 std::string ReadTestFileToString(const base::StringPiece& file_name) {
   base::FilePath filepath;
   PathService::Get(base::DIR_SOURCE_ROOT, &filepath);
@@ -36,8 +32,6 @@
   return file_data;
 }
 
-}  // namespace
-
 std::vector<std::string> ReadCertificateChainFromFile(
     const base::StringPiece& file_name) {
   std::string file_data = ReadTestFileToString(file_name);
diff --git a/components/cast_certificate/cast_cert_validator_test_helpers.h b/components/cast_certificate/cast_cert_validator_test_helpers.h
index d5281a70..72cb503 100644
--- a/components/cast_certificate/cast_cert_validator_test_helpers.h
+++ b/components/cast_certificate/cast_cert_validator_test_helpers.h
@@ -35,6 +35,10 @@
 // |file_name| should be relative to //components/test/data/cast_certificate
 SignatureTestData ReadSignatureTestData(const base::StringPiece& file_name);
 
+// Reads a file from the test data directory
+// (//src/components/test/data/cast_certificate)
+std::string ReadTestFileToString(const base::StringPiece& file_name);
+
 }  // namespace testing
 
 }  // namespace cast_certificate
diff --git a/components/cast_certificate/cast_cert_validator_unittest.cc b/components/cast_certificate/cast_cert_validator_unittest.cc
index e363a1d..275e18a 100644
--- a/components/cast_certificate/cast_cert_validator_unittest.cc
+++ b/components/cast_certificate/cast_cert_validator_unittest.cc
@@ -39,14 +39,15 @@
              const std::string& expected_common_name,
              CastDeviceCertPolicy expected_policy,
              const std::string& certs_file_name,
-             const base::Time::Exploded& time,
+             const base::Time& time,
              const std::string& optional_signed_data_file_name) {
   auto certs =
       cast_certificate::testing::ReadCertificateChainFromFile(certs_file_name);
 
   std::unique_ptr<CertVerificationContext> context;
   CastDeviceCertPolicy policy;
-  bool result = VerifyDeviceCert(certs, time, &context, &policy);
+  bool result = VerifyDeviceCert(certs, time, &context, &policy, nullptr,
+                                 CRLPolicy::CRL_OPTIONAL);
 
   if (expected_result == RESULT_FAIL) {
     ASSERT_FALSE(result);
@@ -89,24 +90,26 @@
 }
 
 // Creates a time in UTC at midnight.
-base::Time::Exploded CreateDate(int year, int month, int day) {
+base::Time CreateDate(int year, int month, int day) {
   base::Time::Exploded time = {0};
   time.year = year;
   time.month = month;
   time.day_of_month = day;
-  return time;
+  base::Time result;
+  EXPECT_TRUE(base::Time::FromUTCExploded(time, &result));
+  return result;
 }
 
 // Returns 2016-04-01 00:00:00 UTC.
 //
 // This is a time when most of the test certificate paths are
 // valid.
-base::Time::Exploded AprilFirst2016() {
+base::Time AprilFirst2016() {
   return CreateDate(2016, 4, 1);
 }
 
 // Returns 2015-01-01 00:00:00 UTC.
-base::Time::Exploded JanuaryFirst2015() {
+base::Time JanuaryFirst2015() {
   return CreateDate(2015, 1, 1);
 }
 
@@ -114,7 +117,7 @@
 //
 // This is so far in the future that the test chains in this unit-test
 // should all be invalid.
-base::Time::Exploded MarchFirst2040() {
+base::Time MarchFirst2040() {
   return CreateDate(2040, 3, 1);
 }
 
diff --git a/components/cast_certificate/cast_crl.cc b/components/cast_certificate/cast_crl.cc
new file mode 100644
index 0000000..7efd90b
--- /dev/null
+++ b/components/cast_certificate/cast_crl.cc
@@ -0,0 +1,340 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/cast_certificate/cast_crl.h"
+
+#include <unordered_map>
+#include <unordered_set>
+
+#include "base/base64.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/singleton.h"
+#include "components/cast_certificate/proto/revocation.pb.h"
+#include "crypto/sha2.h"
+#include "net/cert/internal/parse_certificate.h"
+#include "net/cert/internal/parsed_certificate.h"
+#include "net/cert/internal/path_builder.h"
+#include "net/cert/internal/signature_algorithm.h"
+#include "net/cert/internal/signature_policy.h"
+#include "net/cert/internal/trust_store.h"
+#include "net/cert/internal/verify_certificate_chain.h"
+#include "net/cert/internal/verify_signed_data.h"
+#include "net/cert/x509_certificate.h"
+#include "net/der/encode_values.h"
+#include "net/der/input.h"
+#include "net/der/parser.h"
+#include "net/der/parse_values.h"
+
+namespace cast_certificate {
+namespace {
+
+enum CrlVersion {
+  // version 0: Spki Hash Algorithm = SHA-256
+  //            Signature Algorithm = RSA-PKCS1 V1.5 with SHA-256
+  CRL_VERSION_0 = 0,
+};
+
+// -------------------------------------------------------------------------
+// Cast CRL trust anchors.
+// -------------------------------------------------------------------------
+
+// There is one trusted root for Cast CRL certificate chains:
+//
+//   (1) CN=Cast CRL Root CA    (kCastCRLRootCaDer)
+//
+// These constants are defined by the file included next:
+
+#include "components/cast_certificate/cast_crl_root_ca_cert_der-inc.h"
+
+// Singleton for the Cast CRL trust store.
+class CastCRLTrustStore {
+ public:
+  static CastCRLTrustStore* GetInstance() {
+    return base::Singleton<CastCRLTrustStore, base::LeakySingletonTraits<
+                                                  CastCRLTrustStore>>::get();
+  }
+
+  static net::TrustStore& Get() { return GetInstance()->store_; }
+
+ private:
+  friend struct base::DefaultSingletonTraits<CastCRLTrustStore>;
+
+  CastCRLTrustStore() {
+    // Initialize the trust store with the root certificate.
+    // TODO(ryanchung): Add official Cast CRL Root here
+    // scoped_refptr<net::ParsedCertificate> root = net::ParsedCertificate::
+    //     net::ParsedCertificate::CreateFromCertificateData(
+    //         kCastCRLRootCaDer, sizeof(kCastCRLRootCaDer),
+    //         net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE, {});
+    // CHECK(root);
+    // store_.AddTrustedCertificate(std::move(root));
+  }
+
+  net::TrustStore store_;
+  DISALLOW_COPY_AND_ASSIGN(CastCRLTrustStore);
+};
+
+// Converts a uint64_t unix timestamp to net::der::GeneralizedTime.
+bool ConvertTimeSeconds(uint64_t seconds,
+                        net::der::GeneralizedTime* generalized_time) {
+  base::Time unix_timestamp =
+      base::Time::UnixEpoch() +
+      base::TimeDelta::FromSeconds(base::saturated_cast<int64_t>(seconds));
+  return net::der::EncodeTimeAsGeneralizedTime(unix_timestamp,
+                                               generalized_time);
+}
+
+// Specifies the signature verification policy.
+// The required algorithms are:
+// RSASSA PKCS#1 v1.5 with SHA-256, using RSA keys 2048-bits or longer.
+std::unique_ptr<net::SignaturePolicy> CreateCastSignaturePolicy() {
+  return base::WrapUnique(new net::SimpleSignaturePolicy(2048));
+}
+
+// Verifies the CRL is signed by a trusted CRL authority at the time the CRL
+// was issued. Verifies the signature of |tbs_crl| is valid based on the
+// certificate and signature in |crl|. The validity of |tbs_crl| is verified
+// at |time|. The validity period of the CRL is adjusted to be the earliest
+// of the issuer certificate chain's expiration and the CRL's expiration and
+// the result is stored in |overall_not_after|.
+bool VerifyCRL(const Crl& crl,
+               const TbsCrl& tbs_crl,
+               const base::Time& time,
+               net::der::GeneralizedTime* overall_not_after) {
+  // Verify the trust of the CRL authority.
+  scoped_refptr<net::ParsedCertificate> parsed_cert =
+      net::ParsedCertificate::CreateFromCertificateData(
+          reinterpret_cast<const uint8_t*>(crl.signer_cert().data()),
+          crl.signer_cert().size(),
+          net::ParsedCertificate::DataSource::EXTERNAL_REFERENCE, {});
+  if (parsed_cert == nullptr) {
+    VLOG(2) << "CRL - Issuer certificate parsing failed.";
+    return false;
+  }
+
+  // Wrap the signature in a BitString.
+  net::der::BitString signature_value_bit_string = net::der::BitString(
+      net::der::Input(base::StringPiece(crl.signature())), 0);
+
+  // Verify the signature.
+  auto signature_policy = CreateCastSignaturePolicy();
+  std::unique_ptr<net::SignatureAlgorithm> signature_algorithm_type =
+      net::SignatureAlgorithm::CreateRsaPkcs1(net::DigestAlgorithm::Sha256);
+  if (!VerifySignedData(*signature_algorithm_type,
+                        net::der::Input(&crl.tbs_crl()),
+                        signature_value_bit_string, parsed_cert->tbs().spki_tlv,
+                        signature_policy.get())) {
+    VLOG(2) << "CRL - Signature verification failed.";
+    return false;
+  }
+
+  // Verify the issuer certificate.
+  net::der::GeneralizedTime verification_time;
+  if (!net::der::EncodeTimeAsGeneralizedTime(time, &verification_time)) {
+    VLOG(2) << "CRL - Unable to parse verification time.";
+    return false;
+  }
+  net::CertPathBuilder::Result result;
+  net::CertPathBuilder path_builder(
+      parsed_cert.get(), &CastCRLTrustStore::Get(), signature_policy.get(),
+      verification_time, &result);
+  net::CompletionStatus rv = path_builder.Run(base::Closure());
+  DCHECK_EQ(rv, net::CompletionStatus::SYNC);
+  if (!result.is_success() || result.paths.empty() ||
+      !result.paths[result.best_result_index]->is_success()) {
+    VLOG(2) << "CRL - Issuer certificate verification failed.";
+    return false;
+  }
+  // There are no requirements placed on the leaf certificate having any
+  // particular KeyUsages. Leaf certificate checks are bypassed.
+
+  // Verify the CRL is still valid.
+  net::der::GeneralizedTime not_before;
+  if (!ConvertTimeSeconds(tbs_crl.not_before_seconds(), &not_before)) {
+    VLOG(2) << "CRL - Unable to parse not_before.";
+    return false;
+  }
+  net::der::GeneralizedTime not_after;
+  if (!ConvertTimeSeconds(tbs_crl.not_after_seconds(), &not_after)) {
+    VLOG(2) << "CRL - Unable to parse not_after.";
+    return false;
+  }
+  if ((verification_time < not_before) || (verification_time > not_after)) {
+    VLOG(2) << "CRL - Not time-valid.";
+    return false;
+  }
+
+  // Set CRL expiry to the earliest of the cert chain expiry and CRL expiry.
+  *overall_not_after = not_after;
+  for (const auto& cert : result.paths[result.best_result_index]->path) {
+    net::der::GeneralizedTime cert_not_after = cert->tbs().validity_not_after;
+    if (cert_not_after < *overall_not_after)
+      *overall_not_after = cert_not_after;
+  }
+
+  // Perform sanity check on serial numbers.
+  for (const auto& range : tbs_crl.revoked_serial_number_ranges()) {
+    uint64_t first_serial_number = range.first_serial_number();
+    uint64_t last_serial_number = range.last_serial_number();
+    if (last_serial_number < first_serial_number) {
+      VLOG(2) << "CRL - Malformed serial number range.";
+      return false;
+    }
+  }
+  return true;
+}
+
+class CastCRLImpl : public CastCRL {
+ public:
+  CastCRLImpl(const TbsCrl& tbs_crl,
+              const net::der::GeneralizedTime& overall_not_after);
+  ~CastCRLImpl() override;
+
+  bool CheckRevocation(const net::ParsedCertificateList& trusted_chain,
+                       const base::Time& time) const override;
+
+ private:
+  struct SerialNumberRange {
+    uint64_t first_serial;
+    uint64_t last_serial;
+  };
+
+  net::der::GeneralizedTime not_before_;
+  net::der::GeneralizedTime not_after_;
+
+  // Revoked public key hashes.
+  // The values consist of the SHA256 hash of the SubjectPublicKeyInfo.
+  std::set<std::string> revoked_hashes_;
+
+  // Revoked serial number ranges indexed by issuer public key hash.
+  // The key is the SHA256 hash of issuer's SubjectPublicKeyInfo.
+  // The value is a list of revoked serial number ranges.
+  std::unordered_map<std::string, std::vector<SerialNumberRange>>
+      revoked_serial_numbers_;
+  DISALLOW_COPY_AND_ASSIGN(CastCRLImpl);
+};
+
+CastCRLImpl::CastCRLImpl(const TbsCrl& tbs_crl,
+                         const net::der::GeneralizedTime& overall_not_after) {
+  // Parse the validity information.
+  // Assume ConvertTimeSeconds will succeed. Successful call to VerifyCRL
+  // means that these calls were successful.
+  ConvertTimeSeconds(tbs_crl.not_before_seconds(), &not_before_);
+  ConvertTimeSeconds(tbs_crl.not_after_seconds(), &not_after_);
+  if (overall_not_after < not_after_)
+    not_after_ = overall_not_after;
+
+  // Parse the revoked hashes.
+  for (const auto& hash : tbs_crl.revoked_public_key_hashes()) {
+    revoked_hashes_.insert(hash);
+  }
+
+  // Parse the revoked serial ranges.
+  for (const auto& range : tbs_crl.revoked_serial_number_ranges()) {
+    std::string issuer_hash = range.issuer_public_key_hash();
+
+    uint64_t first_serial_number = range.first_serial_number();
+    uint64_t last_serial_number = range.last_serial_number();
+    auto& serial_number_range = revoked_serial_numbers_[issuer_hash];
+    serial_number_range.push_back({first_serial_number, last_serial_number});
+  }
+}
+
+CastCRLImpl::~CastCRLImpl() {}
+
+// Verifies the revocation status of the certificate chain, at the specified
+// time.
+bool CastCRLImpl::CheckRevocation(
+    const net::ParsedCertificateList& trusted_chain,
+    const base::Time& time) const {
+  if (trusted_chain.empty())
+    return false;
+
+  // Check the validity of the CRL at the specified time.
+  net::der::GeneralizedTime verification_time;
+  if (!net::der::EncodeTimeAsGeneralizedTime(time, &verification_time)) {
+    VLOG(2) << "CRL verification time malformed.";
+    return false;
+  }
+  if ((verification_time < not_before_) || (verification_time > not_after_)) {
+    VLOG(2) << "CRL not time-valid. Perform hard fail.";
+    return false;
+  }
+
+  // Check revocation.
+  for (size_t i = 0; i < trusted_chain.size(); ++i) {
+    const auto& parsed_cert = trusted_chain[i];
+    // Calculate the public key's hash to check for revocation.
+    std::string spki_hash =
+        crypto::SHA256HashString(parsed_cert->tbs().spki_tlv.AsString());
+    if (revoked_hashes_.find(spki_hash) != revoked_hashes_.end()) {
+      VLOG(2) << "Public key is revoked.";
+      return false;
+    }
+
+    // Check if the subordinate certificate was revoked by serial number.
+    if (i > 0) {
+      auto issuer_iter = revoked_serial_numbers_.find(spki_hash);
+      if (issuer_iter != revoked_serial_numbers_.end()) {
+        const auto& subordinate = trusted_chain[i - 1];
+        uint64_t serial_number;
+        // Only Google generated device certificates will be revoked by range.
+        // These will always be less than 64 bits in length.
+        if (!net::der::ParseUint64(subordinate->tbs().serial_number,
+                                   &serial_number)) {
+          continue;
+        }
+        for (const auto& revoked_serial : issuer_iter->second) {
+          if (revoked_serial.first_serial <= serial_number &&
+              revoked_serial.last_serial >= serial_number) {
+            VLOG(2) << "Serial number is revoked";
+            return false;
+          }
+        }
+      }
+    }
+  }
+  return true;
+}
+
+}  // namespace
+
+std::unique_ptr<CastCRL> ParseAndVerifyCRL(const std::string& crl_proto,
+                                           const base::Time& time) {
+  CrlBundle crl_bundle;
+  if (!crl_bundle.ParseFromString(crl_proto)) {
+    LOG(ERROR) << "CRL - Binary could not be parsed.";
+    return nullptr;
+  }
+  for (auto const& crl : crl_bundle.crls()) {
+    TbsCrl tbs_crl;
+    if (!tbs_crl.ParseFromString(crl.tbs_crl())) {
+      LOG(WARNING) << "Binary TBS CRL could not be parsed.";
+      continue;
+    }
+    if (tbs_crl.version() != CRL_VERSION_0) {
+      continue;
+    }
+    net::der::GeneralizedTime overall_not_after;
+    if (!VerifyCRL(crl, tbs_crl, time, &overall_not_after)) {
+      LOG(ERROR) << "CRL - Verification failed.";
+      return nullptr;
+    }
+    return base::WrapUnique(new CastCRLImpl(tbs_crl, overall_not_after));
+  }
+  LOG(ERROR) << "No supported version of revocation data.";
+  return nullptr;
+}
+
+bool SetCRLTrustAnchorForTest(const std::string& cert) {
+  scoped_refptr<net::ParsedCertificate> anchor(
+      net::ParsedCertificate::CreateFromCertificateCopy(cert, {}));
+  if (!anchor)
+    return false;
+  CastCRLTrustStore::Get().Clear();
+  CastCRLTrustStore::Get().AddTrustedCertificate(std::move(anchor));
+  return true;
+}
+
+}  // namespace cast_certificate
diff --git a/components/cast_certificate/cast_crl.h b/components/cast_certificate/cast_crl.h
new file mode 100644
index 0000000..e95faf48
--- /dev/null
+++ b/components/cast_certificate/cast_crl.h
@@ -0,0 +1,64 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CAST_CERTIFICATE_CAST_CRL_H_
+#define COMPONENTS_CAST_CERTIFICATE_CAST_CRL_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "net/cert/internal/parsed_certificate.h"
+
+namespace cast_certificate {
+
+// This class represents the CRL information parsed from the binary proto.
+class CastCRL {
+ public:
+  virtual ~CastCRL(){};
+
+  // Verifies the revocation status of a cast device certificate given a chain
+  // of X.509 certificates.
+  //
+  // Inputs:
+  // * |certs| is the verified chain of X.509 certificates:
+  //   * |certs[0]| is the target certificate (i.e. the device certificate).
+  //   * |certs[i]| is the certificate that issued certs[i-1].
+  //   * |certs.back()| is assumed to be a trusted root.
+  //
+  // * |time| is the unix timestamp to use for determining if the certificate
+  //   is revoked.
+  //
+  // Output:
+  // Returns true if no certificate in the chain was revoked.
+  virtual bool CheckRevocation(const net::ParsedCertificateList& certs,
+                               const base::Time& time) const = 0;
+};
+
+// Parses and verifies the CRL used to verify the revocation status of
+// Cast device certificates.
+//
+// Inputs:
+// * |crl_proto| is a serialized cast_certificate.CrlBundle proto.
+// * |time| is the unix timestamp to use for determining if the CRL is valid.
+//
+// Output:
+// Returns the CRL object if success, nullptr otherwise.
+std::unique_ptr<CastCRL> ParseAndVerifyCRL(const std::string& crl_proto,
+                                           const base::Time& time);
+
+// Exposed only for testing, not for use in production code.
+//
+// Replaces trusted root certificates into the CastCRLTrustStore.
+//
+// Output:
+// Returns true if successful, false if nothing is changed.
+bool SetCRLTrustAnchorForTest(const std::string& cert) WARN_UNUSED_RESULT;
+
+}  // namespace cast_certificate
+
+#endif  // COMPONENTS_CAST_CERTIFICATE_CAST_CRL_H_
diff --git a/components/cast_certificate/cast_crl_root_ca_cert_der-inc.h b/components/cast_certificate/cast_crl_root_ca_cert_der-inc.h
new file mode 100644
index 0000000..5463d19
--- /dev/null
+++ b/components/cast_certificate/cast_crl_root_ca_cert_der-inc.h
@@ -0,0 +1,8 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(ryanchung): Add Cast CRL Root here.
+const unsigned char kCastCRLRootCaDer[] = {
+    0x30,
+};
diff --git a/components/cast_certificate/cast_crl_unittest.cc b/components/cast_certificate/cast_crl_unittest.cc
new file mode 100644
index 0000000..d65b3a63
--- /dev/null
+++ b/components/cast_certificate/cast_crl_unittest.cc
@@ -0,0 +1,205 @@
+// 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 "base/time/time.h"
+#include "components/cast_certificate/cast_cert_validator.h"
+#include "components/cast_certificate/cast_cert_validator_test_helpers.h"
+#include "components/cast_certificate/cast_crl.h"
+#include "components/cast_certificate/proto/test_suite.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cast_certificate {
+namespace {
+
+// Converts uint64_t unix timestamp in seconds to base::Time.
+base::Time ConvertUnixTimestampSeconds(uint64_t time) {
+  return base::Time::UnixEpoch() +
+         base::TimeDelta::FromMilliseconds(time * 1000);
+}
+
+// Indicates the expected result of test step's verification.
+enum TestStepResult {
+  RESULT_SUCCESS,
+  RESULT_FAIL,
+};
+
+// Verifies that the provided certificate chain is valid at the specified time
+// and chains up to a trust anchor.
+bool TestVerifyCertificate(TestStepResult expected_result,
+                           const std::vector<std::string>& certificate_chain,
+                           const base::Time& time) {
+  std::unique_ptr<CertVerificationContext> context;
+  CastDeviceCertPolicy policy;
+  bool result = VerifyDeviceCert(certificate_chain, time, &context, &policy,
+                                 nullptr, CRLPolicy::CRL_OPTIONAL);
+  if (expected_result != RESULT_SUCCESS) {
+    EXPECT_FALSE(result);
+    return !result;
+  }
+  EXPECT_TRUE(result);
+  return result;
+}
+
+// Verifies that the provided Cast CRL is signed by a trusted issuer
+// and that the CRL can be parsed successfully.
+// The validity of the CRL is also checked at the specified time.
+bool TestVerifyCRL(TestStepResult expected_result,
+                   const std::string& crl_bundle,
+                   const base::Time& time) {
+  std::unique_ptr<CastCRL> crl = ParseAndVerifyCRL(crl_bundle, time);
+  if (expected_result != RESULT_SUCCESS) {
+    EXPECT_EQ(crl, nullptr);
+    return crl == nullptr;
+  }
+  EXPECT_NE(crl, nullptr);
+  return crl != nullptr;
+}
+
+// Verifies that the certificate chain provided is not revoked according to
+// the provided Cast CRL at |cert_time|.
+// The provided CRL is verified at |crl_time|.
+// If |crl_required| is set, then a valid Cast CRL must be provided.
+// Otherwise, a missing CRL is be ignored.
+bool TestVerifyRevocation(TestStepResult expected_result,
+                          const std::vector<std::string>& certificate_chain,
+                          const std::string& crl_bundle,
+                          const base::Time& crl_time,
+                          const base::Time& cert_time,
+                          bool crl_required) {
+  std::unique_ptr<CastCRL> crl;
+  if (!crl_bundle.empty()) {
+    crl = ParseAndVerifyCRL(crl_bundle, crl_time);
+    EXPECT_NE(crl.get(), nullptr);
+  }
+
+  std::unique_ptr<CertVerificationContext> context;
+  CastDeviceCertPolicy policy;
+  CRLPolicy crl_policy = CRLPolicy::CRL_REQUIRED;
+  if (!crl_required)
+    crl_policy = CRLPolicy::CRL_OPTIONAL;
+  int result = VerifyDeviceCert(certificate_chain, cert_time, &context, &policy,
+                                crl.get(), crl_policy);
+  if (expected_result != RESULT_SUCCESS) {
+    EXPECT_FALSE(result);
+    return !result;
+  }
+  EXPECT_TRUE(result);
+  return result;
+}
+
+// Runs a single test case.
+bool RunTest(const DeviceCertTest& test_case) {
+  bool use_test_trust_anchors = test_case.use_test_trust_anchors();
+  if (use_test_trust_anchors) {
+    const auto crl_test_root =
+        cast_certificate::testing::ReadCertificateChainFromFile(
+            "certificates/cast_crl_test_root_ca.pem");
+    EXPECT_EQ(crl_test_root.size(), 1u);
+    EXPECT_TRUE(SetCRLTrustAnchorForTest(crl_test_root[0]));
+    const auto cast_test_root =
+        cast_certificate::testing::ReadCertificateChainFromFile(
+            "certificates/cast_test_root_ca.pem");
+    EXPECT_EQ(cast_test_root.size(), 1u);
+    EXPECT_TRUE(SetTrustAnchorForTest(cast_test_root[0]));
+  }
+
+  VerificationResult expected_result = test_case.expected_result();
+
+  std::vector<std::string> certificate_chain;
+  for (auto const& cert : test_case.der_cert_path()) {
+    certificate_chain.push_back(cert);
+  }
+
+  base::Time cert_verification_time =
+      ConvertUnixTimestampSeconds(test_case.cert_verification_time_seconds());
+
+  uint64_t crl_verify_time = test_case.crl_verification_time_seconds();
+  base::Time crl_verification_time =
+      ConvertUnixTimestampSeconds(crl_verify_time);
+  if (crl_verify_time == 0)
+    crl_verification_time = cert_verification_time;
+
+  std::string crl_bundle = test_case.crl_bundle();
+  switch (expected_result) {
+    case PATH_VERIFICATION_FAILED:
+      return TestVerifyCertificate(RESULT_FAIL, certificate_chain,
+                                   cert_verification_time);
+      break;
+    case CRL_VERIFICATION_FAILED:
+      return TestVerifyCRL(RESULT_FAIL, crl_bundle, crl_verification_time);
+      break;
+    case REVOCATION_CHECK_FAILED_WITHOUT_CRL:
+      return TestVerifyCertificate(RESULT_SUCCESS, certificate_chain,
+                                   cert_verification_time) &&
+             TestVerifyCRL(RESULT_FAIL, crl_bundle, crl_verification_time) &&
+             TestVerifyRevocation(RESULT_FAIL, certificate_chain, crl_bundle,
+                                  crl_verification_time, cert_verification_time,
+                                  true);
+      break;
+    case REVOCATION_CHECK_FAILED:
+      return TestVerifyCertificate(RESULT_SUCCESS, certificate_chain,
+                                   cert_verification_time) &&
+             TestVerifyCRL(RESULT_SUCCESS, crl_bundle, crl_verification_time) &&
+             TestVerifyRevocation(RESULT_FAIL, certificate_chain, crl_bundle,
+                                  crl_verification_time, cert_verification_time,
+                                  false);
+      break;
+    case SUCCESS:
+      return (crl_bundle.empty() || TestVerifyCRL(RESULT_SUCCESS, crl_bundle,
+                                                  crl_verification_time)) &&
+             TestVerifyCertificate(RESULT_SUCCESS, certificate_chain,
+                                   cert_verification_time) &&
+             TestVerifyRevocation(RESULT_SUCCESS, certificate_chain, crl_bundle,
+                                  crl_verification_time, cert_verification_time,
+                                  !crl_bundle.empty());
+      break;
+    case UNSPECIFIED:
+      return false;
+      break;
+  }
+  return false;
+}
+
+// Parses the provided test suite provided in wire-format proto.
+// Each test contains the inputs and the expected output.
+// To see the description of the test, execute the test.
+// These tests are generated by a test generator in google3.
+void RunTestSuite(const std::string& test_suite_file_name) {
+  std::string testsuite_raw =
+      cast_certificate::testing::ReadTestFileToString(test_suite_file_name);
+  DeviceCertTestSuite test_suite;
+  EXPECT_TRUE(test_suite.ParseFromString(testsuite_raw));
+  uint16_t success = 0;
+  uint16_t failed = 0;
+  std::vector<std::string> failed_tests;
+
+  for (auto const& test_case : test_suite.tests()) {
+    LOG(INFO) << "[ RUN      ] " << test_case.description();
+    bool result = RunTest(test_case);
+    EXPECT_TRUE(result);
+    if (!result) {
+      LOG(INFO) << "[  FAILED  ] " << test_case.description();
+      ++failed;
+      failed_tests.push_back(test_case.description());
+    } else {
+      LOG(INFO) << "[  PASSED  ] " << test_case.description();
+      ++success;
+    }
+  }
+  LOG(INFO) << "[  PASSED  ] " << success << " test(s).";
+  if (failed) {
+    LOG(INFO) << "[  FAILED  ] " << failed << " test(s), listed below:";
+    for (const auto& failed_test : failed_tests) {
+      LOG(INFO) << "[  FAILED  ] " << failed_test;
+    }
+  }
+}
+
+TEST(CastCertificateTest, TestSuite1) {
+  RunTestSuite("testsuite/testsuite1.pb");
+}
+
+}  // namespace
+
+}  // namespace cast_certificate
diff --git a/components/cast_certificate/proto/BUILD.gn b/components/cast_certificate/proto/BUILD.gn
new file mode 100644
index 0000000..9694997
--- /dev/null
+++ b/components/cast_certificate/proto/BUILD.gn
@@ -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.
+
+import("//third_party/protobuf/proto_library.gni")
+if (is_android) {
+  import("//build/config/android/rules.gni")
+}
+
+proto_library("proto") {
+  sources = [
+    "revocation.proto",
+  ]
+}
+
+proto_library("unittest_proto") {
+  sources = [
+    "test_suite.proto",
+  ]
+}
+
+if (is_android) {
+  proto_java_library("proto_java") {
+    proto_path = "//components/cast_certificate/proto"
+
+    sources = [
+      "revocation.proto",
+    ]
+  }
+  proto_java_library("unittest_proto_java") {
+    proto_path = "//components/cast_certificate/proto"
+
+    sources = [
+      "test_suite.proto",
+    ]
+  }
+}
diff --git a/components/cast_certificate/proto/revocation.proto b/components/cast_certificate/proto/revocation.proto
new file mode 100644
index 0000000..d3f9d7f
--- /dev/null
+++ b/components/cast_certificate/proto/revocation.proto
@@ -0,0 +1,60 @@
+// Copyright (c) 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.
+//
+// Data structures related to Cast device certificate revocation infrastructure.
+
+// This proto must be kept in sync with google3.
+
+syntax = "proto2";
+
+package cast_certificate;
+
+option optimize_for = LITE_RUNTIME;
+
+message CrlBundle {
+  // List of supported versions of the same revocation list.
+  repeated Crl crls = 1;
+}
+
+message Crl {
+  // Octet string of serialized TbsCrl protobuf.
+  optional bytes tbs_crl = 1;
+
+  // Binary ASN.1 DER encoding of the signer's certificate.
+  optional bytes signer_cert = 2;
+
+  // Signature calculated over the contents of the tbs_crl field. Signature
+  // algorithm is implied by TbsCrl.version.
+  optional bytes signature = 3;
+}
+
+message TbsCrl {
+  // Version 0 algorithms:
+  //  revoked_public_key_hashes: SHA-256
+  //  SerialNumberRange.issuer_public_key_hash: SHA-256
+  //  Crl.signature: RSA-PKCS1 V1.5 with SHA-256
+  optional uint64 version = 1 [default = 0];
+
+  // Inclusive validity range of the CRL in Unix time.
+  optional uint64 not_before_seconds = 2;
+  optional uint64 not_after_seconds = 3;
+
+  // SPKI hashes of revoked credentials. Hashing algorithm is implied by
+  // TbsCrl.version.
+  repeated bytes revoked_public_key_hashes = 4;
+
+  repeated SerialNumberRange revoked_serial_number_ranges = 5;
+}
+
+message SerialNumberRange {
+  // SPKI hash of the certificate issuer. Hashing algorithm is implied by the
+  // enclosing TbsCrl.version.
+  optional bytes issuer_public_key_hash = 1;
+
+  // Inclusive range of revoked certificate serial numbers. Only certificates
+  // with positive serial numbers that fit within 64 bits can be revoked through
+  // this mechanism.
+  optional uint64 first_serial_number = 2;
+  optional uint64 last_serial_number = 3;
+}
diff --git a/components/cast_certificate/proto/test_suite.proto b/components/cast_certificate/proto/test_suite.proto
new file mode 100644
index 0000000..d6b9a36
--- /dev/null
+++ b/components/cast_certificate/proto/test_suite.proto
@@ -0,0 +1,56 @@
+// Copyright (c) 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.
+
+syntax = "proto2";
+
+package cast_certificate;
+
+option optimize_for = LITE_RUNTIME;
+
+// A suite of test data to exercise Cast device certificate verification and
+// revocation logic.
+message DeviceCertTestSuite {
+  repeated DeviceCertTest tests = 1;
+}
+
+enum VerificationResult {
+  // This should never be encountered in a valid test.
+  UNSPECIFIED = 0;
+  // The device certificate is valid.
+  SUCCESS = 1;
+  // Problem with device certificate or its path.
+  PATH_VERIFICATION_FAILED = 2;
+  // Problem with the CRL.
+  CRL_VERIFICATION_FAILED = 3;
+  // Device certificate or one of the certificates in its path did not pass the
+  // revocation check.
+  REVOCATION_CHECK_FAILED = 4;
+  // No CRL was provided, but revocation check is required, and therefore fails.
+  REVOCATION_CHECK_FAILED_WITHOUT_CRL = 5;
+}
+
+message DeviceCertTest {
+  // Human-readable description of the test.
+  optional string description = 1;
+
+  // Expected result of the certificate verification.
+  optional VerificationResult expected_result = 4;
+
+  // Device certiticate path up to a trusted root.  Root is not included.
+  repeated bytes der_cert_path = 2;
+
+  // Serialized cast.CrlBundle proto if revocation check is required.
+  optional bytes crl_bundle = 3;
+
+  // Time at which to verify the device certificate.
+  optional uint64 cert_verification_time_seconds = 5;
+
+  // Time at which to verify the CRL. It this field is omitted, the CRL is
+  // verified at cert_verification_time_seconds.
+  optional uint64 crl_verification_time_seconds = 6;
+
+  // Chooses between test and production trust anchors for device certificates
+  // and CRLs. Defaults to using the test trust anchors.
+  optional bool use_test_trust_anchors = 7 [default = true];
+}
diff --git a/components/certificate_transparency.gypi b/components/certificate_transparency.gypi
index cfdfe4b..8d1c7b4 100644
--- a/components/certificate_transparency.gypi
+++ b/components/certificate_transparency.gypi
@@ -35,6 +35,6 @@
         'certificate_transparency/tree_state_tracker.h',
         'certificate_transparency/tree_state_tracker.cc',
       ],
-    }
+    },
   ],
 }
diff --git a/components/certificate_transparency/BUILD.gn b/components/certificate_transparency/BUILD.gn
index 1dd31c98..ec807604 100644
--- a/components/certificate_transparency/BUILD.gn
+++ b/components/certificate_transparency/BUILD.gn
@@ -36,9 +36,10 @@
     "ct_policy_manager_unittest.cc",
     "log_dns_client_unittest.cc",
     "log_proof_fetcher_unittest.cc",
+    "mock_log_dns_traffic.cc",
+    "mock_log_dns_traffic.h",
     "single_tree_tracker_unittest.cc",
   ]
-
   deps = [
     ":certificate_transparency",
     "//base/test:test_support",
diff --git a/components/certificate_transparency/log_dns_client.cc b/components/certificate_transparency/log_dns_client.cc
index 76c7494e..5fb28b9 100644
--- a/components/certificate_transparency/log_dns_client.cc
+++ b/components/certificate_transparency/log_dns_client.cc
@@ -13,7 +13,6 @@
 #include "base/strings/string_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
-#include "base/values.h"
 #include "components/base32/base32.h"
 #include "crypto/sha2.h"
 #include "net/base/net_errors.h"
diff --git a/components/certificate_transparency/log_dns_client_unittest.cc b/components/certificate_transparency/log_dns_client_unittest.cc
index 7d25bafe..64223ef5 100644
--- a/components/certificate_transparency/log_dns_client_unittest.cc
+++ b/components/certificate_transparency/log_dns_client_unittest.cc
@@ -4,27 +4,22 @@
 
 #include "components/certificate_transparency/log_dns_client.h"
 
-#include <algorithm>
-#include <numeric>
+#include <memory>
+#include <string>
 #include <utility>
 #include <vector>
 
-#include "base/big_endian.h"
-#include "base/macros.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
-#include "base/sys_byteorder.h"
-#include "base/test/test_timeouts.h"
+#include "components/certificate_transparency/mock_log_dns_traffic.h"
 #include "crypto/sha2.h"
 #include "net/base/net_errors.h"
 #include "net/cert/merkle_audit_proof.h"
-#include "net/cert/merkle_tree_leaf.h"
 #include "net/cert/signed_certificate_timestamp.h"
 #include "net/dns/dns_client.h"
 #include "net/dns/dns_config_service.h"
 #include "net/dns/dns_protocol.h"
 #include "net/log/net_log.h"
-#include "net/socket/socket_test_util.h"
 #include "net/test/gtest_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -43,89 +38,6 @@
     "\x1f\x25\xe1\xca\xba\x4f\xf9\xb8\x27\x24\x83\x0f\xca\x60\xe4\xc2\xbe\xa8"
     "\xc3\xa9\x44\x1c\x27\xb0\xb4\x3e\x6a\x96\x94\xc7\xb8\x04";
 
-// Necessary to expose SetDnsConfig for testing.
-class DnsChangeNotifier : public net::NetworkChangeNotifier {
- public:
-  static void SetInitialDnsConfig(const net::DnsConfig& config) {
-    net::NetworkChangeNotifier::SetInitialDnsConfig(config);
-  }
-
-  static void SetDnsConfig(const net::DnsConfig& config) {
-    net::NetworkChangeNotifier::SetDnsConfig(config);
-  }
-};
-
-// Always return min, to simplify testing.
-// This should result in the DNS query ID always being 0.
-int FakeRandInt(int min, int max) {
-  return min;
-}
-
-std::vector<char> CreateDnsTxtRequest(base::StringPiece qname) {
-  std::string encoded_qname;
-  EXPECT_TRUE(net::DNSDomainFromDot(qname, &encoded_qname));
-
-  const size_t query_section_size = encoded_qname.size() + 4;
-
-  std::vector<char> request(sizeof(net::dns_protocol::Header) +
-                            query_section_size);
-  base::BigEndianWriter writer(request.data(), request.size());
-
-  // Header
-  net::dns_protocol::Header header = {};
-  header.flags = base::HostToNet16(net::dns_protocol::kFlagRD);
-  header.qdcount = base::HostToNet16(1);
-  EXPECT_TRUE(writer.WriteBytes(&header, sizeof(header)));
-  // Query section
-  EXPECT_TRUE(writer.WriteBytes(encoded_qname.data(), encoded_qname.size()));
-  EXPECT_TRUE(writer.WriteU16(net::dns_protocol::kTypeTXT));
-  EXPECT_TRUE(writer.WriteU16(net::dns_protocol::kClassIN));
-  EXPECT_EQ(0, writer.remaining());
-
-  return request;
-}
-
-std::vector<char> CreateDnsTxtResponse(const std::vector<char>& request,
-                                       base::StringPiece answer) {
-  const size_t answers_section_size = 12 + answer.size();
-  constexpr uint32_t ttl = 86400;  // seconds
-
-  std::vector<char> response(request.size() + answers_section_size);
-  std::copy(request.begin(), request.end(), response.begin());
-  // Modify the header
-  net::dns_protocol::Header* header =
-      reinterpret_cast<net::dns_protocol::Header*>(response.data());
-  header->ancount = base::HostToNet16(1);
-  header->flags |= base::HostToNet16(net::dns_protocol::kFlagResponse);
-
-  // Write the answer section
-  base::BigEndianWriter writer(response.data() + request.size(),
-                               response.size() - request.size());
-  EXPECT_TRUE(writer.WriteU8(0xc0));  // qname is a pointer
-  EXPECT_TRUE(writer.WriteU8(
-      sizeof(*header)));  // address of qname (start of query section)
-  EXPECT_TRUE(writer.WriteU16(net::dns_protocol::kTypeTXT));
-  EXPECT_TRUE(writer.WriteU16(net::dns_protocol::kClassIN));
-  EXPECT_TRUE(writer.WriteU32(ttl));
-  EXPECT_TRUE(writer.WriteU16(answer.size()));
-  EXPECT_TRUE(writer.WriteBytes(answer.data(), answer.size()));
-  EXPECT_EQ(0, writer.remaining());
-
-  return response;
-}
-
-std::vector<char> CreateDnsErrorResponse(const std::vector<char>& request,
-                                         uint8_t rcode) {
-  std::vector<char> response(request);
-  // Modify the header
-  net::dns_protocol::Header* header =
-      reinterpret_cast<net::dns_protocol::Header*>(response.data());
-  header->ancount = base::HostToNet16(1);
-  header->flags |= base::HostToNet16(net::dns_protocol::kFlagResponse | rcode);
-
-  return response;
-}
-
 std::vector<std::string> GetSampleAuditProof(size_t length) {
   std::vector<std::string> audit_proof(length);
   // Makes each node of the audit proof different, so that tests are able to
@@ -200,161 +112,18 @@
   base::RunLoop run_loop_;
 };
 
-// A container for all of the data we need to keep alive for a mock socket.
-// This is useful because Mock{Read,Write}, SequencedSocketData and
-// MockClientSocketFactory all do not take ownership of or copy their arguments,
-// so we have to manage the lifetime of those arguments ourselves. Wrapping all
-// of that up in a single class simplifies this.
-class MockSocketData {
- public:
-  // A socket that expects one write and one read operation.
-  MockSocketData(const std::vector<char>& write, const std::vector<char>& read)
-      : expected_write_payload_(write),
-        expected_read_payload_(read),
-        expected_write_(net::SYNCHRONOUS,
-                        expected_write_payload_.data(),
-                        expected_write_payload_.size(),
-                        0),
-        expected_reads_{net::MockRead(net::ASYNC,
-                                      expected_read_payload_.data(),
-                                      expected_read_payload_.size(),
-                                      1),
-                        eof_},
-        socket_data_(expected_reads_, 2, &expected_write_, 1) {}
-
-  // A socket that expects one write and a read error.
-  MockSocketData(const std::vector<char>& write, int net_error)
-      : expected_write_payload_(write),
-        expected_write_(net::SYNCHRONOUS,
-                        expected_write_payload_.data(),
-                        expected_write_payload_.size(),
-                        0),
-        expected_reads_{net::MockRead(net::ASYNC, net_error, 1), eof_},
-        socket_data_(expected_reads_, 2, &expected_write_, 1) {}
-
-  // A socket that expects one write and no response.
-  explicit MockSocketData(const std::vector<char>& write)
-      : expected_write_payload_(write),
-        expected_write_(net::SYNCHRONOUS,
-                        expected_write_payload_.data(),
-                        expected_write_payload_.size(),
-                        0),
-        expected_reads_{net::MockRead(net::SYNCHRONOUS, net::ERR_IO_PENDING, 1),
-                        eof_},
-        socket_data_(expected_reads_, 2, &expected_write_, 1) {}
-
-  void SetWriteMode(net::IoMode mode) { expected_write_.mode = mode; }
-  void SetReadMode(net::IoMode mode) { expected_reads_[0].mode = mode; }
-
-  void AddToFactory(net::MockClientSocketFactory* socket_factory) {
-    socket_factory->AddSocketDataProvider(&socket_data_);
-  }
-
- private:
-  // Prevents read overruns and makes a socket timeout the default behaviour.
-  static const net::MockRead eof_;
-
-  const std::vector<char> expected_write_payload_;
-  const std::vector<char> expected_read_payload_;
-  // Encapsulates the data that is expected to be written to a socket.
-  net::MockWrite expected_write_;
-  // Encapsulates the data/error that should be returned when reading from a
-  // socket. The expected response is followed by |eof_|, to catch further,
-  // unexpected read attempts.
-  net::MockRead expected_reads_[2];
-  net::SequencedSocketData socket_data_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockSocketData);
-};
-
-const net::MockRead MockSocketData::eof_(net::SYNCHRONOUS,
-                                         net::ERR_IO_PENDING,
-                                         2);
-
 class LogDnsClientTest : public ::testing::TestWithParam<net::IoMode> {
  protected:
-  LogDnsClientTest() :
-    network_change_notifier_(net::NetworkChangeNotifier::CreateMock()) {
-    net::DnsConfig dns_config;
-    // Use an invalid nameserver address. This prevents the tests accidentally
-    // sending real DNS queries. The mock sockets don't care that the address
-    // is invalid.
-    dns_config.nameservers.push_back(net::IPEndPoint());
-    // Don't attempt retransmissions - just fail.
-    dns_config.attempts = 1;
-    // This ensures timeouts are long enough for memory tests.
-    dns_config.timeout = TestTimeouts::action_timeout();
-    // Simplify testing - don't require random numbers for the source port.
-    // This means our FakeRandInt function should only be called to get query
-    // IDs.
-    dns_config.randomize_ports = false;
-
-    DnsChangeNotifier::SetInitialDnsConfig(dns_config);
-  }
-
-  void ExpectRequestAndErrorResponse(base::StringPiece qname, uint8_t rcode) {
-    std::vector<char> request = CreateDnsTxtRequest(qname);
-    std::vector<char> response = CreateDnsErrorResponse(request, rcode);
-
-    mock_socket_data_.emplace_back(new MockSocketData(request, response));
-    mock_socket_data_.back()->SetReadMode(GetParam());
-    mock_socket_data_.back()->AddToFactory(&socket_factory_);
-  }
-
-  void ExpectRequestAndSocketError(base::StringPiece qname, int net_error) {
-    std::vector<char> request = CreateDnsTxtRequest(qname);
-
-    mock_socket_data_.emplace_back(new MockSocketData(request, net_error));
-    mock_socket_data_.back()->SetReadMode(GetParam());
-    mock_socket_data_.back()->AddToFactory(&socket_factory_);
-  }
-
-  void ExpectRequestAndTimeout(base::StringPiece qname) {
-    std::vector<char> request = CreateDnsTxtRequest(qname);
-
-    mock_socket_data_.emplace_back(new MockSocketData(request));
-    mock_socket_data_.back()->SetReadMode(GetParam());
-    mock_socket_data_.back()->AddToFactory(&socket_factory_);
-
-    // Speed up timeout tests.
-    net::DnsConfig dns_config;
-    DnsChangeNotifier::GetDnsConfig(&dns_config);
-    dns_config.timeout = TestTimeouts::tiny_timeout();
-    DnsChangeNotifier::SetDnsConfig(dns_config);
-  }
-
-  void ExpectLeafIndexRequestAndResponse(base::StringPiece qname,
-                                         base::StringPiece leaf_index) {
-    // Prepend size to leaf_index to create the query answer (rdata)
-    ASSERT_LE(leaf_index.size(), 0xFFul);  // size must fit into a single byte
-    std::string answer = leaf_index.as_string();
-    answer.insert(answer.begin(), static_cast<char>(leaf_index.size()));
-
-    ExpectRequestAndResponse(qname, answer);
-  }
-
-  void ExpectAuditProofRequestAndResponse(
-      base::StringPiece qname,
-      std::vector<std::string>::const_iterator audit_path_start,
-      std::vector<std::string>::const_iterator audit_path_end) {
-    // Join nodes in the audit path into a single string.
-    std::string proof =
-        std::accumulate(audit_path_start, audit_path_end, std::string());
-
-    // Prepend size to proof to create the query answer (rdata)
-    ASSERT_LE(proof.size(), 0xFFul);  // size must fit into a single byte
-    proof.insert(proof.begin(), static_cast<char>(proof.size()));
-
-    ExpectRequestAndResponse(qname, proof);
+  LogDnsClientTest()
+      : network_change_notifier_(net::NetworkChangeNotifier::CreateMock()) {
+    mock_dns_.SetSocketReadMode(GetParam());
+    mock_dns_.InitializeDnsConfig();
   }
 
   void QueryLeafIndex(base::StringPiece log_domain,
                       base::StringPiece leaf_hash,
                       MockLeafIndexCallback* callback) {
-    std::unique_ptr<net::DnsClient> dns_client = CreateDnsClient();
-    LogDnsClient log_client(std::move(dns_client), net::BoundNetLog());
-    net::NetworkChangeNotifier::NotifyObserversOfInitialDNSConfigReadForTests();
-
+    LogDnsClient log_client(mock_dns_.CreateDnsClient(), net::BoundNetLog());
     log_client.QueryLeafIndex(log_domain, leaf_hash, callback->AsCallback());
     callback->WaitUntilRun();
   }
@@ -363,47 +132,24 @@
                        uint64_t leaf_index,
                        uint64_t tree_size,
                        MockAuditProofCallback* callback) {
-    std::unique_ptr<net::DnsClient> dns_client = CreateDnsClient();
-    LogDnsClient log_client(std::move(dns_client), net::BoundNetLog());
-    net::NetworkChangeNotifier::NotifyObserversOfInitialDNSConfigReadForTests();
-
+    LogDnsClient log_client(mock_dns_.CreateDnsClient(), net::BoundNetLog());
     log_client.QueryAuditProof(log_domain, leaf_index, tree_size,
                                callback->AsCallback());
     callback->WaitUntilRun();
   }
 
-  std::unique_ptr<net::DnsClient> CreateDnsClient() {
-    return net::DnsClient::CreateClientForTesting(nullptr, &socket_factory_,
-                                                  base::Bind(&FakeRandInt));
-  }
-
- private:
-
-  void ExpectRequestAndResponse(base::StringPiece qname,
-                                base::StringPiece answer) {
-    std::vector<char> request = CreateDnsTxtRequest(qname);
-    std::vector<char> response = CreateDnsTxtResponse(request, answer);
-
-    mock_socket_data_.emplace_back(new MockSocketData(request, response));
-    mock_socket_data_.back()->SetReadMode(GetParam());
-    mock_socket_data_.back()->AddToFactory(&socket_factory_);
-  }
-
   // This will be the NetworkChangeNotifier singleton for the duration of the
   // test. It is accessed statically by LogDnsClient.
   std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier_;
   // Queues and handles asynchronous DNS tasks. Indirectly used by LogDnsClient,
   // the underlying net::DnsClient, and NetworkChangeNotifier.
   base::MessageLoopForIO message_loop_;
-  // One MockSocketData for each socket that is created. This corresponds to one
-  // for each DNS request sent.
-  std::vector<std::unique_ptr<MockSocketData>> mock_socket_data_;
-  // Provides as many mock sockets as there are entries in |mock_socket_data_|.
-  net::MockClientSocketFactory socket_factory_;
+  // Allows mock DNS sockets to be setup.
+  MockLogDnsTraffic mock_dns_;
 };
 
 TEST_P(LogDnsClientTest, QueryLeafIndex) {
-  ExpectLeafIndexRequestAndResponse(
+  mock_dns_.ExpectLeafIndexRequestAndResponse(
       "D4S6DSV2J743QJZEQMH4UYHEYK7KRQ5JIQOCPMFUHZVJNFGHXACA.hash.ct.test.",
       "123456");
 
@@ -415,7 +161,7 @@
 }
 
 TEST_P(LogDnsClientTest, QueryLeafIndexReportsThatLogDomainDoesNotExist) {
-  ExpectRequestAndErrorResponse(
+  mock_dns_.ExpectRequestAndErrorResponse(
       "D4S6DSV2J743QJZEQMH4UYHEYK7KRQ5JIQOCPMFUHZVJNFGHXACA.hash.ct.test.",
       net::dns_protocol::kRcodeNXDOMAIN);
 
@@ -427,7 +173,7 @@
 }
 
 TEST_P(LogDnsClientTest, QueryLeafIndexReportsServerFailure) {
-  ExpectRequestAndErrorResponse(
+  mock_dns_.ExpectRequestAndErrorResponse(
       "D4S6DSV2J743QJZEQMH4UYHEYK7KRQ5JIQOCPMFUHZVJNFGHXACA.hash.ct.test.",
       net::dns_protocol::kRcodeSERVFAIL);
 
@@ -439,7 +185,7 @@
 }
 
 TEST_P(LogDnsClientTest, QueryLeafIndexReportsServerRefusal) {
-  ExpectRequestAndErrorResponse(
+  mock_dns_.ExpectRequestAndErrorResponse(
       "D4S6DSV2J743QJZEQMH4UYHEYK7KRQ5JIQOCPMFUHZVJNFGHXACA.hash.ct.test.",
       net::dns_protocol::kRcodeREFUSED);
 
@@ -452,7 +198,7 @@
 
 TEST_P(LogDnsClientTest,
        QueryLeafIndexReportsMalformedResponseIfLeafIndexIsNotNumeric) {
-  ExpectLeafIndexRequestAndResponse(
+  mock_dns_.ExpectLeafIndexRequestAndResponse(
       "D4S6DSV2J743QJZEQMH4UYHEYK7KRQ5JIQOCPMFUHZVJNFGHXACA.hash.ct.test.",
       "foo");
 
@@ -465,7 +211,7 @@
 
 TEST_P(LogDnsClientTest,
        QueryLeafIndexReportsMalformedResponseIfLeafIndexIsFloatingPoint) {
-  ExpectLeafIndexRequestAndResponse(
+  mock_dns_.ExpectLeafIndexRequestAndResponse(
       "D4S6DSV2J743QJZEQMH4UYHEYK7KRQ5JIQOCPMFUHZVJNFGHXACA.hash.ct.test.",
       "123456.0");
 
@@ -478,7 +224,7 @@
 
 TEST_P(LogDnsClientTest,
        QueryLeafIndexReportsMalformedResponseIfLeafIndexIsEmpty) {
-  ExpectLeafIndexRequestAndResponse(
+  mock_dns_.ExpectLeafIndexRequestAndResponse(
       "D4S6DSV2J743QJZEQMH4UYHEYK7KRQ5JIQOCPMFUHZVJNFGHXACA.hash.ct.test.", "");
 
   MockLeafIndexCallback callback;
@@ -490,7 +236,7 @@
 
 TEST_P(LogDnsClientTest,
        QueryLeafIndexReportsMalformedResponseIfLeafIndexHasNonNumericPrefix) {
-  ExpectLeafIndexRequestAndResponse(
+  mock_dns_.ExpectLeafIndexRequestAndResponse(
       "D4S6DSV2J743QJZEQMH4UYHEYK7KRQ5JIQOCPMFUHZVJNFGHXACA.hash.ct.test.",
       "foo123456");
 
@@ -503,7 +249,7 @@
 
 TEST_P(LogDnsClientTest,
        QueryLeafIndexReportsMalformedResponseIfLeafIndexHasNonNumericSuffix) {
-  ExpectLeafIndexRequestAndResponse(
+  mock_dns_.ExpectLeafIndexRequestAndResponse(
       "D4S6DSV2J743QJZEQMH4UYHEYK7KRQ5JIQOCPMFUHZVJNFGHXACA.hash.ct.test.",
       "123456foo");
 
@@ -555,7 +301,7 @@
 }
 
 TEST_P(LogDnsClientTest, QueryLeafIndexReportsSocketError) {
-  ExpectRequestAndSocketError(
+  mock_dns_.ExpectRequestAndSocketError(
       "D4S6DSV2J743QJZEQMH4UYHEYK7KRQ5JIQOCPMFUHZVJNFGHXACA.hash.ct.test.",
       net::ERR_CONNECTION_REFUSED);
 
@@ -567,7 +313,7 @@
 }
 
 TEST_P(LogDnsClientTest, QueryLeafIndexReportsTimeout) {
-  ExpectRequestAndTimeout(
+  mock_dns_.ExpectRequestAndTimeout(
       "D4S6DSV2J743QJZEQMH4UYHEYK7KRQ5JIQOCPMFUHZVJNFGHXACA.hash.ct.test.");
 
   MockLeafIndexCallback callback;
@@ -582,15 +328,15 @@
 
   // It should require 3 queries to collect the entire audit proof, as there is
   // only space for 7 nodes per UDP packet.
-  ExpectAuditProofRequestAndResponse("0.123456.999999.tree.ct.test.",
-                                     audit_proof.begin(),
-                                     audit_proof.begin() + 7);
-  ExpectAuditProofRequestAndResponse("7.123456.999999.tree.ct.test.",
-                                     audit_proof.begin() + 7,
-                                     audit_proof.begin() + 14);
-  ExpectAuditProofRequestAndResponse("14.123456.999999.tree.ct.test.",
-                                     audit_proof.begin() + 14,
-                                     audit_proof.end());
+  mock_dns_.ExpectAuditProofRequestAndResponse("0.123456.999999.tree.ct.test.",
+                                               audit_proof.begin(),
+                                               audit_proof.begin() + 7);
+  mock_dns_.ExpectAuditProofRequestAndResponse("7.123456.999999.tree.ct.test.",
+                                               audit_proof.begin() + 7,
+                                               audit_proof.begin() + 14);
+  mock_dns_.ExpectAuditProofRequestAndResponse("14.123456.999999.tree.ct.test.",
+                                               audit_proof.begin() + 14,
+                                               audit_proof.end());
 
   MockAuditProofCallback callback;
   QueryAuditProof("ct.test", 123456, 999999, &callback);
@@ -606,24 +352,24 @@
   const std::vector<std::string> audit_proof = GetSampleAuditProof(20);
 
   // Make some of the responses contain fewer proof nodes than they can hold.
-  ExpectAuditProofRequestAndResponse("0.123456.999999.tree.ct.test.",
-                                     audit_proof.begin(),
-                                     audit_proof.begin() + 1);
-  ExpectAuditProofRequestAndResponse("1.123456.999999.tree.ct.test.",
-                                     audit_proof.begin() + 1,
-                                     audit_proof.begin() + 3);
-  ExpectAuditProofRequestAndResponse("3.123456.999999.tree.ct.test.",
-                                     audit_proof.begin() + 3,
-                                     audit_proof.begin() + 6);
-  ExpectAuditProofRequestAndResponse("6.123456.999999.tree.ct.test.",
-                                     audit_proof.begin() + 6,
-                                     audit_proof.begin() + 10);
-  ExpectAuditProofRequestAndResponse("10.123456.999999.tree.ct.test.",
-                                     audit_proof.begin() + 10,
-                                     audit_proof.begin() + 13);
-  ExpectAuditProofRequestAndResponse("13.123456.999999.tree.ct.test.",
-                                     audit_proof.begin() + 13,
-                                     audit_proof.end());
+  mock_dns_.ExpectAuditProofRequestAndResponse("0.123456.999999.tree.ct.test.",
+                                               audit_proof.begin(),
+                                               audit_proof.begin() + 1);
+  mock_dns_.ExpectAuditProofRequestAndResponse("1.123456.999999.tree.ct.test.",
+                                               audit_proof.begin() + 1,
+                                               audit_proof.begin() + 3);
+  mock_dns_.ExpectAuditProofRequestAndResponse("3.123456.999999.tree.ct.test.",
+                                               audit_proof.begin() + 3,
+                                               audit_proof.begin() + 6);
+  mock_dns_.ExpectAuditProofRequestAndResponse("6.123456.999999.tree.ct.test.",
+                                               audit_proof.begin() + 6,
+                                               audit_proof.begin() + 10);
+  mock_dns_.ExpectAuditProofRequestAndResponse("10.123456.999999.tree.ct.test.",
+                                               audit_proof.begin() + 10,
+                                               audit_proof.begin() + 13);
+  mock_dns_.ExpectAuditProofRequestAndResponse("13.123456.999999.tree.ct.test.",
+                                               audit_proof.begin() + 13,
+                                               audit_proof.end());
 
   MockAuditProofCallback callback;
   QueryAuditProof("ct.test", 123456, 999999, &callback);
@@ -636,8 +382,8 @@
 }
 
 TEST_P(LogDnsClientTest, QueryAuditProofReportsThatLogDomainDoesNotExist) {
-  ExpectRequestAndErrorResponse("0.123456.999999.tree.ct.test.",
-                                net::dns_protocol::kRcodeNXDOMAIN);
+  mock_dns_.ExpectRequestAndErrorResponse("0.123456.999999.tree.ct.test.",
+                                          net::dns_protocol::kRcodeNXDOMAIN);
 
   MockAuditProofCallback callback;
   QueryAuditProof("ct.test", 123456, 999999, &callback);
@@ -647,8 +393,8 @@
 }
 
 TEST_P(LogDnsClientTest, QueryAuditProofReportsServerFailure) {
-  ExpectRequestAndErrorResponse("0.123456.999999.tree.ct.test.",
-                                net::dns_protocol::kRcodeSERVFAIL);
+  mock_dns_.ExpectRequestAndErrorResponse("0.123456.999999.tree.ct.test.",
+                                          net::dns_protocol::kRcodeSERVFAIL);
 
   MockAuditProofCallback callback;
   QueryAuditProof("ct.test", 123456, 999999, &callback);
@@ -658,8 +404,8 @@
 }
 
 TEST_P(LogDnsClientTest, QueryAuditProofReportsServerRefusal) {
-  ExpectRequestAndErrorResponse("0.123456.999999.tree.ct.test.",
-                                net::dns_protocol::kRcodeREFUSED);
+  mock_dns_.ExpectRequestAndErrorResponse("0.123456.999999.tree.ct.test.",
+                                          net::dns_protocol::kRcodeREFUSED);
 
   MockAuditProofCallback callback;
   QueryAuditProof("ct.test", 123456, 999999, &callback);
@@ -673,8 +419,8 @@
   // node is shorter than a SHA-256 hash (31 vs 32 bytes)
   const std::vector<std::string> audit_proof(1, std::string(31, 'a'));
 
-  ExpectAuditProofRequestAndResponse("0.123456.999999.tree.ct.test.",
-                                     audit_proof.begin(), audit_proof.end());
+  mock_dns_.ExpectAuditProofRequestAndResponse(
+      "0.123456.999999.tree.ct.test.", audit_proof.begin(), audit_proof.end());
 
   MockAuditProofCallback callback;
   QueryAuditProof("ct.test", 123456, 999999, &callback);
@@ -687,8 +433,8 @@
   // node is longer than a SHA-256 hash (33 vs 32 bytes)
   const std::vector<std::string> audit_proof(1, std::string(33, 'a'));
 
-  ExpectAuditProofRequestAndResponse("0.123456.999999.tree.ct.test.",
-                                     audit_proof.begin(), audit_proof.end());
+  mock_dns_.ExpectAuditProofRequestAndResponse(
+      "0.123456.999999.tree.ct.test.", audit_proof.begin(), audit_proof.end());
 
   MockAuditProofCallback callback;
   QueryAuditProof("ct.test", 123456, 999999, &callback);
@@ -700,8 +446,8 @@
 TEST_P(LogDnsClientTest, QueryAuditProofReportsResponseMalformedIfEmpty) {
   const std::vector<std::string> audit_proof;
 
-  ExpectAuditProofRequestAndResponse("0.123456.999999.tree.ct.test.",
-                                     audit_proof.begin(), audit_proof.end());
+  mock_dns_.ExpectAuditProofRequestAndResponse(
+      "0.123456.999999.tree.ct.test.", audit_proof.begin(), audit_proof.end());
 
   MockAuditProofCallback callback;
   QueryAuditProof("ct.test", 123456, 999999, &callback);
@@ -745,8 +491,8 @@
 }
 
 TEST_P(LogDnsClientTest, QueryAuditProofReportsSocketError) {
-  ExpectRequestAndSocketError("0.123456.999999.tree.ct.test.",
-                              net::ERR_CONNECTION_REFUSED);
+  mock_dns_.ExpectRequestAndSocketError("0.123456.999999.tree.ct.test.",
+                                        net::ERR_CONNECTION_REFUSED);
 
   MockAuditProofCallback callback;
   QueryAuditProof("ct.test", 123456, 999999, &callback);
@@ -756,7 +502,7 @@
 }
 
 TEST_P(LogDnsClientTest, QueryAuditProofReportsTimeout) {
-  ExpectRequestAndTimeout("0.123456.999999.tree.ct.test.");
+  mock_dns_.ExpectRequestAndTimeout("0.123456.999999.tree.ct.test.");
 
   MockAuditProofCallback callback;
   QueryAuditProof("ct.test", 123456, 999999, &callback);
@@ -766,7 +512,7 @@
 }
 
 TEST_P(LogDnsClientTest, AdoptsLatestDnsConfigIfValid) {
-  std::unique_ptr<net::DnsClient> tmp = CreateDnsClient();
+  std::unique_ptr<net::DnsClient> tmp = mock_dns_.CreateDnsClient();
   net::DnsClient* dns_client = tmp.get();
   LogDnsClient log_client(std::move(tmp), net::BoundNetLog());
 
@@ -774,7 +520,7 @@
   net::DnsConfig config(*dns_client->GetConfig());
   ASSERT_NE(123, config.attempts);
   config.attempts = 123;
-  DnsChangeNotifier::SetDnsConfig(config);
+  mock_dns_.SetDnsConfig(config);
 
   // Let the DNS config change propogate.
   base::RunLoop().RunUntilIdle();
@@ -782,7 +528,7 @@
 }
 
 TEST_P(LogDnsClientTest, IgnoresLatestDnsConfigIfInvalid) {
-  std::unique_ptr<net::DnsClient> tmp = CreateDnsClient();
+  std::unique_ptr<net::DnsClient> tmp = mock_dns_.CreateDnsClient();
   net::DnsClient* dns_client = tmp.get();
   LogDnsClient log_client(std::move(tmp), net::BoundNetLog());
 
@@ -790,7 +536,7 @@
   net::DnsConfig config(*dns_client->GetConfig());
   ASSERT_THAT(config.nameservers, Not(IsEmpty()));
   config.nameservers.clear();  // Makes config invalid
-  DnsChangeNotifier::SetDnsConfig(config);
+  mock_dns_.SetDnsConfig(config);
 
   // Let the DNS config change propogate.
   base::RunLoop().RunUntilIdle();
diff --git a/components/certificate_transparency/mock_log_dns_traffic.cc b/components/certificate_transparency/mock_log_dns_traffic.cc
new file mode 100644
index 0000000..47922c05b
--- /dev/null
+++ b/components/certificate_transparency/mock_log_dns_traffic.cc
@@ -0,0 +1,270 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/certificate_transparency/mock_log_dns_traffic.h"
+
+#include <algorithm>
+#include <numeric>
+#include <vector>
+
+#include "base/big_endian.h"
+#include "base/sys_byteorder.h"
+#include "base/test/test_timeouts.h"
+#include "net/dns/dns_client.h"
+#include "net/dns/dns_protocol.h"
+#include "net/dns/dns_util.h"
+#include "net/socket/socket_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace certificate_transparency {
+
+namespace {
+
+// Necessary to expose SetDnsConfig for testing.
+class DnsChangeNotifier : public net::NetworkChangeNotifier {
+ public:
+  static void SetInitialDnsConfig(const net::DnsConfig& config) {
+    net::NetworkChangeNotifier::SetInitialDnsConfig(config);
+  }
+
+  static void SetDnsConfig(const net::DnsConfig& config) {
+    net::NetworkChangeNotifier::SetDnsConfig(config);
+  }
+};
+
+// Always return min, to simplify testing.
+// This should result in the DNS query ID always being 0.
+int FakeRandInt(int min, int max) {
+  return min;
+}
+
+std::vector<char> CreateDnsTxtRequest(base::StringPiece qname) {
+  std::string encoded_qname;
+  EXPECT_TRUE(net::DNSDomainFromDot(qname, &encoded_qname));
+
+  // DNS query section:
+  // N bytes - qname
+  // 2 bytes - record type
+  // 2 bytes - record class
+  // Total = N + 4 bytes
+  const size_t query_section_size = encoded_qname.size() + 4;
+
+  std::vector<char> request(sizeof(net::dns_protocol::Header) +
+                            query_section_size);
+  base::BigEndianWriter writer(request.data(), request.size());
+
+  // Header
+  net::dns_protocol::Header header = {};
+  header.flags = base::HostToNet16(net::dns_protocol::kFlagRD);
+  header.qdcount = base::HostToNet16(1);
+  EXPECT_TRUE(writer.WriteBytes(&header, sizeof(header)));
+  // Query section
+  EXPECT_TRUE(writer.WriteBytes(encoded_qname.data(), encoded_qname.size()));
+  EXPECT_TRUE(writer.WriteU16(net::dns_protocol::kTypeTXT));
+  EXPECT_TRUE(writer.WriteU16(net::dns_protocol::kClassIN));
+  EXPECT_EQ(0, writer.remaining());
+
+  return request;
+}
+
+std::vector<char> CreateDnsTxtResponse(const std::vector<char>& request,
+                                       base::StringPiece answer) {
+  // DNS answers section:
+  // 2 bytes - qname pointer
+  // 2 bytes - record type
+  // 2 bytes - record class
+  // 4 bytes - time-to-live
+  // 2 bytes - size of answer (N)
+  // N bytes - answer
+  // Total = 12 + N bytes
+  const size_t answers_section_size = 12 + answer.size();
+  constexpr uint32_t ttl = 86400;  // seconds
+
+  std::vector<char> response(request.size() + answers_section_size);
+  std::copy(request.begin(), request.end(), response.begin());
+  // Modify the header
+  net::dns_protocol::Header* header =
+      reinterpret_cast<net::dns_protocol::Header*>(response.data());
+  header->ancount = base::HostToNet16(1);
+  header->flags |= base::HostToNet16(net::dns_protocol::kFlagResponse);
+
+  // Write the answer section
+  base::BigEndianWriter writer(response.data() + request.size(),
+                               response.size() - request.size());
+  EXPECT_TRUE(writer.WriteU8(0xc0));  // qname is a pointer
+  EXPECT_TRUE(writer.WriteU8(
+      sizeof(*header)));  // address of qname (start of query section)
+  EXPECT_TRUE(writer.WriteU16(net::dns_protocol::kTypeTXT));
+  EXPECT_TRUE(writer.WriteU16(net::dns_protocol::kClassIN));
+  EXPECT_TRUE(writer.WriteU32(ttl));
+  EXPECT_TRUE(writer.WriteU16(answer.size()));
+  EXPECT_TRUE(writer.WriteBytes(answer.data(), answer.size()));
+  EXPECT_EQ(0, writer.remaining());
+
+  return response;
+}
+
+std::vector<char> CreateDnsErrorResponse(const std::vector<char>& request,
+                                         uint8_t rcode) {
+  std::vector<char> response(request);
+  // Modify the header
+  net::dns_protocol::Header* header =
+      reinterpret_cast<net::dns_protocol::Header*>(response.data());
+  header->ancount = base::HostToNet16(1);
+  header->flags |= base::HostToNet16(net::dns_protocol::kFlagResponse | rcode);
+
+  return response;
+}
+
+}  // namespace
+
+namespace internal {
+
+MockSocketData::MockSocketData(const std::vector<char>& write,
+                               const std::vector<char>& read)
+    : expected_write_payload_(write),
+      expected_read_payload_(read),
+      expected_write_(net::SYNCHRONOUS,
+                      expected_write_payload_.data(),
+                      expected_write_payload_.size(),
+                      0),
+      expected_reads_{net::MockRead(net::ASYNC,
+                                    expected_read_payload_.data(),
+                                    expected_read_payload_.size(),
+                                    1),
+                      no_more_data_},
+      socket_data_(expected_reads_, 2, &expected_write_, 1) {}
+
+MockSocketData::MockSocketData(const std::vector<char>& write, int net_error)
+    : expected_write_payload_(write),
+      expected_write_(net::SYNCHRONOUS,
+                      expected_write_payload_.data(),
+                      expected_write_payload_.size(),
+                      0),
+      expected_reads_{net::MockRead(net::ASYNC, net_error, 1), no_more_data_},
+      socket_data_(expected_reads_, 2, &expected_write_, 1) {}
+
+MockSocketData::MockSocketData(const std::vector<char>& write)
+    : expected_write_payload_(write),
+      expected_write_(net::SYNCHRONOUS,
+                      expected_write_payload_.data(),
+                      expected_write_payload_.size(),
+                      0),
+      expected_reads_{net::MockRead(net::SYNCHRONOUS, net::ERR_IO_PENDING, 1),
+                      no_more_data_},
+      socket_data_(expected_reads_, 2, &expected_write_, 1) {}
+
+MockSocketData::~MockSocketData() {}
+
+void MockSocketData::AddToFactory(
+    net::MockClientSocketFactory* socket_factory) {
+  socket_factory->AddSocketDataProvider(&socket_data_);
+}
+
+const net::MockRead MockSocketData::no_more_data_(net::SYNCHRONOUS,
+                                                  net::ERR_IO_PENDING,
+                                                  2);
+
+}  // namespace internal
+
+using internal::MockSocketData;
+
+MockLogDnsTraffic::MockLogDnsTraffic() : socket_read_mode_(net::ASYNC) {}
+
+MockLogDnsTraffic::~MockLogDnsTraffic() {}
+
+void MockLogDnsTraffic::ExpectRequestAndErrorResponse(base::StringPiece qname,
+                                                      uint8_t rcode) {
+  std::vector<char> request = CreateDnsTxtRequest(qname);
+  EmplaceMockSocketData(CreateDnsTxtRequest(qname),
+                        CreateDnsErrorResponse(request, rcode));
+}
+
+void MockLogDnsTraffic::ExpectRequestAndSocketError(base::StringPiece qname,
+                                                    int net_error) {
+  EmplaceMockSocketData(CreateDnsTxtRequest(qname), net_error);
+}
+
+void MockLogDnsTraffic::ExpectRequestAndTimeout(base::StringPiece qname) {
+  EmplaceMockSocketData(CreateDnsTxtRequest(qname));
+
+  // Speed up timeout tests.
+  SetDnsTimeout(TestTimeouts::tiny_timeout());
+}
+
+void MockLogDnsTraffic::ExpectLeafIndexRequestAndResponse(
+    base::StringPiece qname,
+    base::StringPiece leaf_index) {
+  // Prepend size to leaf_index to create the query answer (rdata)
+  ASSERT_LE(leaf_index.size(), 0xFFul);  // size must fit into a single byte
+  std::string answer = leaf_index.as_string();
+  answer.insert(answer.begin(), static_cast<char>(leaf_index.size()));
+
+  ExpectRequestAndResponse(qname, answer);
+}
+
+void MockLogDnsTraffic::ExpectAuditProofRequestAndResponse(
+    base::StringPiece qname,
+    std::vector<std::string>::const_iterator audit_path_start,
+    std::vector<std::string>::const_iterator audit_path_end) {
+  // Join nodes in the audit path into a single string.
+  std::string proof =
+      std::accumulate(audit_path_start, audit_path_end, std::string());
+
+  // Prepend size to proof to create the query answer (rdata)
+  ASSERT_LE(proof.size(), 0xFFul);  // size must fit into a single byte
+  proof.insert(proof.begin(), static_cast<char>(proof.size()));
+
+  ExpectRequestAndResponse(qname, proof);
+}
+
+void MockLogDnsTraffic::InitializeDnsConfig() {
+  net::DnsConfig dns_config;
+  // Use an invalid nameserver address. This prevents the tests accidentally
+  // sending real DNS queries. The mock sockets don't care that the address
+  // is invalid.
+  dns_config.nameservers.push_back(net::IPEndPoint());
+  // Don't attempt retransmissions - just fail.
+  dns_config.attempts = 1;
+  // This ensures timeouts are long enough for memory tests.
+  dns_config.timeout = TestTimeouts::action_timeout();
+  // Simplify testing - don't require random numbers for the source port.
+  // This means our FakeRandInt function should only be called to get query
+  // IDs.
+  dns_config.randomize_ports = false;
+
+  DnsChangeNotifier::SetInitialDnsConfig(dns_config);
+}
+
+void MockLogDnsTraffic::SetDnsConfig(const net::DnsConfig& config) {
+  DnsChangeNotifier::SetDnsConfig(config);
+}
+
+std::unique_ptr<net::DnsClient> MockLogDnsTraffic::CreateDnsClient() {
+  return net::DnsClient::CreateClientForTesting(nullptr, &socket_factory_,
+                                                base::Bind(&FakeRandInt));
+}
+
+void MockLogDnsTraffic::ExpectRequestAndResponse(base::StringPiece qname,
+                                                 base::StringPiece answer) {
+  std::vector<char> request = CreateDnsTxtRequest(qname);
+  EmplaceMockSocketData(request, CreateDnsTxtResponse(request, answer));
+}
+
+template <typename... Args>
+void MockLogDnsTraffic::EmplaceMockSocketData(Args&&... args) {
+  mock_socket_data_.emplace_back(
+      new MockSocketData(std::forward<Args>(args)...));
+  mock_socket_data_.back()->SetReadMode(socket_read_mode_);
+  mock_socket_data_.back()->AddToFactory(&socket_factory_);
+}
+
+void MockLogDnsTraffic::SetDnsTimeout(const base::TimeDelta& timeout) {
+  net::DnsConfig dns_config;
+  DnsChangeNotifier::GetDnsConfig(&dns_config);
+  dns_config.timeout = timeout;
+  DnsChangeNotifier::SetDnsConfig(dns_config);
+}
+
+}  // namespace certificate_transparency
diff --git a/components/certificate_transparency/mock_log_dns_traffic.h b/components/certificate_transparency/mock_log_dns_traffic.h
new file mode 100644
index 0000000..f91e668
--- /dev/null
+++ b/components/certificate_transparency/mock_log_dns_traffic.h
@@ -0,0 +1,162 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CERTIFICATE_TRANSPARENCY_MOCK_LOG_DNS_TRAFFIC_H_
+#define COMPONENTS_CERTIFICATE_TRANSPARENCY_MOCK_LOG_DNS_TRAFFIC_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+#include "net/dns/dns_client.h"
+#include "net/dns/dns_config_service.h"
+#include "net/socket/socket_test_util.h"
+
+namespace certificate_transparency {
+
+namespace internal {
+
+// A container for all of the data we need to keep alive for a mock socket.
+// This is useful because Mock{Read,Write}, SequencedSocketData and
+// MockClientSocketFactory all do not take ownership of or copy their arguments,
+// so we have to manage the lifetime of those arguments ourselves. Wrapping all
+// of that up in a single class simplifies this.
+// This cannot be forward declared because MockLogDnsTraffic has a
+// vector<unique_ptr<MockSocketData>> member, which requires MockSocketData be
+// defined.
+class MockSocketData {
+ public:
+  // A socket that expects one write and one read operation.
+  MockSocketData(const std::vector<char>& write, const std::vector<char>& read);
+  // A socket that expects one write and a read error.
+  MockSocketData(const std::vector<char>& write, int net_error);
+  // A socket that expects one write and no response.
+  explicit MockSocketData(const std::vector<char>& write);
+
+  ~MockSocketData();
+
+  void SetWriteMode(net::IoMode mode) { expected_write_.mode = mode; }
+  void SetReadMode(net::IoMode mode) { expected_reads_[0].mode = mode; }
+
+  void AddToFactory(net::MockClientSocketFactory* socket_factory);
+
+ private:
+  // Prevents read overruns and makes a socket timeout the default behaviour.
+  static const net::MockRead no_more_data_;
+
+  // This class only supports one write and one read, so just need to store one
+  // payload each.
+  const std::vector<char> expected_write_payload_;
+  const std::vector<char> expected_read_payload_;
+  // Encapsulates the data that is expected to be written to a socket.
+  net::MockWrite expected_write_;
+  // Encapsulates the data/error that should be returned when reading from a
+  // socket. The second "expected" read is always |no_more_data_|, which
+  // causes the socket read to hang until it times out. This results in better
+  // test failure messages (rather than a CHECK-fail due to a socket read
+  // overrunning the MockRead array) and behaviour more like a real socket when
+  // an unexpected second socket read occurs.
+  net::MockRead expected_reads_[2];
+  // Holds pointers to |expected_write_| and |expected_reads_|. This is what is
+  // added to net::MockClientSocketFactory to prepare a mock socket.
+  net::SequencedSocketData socket_data_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockSocketData);
+};
+
+}  // namespace internal
+
+// Mocks DNS requests and responses for a Certificate Transparency (CT) log.
+// This is implemented using mock sockets. Call the CreateDnsClient() method to
+// get a net::DnsClient wired up to these mock sockets.
+// The Expect*() methods must be called from within a GTest test case.
+class MockLogDnsTraffic {
+ public:
+  MockLogDnsTraffic();
+  ~MockLogDnsTraffic();
+
+  // Expect a CT DNS request for the domain |qname|.
+  // Such a request will receive a DNS response indicating that the error
+  // specified by |rcode| occurred. See RFC1035, Section 4.1.1 for |rcode|
+  // values.
+  void ExpectRequestAndErrorResponse(base::StringPiece qname, uint8_t rcode);
+  // Expect a CT DNS request for the domain |qname|.
+  // Such a request will trigger a socket error of type |net_error|.
+  // |net_error| can be any net:Error value.
+  void ExpectRequestAndSocketError(base::StringPiece qname, int net_error);
+  // Expect a CT DNS request for the domain |qname|.
+  // Such a request will timeout.
+  // This will reduce the DNS timeout to minimize test duration.
+  void ExpectRequestAndTimeout(base::StringPiece qname);
+  // Expect a CT DNS request for the domain |qname|.
+  // Such a request will receive a DNS response containing |leaf_index|.
+  // A description of such a request and response can be seen here:
+  // https://github.com/google/certificate-transparency-rfcs/blob/c8844de6bd0b5d3d16bac79865e6edef533d760b/dns/draft-ct-over-dns.md#hash-query-hashquery
+  void ExpectLeafIndexRequestAndResponse(base::StringPiece qname,
+                                         base::StringPiece leaf_index);
+  // Expect a CT DNS request for the domain |qname|.
+  // Such a request will receive a DNS response containing the inclusion proof
+  // nodes between |audit_path_start| and |audit_path_end|.
+  // A description of such a request and response can be seen here:
+  // https://github.com/google/certificate-transparency-rfcs/blob/c8844de6bd0b5d3d16bac79865e6edef533d760b/dns/draft-ct-over-dns.md#tree-query-treequery
+  void ExpectAuditProofRequestAndResponse(
+      base::StringPiece qname,
+      std::vector<std::string>::const_iterator audit_path_start,
+      std::vector<std::string>::const_iterator audit_path_end);
+
+  // Sets the initial DNS config appropriate for testing.
+  // Requires that net::NetworkChangeNotifier is initialized first.
+  // The DNS config is propogated to NetworkChangeNotifier::DNSObservers
+  // asynchronously.
+  void InitializeDnsConfig();
+
+  // Sets the DNS config to |config|.
+  // Requires that net::NetworkChangeNotifier is initialized first.
+  // The DNS config is propogated to NetworkChangeNotifier::DNSObservers
+  // asynchronously.
+  void SetDnsConfig(const net::DnsConfig& config);
+
+  // Creates a DNS client that uses mock sockets.
+  // It is this DNS client that the expectations will be tested against.
+  std::unique_ptr<net::DnsClient> CreateDnsClient();
+
+  // Sets whether mock reads should complete synchronously or asynchronously.
+  void SetSocketReadMode(net::IoMode read_mode) {
+    socket_read_mode_ = read_mode;
+  }
+
+ private:
+  // Expect A CT DNS request for the domain |qname|.
+  // Such a request will receive a DNS response containing |answer|.
+  void ExpectRequestAndResponse(base::StringPiece qname,
+                                base::StringPiece answer);
+
+  // Constructs MockSocketData from |args| and adds it to |socket_factory_|.
+  template <typename... Args>
+  void EmplaceMockSocketData(Args&&... args);
+
+  // Sets the timeout used for DNS queries.
+  // Requires that net::NetworkChangeNotifier is initialized first.
+  // The new timeout is propogated to NetworkChangeNotifier::DNSObservers
+  // asynchronously.
+  void SetDnsTimeout(const base::TimeDelta& timeout);
+
+  // One MockSocketData for each socket that is created. This corresponds to one
+  // for each DNS request sent.
+  std::vector<std::unique_ptr<internal::MockSocketData>> mock_socket_data_;
+  // Provides as many mock sockets as there are entries in |mock_socket_data_|.
+  net::MockClientSocketFactory socket_factory_;
+  // Controls whether mock socket reads are asynchronous.
+  net::IoMode socket_read_mode_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockLogDnsTraffic);
+};
+
+}  // namespace certificate_transparency
+
+#endif  // COMPONENTS_CERTIFICATE_TRANSPARENCY_MOCK_LOG_DNS_TRAFFIC_H_
diff --git a/components/certificate_transparency/single_tree_tracker.cc b/components/certificate_transparency/single_tree_tracker.cc
index 7946208ca..adaf04d4 100644
--- a/components/certificate_transparency/single_tree_tracker.cc
+++ b/components/certificate_transparency/single_tree_tracker.cc
@@ -6,12 +6,45 @@
 
 #include <utility>
 
+#include "base/metrics/histogram_macros.h"
 #include "net/cert/ct_log_verifier.h"
 #include "net/cert/signed_certificate_timestamp.h"
 #include "net/cert/x509_certificate.h"
 
 using net::ct::SignedTreeHead;
 
+namespace {
+
+// Enum indicating whether an SCT can be checked for inclusion and if not,
+// the reason it cannot.
+//
+// Note: The numeric values are used within a histogram and should not change
+// or be re-assigned.
+enum SCTCanBeCheckedForInclusion {
+  // If the SingleTreeTracker does not have a valid STH, then a valid STH is
+  // first required to evaluate whether the SCT can be checked for inclusion
+  // or not.
+  VALID_STH_REQUIRED = 0,
+  // If the STH does not cover the SCT (the timestamp in the SCT is greater than
+  // MMD + timestamp in the STH), then a newer STH is needed.
+  NEWER_STH_REQUIRED = 1,
+  // When an SCT is observed, if the SingleTreeTracker instance has a valid STH
+  // and the STH covers the SCT (the timestamp in the SCT is less than MMD +
+  // timestamp in the STH), then it can be checked for inclusion.
+  CAN_BE_CHECKED = 2,
+  SCT_CAN_BE_CHECKED_MAX
+};
+
+// Measure how often clients encounter very new SCTs, by measuring whether an
+// SCT can be checked for inclusion upon first observation.
+void LogCanBeCheckedForInclusionToUMA(
+    SCTCanBeCheckedForInclusion can_be_checked) {
+  UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.CanInclusionCheckSCT",
+                            can_be_checked, SCT_CAN_BE_CHECKED_MAX);
+}
+
+}  // namespace
+
 namespace certificate_transparency {
 
 SingleTreeTracker::SingleTreeTracker(
@@ -34,15 +67,20 @@
   if (verified_sth_.timestamp.is_null() ||
       (verified_sth_.timestamp <
        (sct->timestamp + base::TimeDelta::FromHours(24)))) {
-    // TODO(eranm): UMA - how often SCTs have to wait for a newer STH for
-    // inclusion check.
     entries_status_.insert(
         std::make_pair(sct->timestamp, SCT_PENDING_NEWER_STH));
+
+    if (!verified_sth_.timestamp.is_null()) {
+      LogCanBeCheckedForInclusionToUMA(NEWER_STH_REQUIRED);
+    } else {
+      LogCanBeCheckedForInclusionToUMA(VALID_STH_REQUIRED);
+    }
+
     return;
   }
 
+  LogCanBeCheckedForInclusionToUMA(CAN_BE_CHECKED);
   // TODO(eranm): Check inclusion here.
-  // TODO(eranm): UMA - how often inclusion can be checked immediately.
   entries_status_.insert(
       std::make_pair(sct->timestamp, SCT_PENDING_INCLUSION_CHECK));
 }
diff --git a/components/certificate_transparency/single_tree_tracker_unittest.cc b/components/certificate_transparency/single_tree_tracker_unittest.cc
index cbdfe61..8bcb171 100644
--- a/components/certificate_transparency/single_tree_tracker_unittest.cc
+++ b/components/certificate_transparency/single_tree_tracker_unittest.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/strings/string_piece.h"
+#include "base/test/histogram_tester.h"
 #include "net/cert/ct_log_verifier.h"
 #include "net/cert/ct_serialization.h"
 #include "net/cert/signed_certificate_timestamp.h"
@@ -20,6 +21,9 @@
 
 namespace {
 
+const char kCanCheckForInclusionHistogramName[] =
+    "Net.CertificateTransparency.CanInclusionCheckSCT";
+
 bool GetOldSignedTreeHead(net::ct::SignedTreeHead* sth) {
   sth->version = net::ct::SignedTreeHead::V1;
   sth->timestamp = base::Time::UnixEpoch() +
@@ -75,6 +79,7 @@
 // Test that an SCT is classified as pending for a newer STH if the
 // SingleTreeTracker has not seen any STHs so far.
 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTNoSTH) {
+  base::HistogramTester histograms;
   // First make sure the SCT has not been observed at all.
   EXPECT_EQ(
       SingleTreeTracker::SCT_NOT_OBSERVED,
@@ -87,11 +92,16 @@
   EXPECT_EQ(
       SingleTreeTracker::SCT_PENDING_NEWER_STH,
       tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+
+  // Expect logging of a value indicating a valid STH is required.
+  histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
+  histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 0, 1);
 }
 
 // Test that an SCT is classified as pending an inclusion check if the
 // SingleTreeTracker has a fresh-enough STH to check inclusion against.
 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTWithRecentSTH) {
+  base::HistogramTester histograms;
   // Provide an STH to the tree_tracker_.
   net::ct::SignedTreeHead sth;
   net::ct::GetSampleSignedTreeHead(&sth);
@@ -110,18 +120,26 @@
   EXPECT_EQ(
       SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
       tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+
+  // Exactly one value should be logged, indicating the SCT can be checked for
+  // inclusion, as |tree_tracker_| did have a valid STH when it was notified
+  // of a new SCT.
+  histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
+  histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 2, 1);
 }
 
 // Test that the SingleTreeTracker correctly queues verified SCTs for inclusion
 // checking such that, upon receiving a fresh STH, it changes the SCT's status
 // from pending newer STH to pending inclusion check.
 TEST_F(SingleTreeTrackerTest, CorrectlyUpdatesSCTStatusOnNewSTH) {
+  base::HistogramTester histograms;
   // Report an observed SCT and make sure it's in the pending newer STH
   // state.
   tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
   EXPECT_EQ(
       SingleTreeTracker::SCT_PENDING_NEWER_STH,
       tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
 
   // Provide with a fresh STH
   net::ct::SignedTreeHead sth;
@@ -132,6 +150,10 @@
   EXPECT_EQ(
       SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
       tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  // Check that no additional UMA was logged for this case as the histogram is
+  // only supposed to measure the state of newly-observed SCTs, not pending
+  // ones.
+  histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
 }
 
 // Test that the SingleTreeTracker does not change an SCT's status if an STH
@@ -155,4 +177,25 @@
       tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
 }
 
+// Test that the SingleTreeTracker correctly logs that an SCT is pending a new
+// STH, when it has a valid STH,  but the observed SCT is not covered by the
+// STH.
+TEST_F(SingleTreeTrackerTest, LogsUMAForNewSCTAndOldSTH) {
+  base::HistogramTester histograms;
+  // Provide an old STH for the same log.
+  net::ct::SignedTreeHead sth;
+  GetOldSignedTreeHead(&sth);
+  tree_tracker_->NewSTHObserved(sth);
+
+  histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 0);
+
+  // Notify of an SCT and make sure it's in the 'pending newer STH' state.
+  tree_tracker_->OnSCTVerified(chain_.get(), cert_sct_.get());
+
+  // Exactly one value should be logged, indicating the SCT cannot be checked
+  // for inclusion as the STH is too old.
+  histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
+  histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 1, 1);
+}
+
 }  // namespace certificate_transparency
diff --git a/components/components.gyp b/components/components.gyp
index 29d3643..3fd4d3f 100644
--- a/components/components.gyp
+++ b/components/components.gyp
@@ -83,6 +83,7 @@
     'security_state.gypi',
     'sessions.gypi',
     'signin.gypi',
+    'spellcheck.gypi',
     'ssl_config.gypi',
     'ssl_errors.gypi',
     'subresource_filter.gypi',
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index 98335edb..199b225 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -107,9 +107,8 @@
       'captive_portal/captive_portal_detector_unittest.cc',
     ],
     'cast_certificate_unittest_sources': [
-      'cast_certificate/cast_cert_validator_test_helpers.cc',
-      'cast_certificate/cast_cert_validator_test_helpers.h',
       'cast_certificate/cast_cert_validator_unittest.cc',
+      'cast_certificate/cast_crl_unittest.cc',
     ],
     'certificate_reporting_unittest_sources': [
       'certificate_reporting/error_report_unittest.cc',
@@ -119,6 +118,8 @@
       'certificate_transparency/ct_policy_manager_unittest.cc',
       'certificate_transparency/log_dns_client_unittest.cc',
       'certificate_transparency/log_proof_fetcher_unittest.cc',
+      'certificate_transparency/mock_log_dns_traffic.cc',
+      'certificate_transparency/mock_log_dns_traffic.h',
       'certificate_transparency/single_tree_tracker_unittest.cc',
     ],
     'child_trace_message_filter_unittest_sources': [
@@ -352,6 +353,7 @@
       'login/screens/screen_context_unittest.cc',
     ],
     'memory_coordinator_unittest_sources': [
+      'memory_coordinator/browser/memory_coordinator_unittest.cc',
       'memory_coordinator/child/child_memory_coordinator_impl_unittest.cc',
     ],
     'memory_pressure_unittest_sources': [
@@ -1154,6 +1156,8 @@
         'components.gyp:bubble',
         'components.gyp:captive_portal_test_support',
         'components.gyp:cast_certificate',
+        'components.gyp:cast_certificate_test_proto',
+        'components.gyp:cast_certificate_test_support',
         'components.gyp:certificate_reporting',
         'components.gyp:cloud_devices_common',
         'components.gyp:component_updater',
@@ -1362,6 +1366,7 @@
             'components.gyp:guest_view_test_support',
             'components.gyp:history_content_browser',
             'components.gyp:keyed_service_content',
+            'components.gyp:memory_coordinator_browser',
             'components.gyp:memory_coordinator_child',
             'components.gyp:metrics_gpu',
             'components.gyp:navigation_interception',
diff --git a/components/cronet.gypi b/components/cronet.gypi
index 1e84f739..5fead38 100644
--- a/components/cronet.gypi
+++ b/components/cronet.gypi
@@ -10,13 +10,13 @@
           'target_name': 'cronet_jni_headers',
           'type': 'none',
           'sources': [
-            'cronet/android/java/src/org/chromium/net/CronetBidirectionalStream.java',
-            'cronet/android/java/src/org/chromium/net/CronetLibraryLoader.java',
-            'cronet/android/java/src/org/chromium/net/CronetUploadDataStream.java',
-            'cronet/android/java/src/org/chromium/net/CronetUrlRequest.java',
-            'cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java',
-            'cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java',
-            'cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java',
+            'cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java',
+            'cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java',
+            'cronet/android/java/src/org/chromium/net/impl/CronetUploadDataStream.java',
+            'cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java',
+            'cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java',
+            'cronet/android/java/src/org/chromium/net/impl/ChromiumUrlRequest.java',
+            'cronet/android/java/src/org/chromium/net/impl/ChromiumUrlRequestContext.java',
           ],
           'variables': {
             'jni_gen_package': 'cronet',
@@ -93,14 +93,14 @@
           'includes': [ '../build/android/java_cpp_template.gypi' ],
         },
         {
-          'target_name': 'cronet_version',
+          'target_name': 'cronet_api_version',
           'type': 'none',
           'variables': {
             'lastchange_path': '<(DEPTH)/build/util/LASTCHANGE',
             'version_py_path': '<(DEPTH)/build/util/version.py',
             'version_path': '<(DEPTH)/chrome/VERSION',
-            'template_input_path': 'cronet/android/java/src/org/chromium/net/Version.template',
-            'output_path': '<(SHARED_INTERMEDIATE_DIR)/templates/<(_target_name)/org/chromium/cronet/Version.java',
+            'template_input_path': 'cronet/android/api/src/org/chromium/net/ApiVersion.template',
+            'output_path': '<(SHARED_INTERMEDIATE_DIR)/templates/<(_target_name)/org/chromium/net/ApiVersion.java',
           },
           'direct_dependent_settings': {
             'variables': {
@@ -117,7 +117,7 @@
           },
           'actions': [
             {
-              'action_name': 'cronet_version',
+              'action_name': 'cronet_api_version',
               'inputs': [
                 '<(template_input_path)',
                 '<(version_path)',
@@ -134,7 +134,53 @@
                 '<(template_input_path)',
                 '<(output_path)',
               ],
-              'message': 'Generating version information',
+              'message': 'Generating API version information',
+            },
+          ],
+        },
+        {
+          'target_name': 'cronet_impl_version',
+          'type': 'none',
+          'variables': {
+            'lastchange_path': '<(DEPTH)/build/util/LASTCHANGE',
+            'version_py_path': '<(DEPTH)/build/util/version.py',
+            'version_path': '<(DEPTH)/chrome/VERSION',
+            'template_input_path': 'cronet/android/java/src/org/chromium/net/impl/ImplVersion.template',
+            'output_path': '<(SHARED_INTERMEDIATE_DIR)/templates/<(_target_name)/org/chromium/net/impl/ImplVersion.java',
+          },
+          'direct_dependent_settings': {
+            'variables': {
+              # Ensure that the output directory is used in the class path
+              # when building targets that depend on this one.
+              'generated_src_dirs': [
+                '<(SHARED_INTERMEDIATE_DIR)/templates/<(_target_name)',
+              ],
+              # Ensure dependents are rebuilt when the generated Java file changes.
+              'additional_input_paths': [
+                '<(output_path)',
+              ],
+            },
+          },
+          'actions': [
+            {
+              'action_name': 'cronet_impl_version',
+              'inputs': [
+                '<(template_input_path)',
+                '<(version_path)',
+                '<(lastchange_path)',
+              ],
+              'outputs': [
+                '<(output_path)',
+              ],
+              'action': [
+                'python',
+                '<(version_py_path)',
+                '-f', '<(version_path)',
+                '-f', '<(lastchange_path)',
+                '<(template_input_path)',
+                '<(output_path)',
+              ],
+              'message': 'Generating impl version information',
             },
           ],
         },
@@ -247,7 +293,7 @@
           'dependencies': [
             'http_cache_type_java',
             'url_request_error_java',
-            'cronet_version',
+            'cronet_api_version',
             'load_states_list',
             'network_quality_observation_source_java',
             '../third_party/android_tools/android_tools.gyp:android_support_v13_java',
@@ -265,6 +311,7 @@
           'dependencies': [
             '../base/base.gyp:base',
             'cronet_api',
+            'cronet_impl_version',
             'chromium_url_request_java',
             'libcronet',
             'net_request_priority_java',
@@ -284,6 +331,7 @@
               '**/CronetUploadDataStream.java',
               '**/CronetUrlRequest.java',
               '**/CronetUrlRequestContext.java',
+              '**/ImplVersion.java',
               '**/RequestPriority.java',
               '**/urlconnection/CronetBufferedOutputStream.java',
               '**/urlconnection/CronetChunkedOutputStream.java',
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index 6992d61..170bb4c 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -19,13 +19,13 @@
 
 generate_jni("cronet_jni_headers") {
   sources = [
-    "java/src/org/chromium/net/ChromiumUrlRequest.java",
-    "java/src/org/chromium/net/ChromiumUrlRequestContext.java",
-    "java/src/org/chromium/net/CronetBidirectionalStream.java",
-    "java/src/org/chromium/net/CronetLibraryLoader.java",
-    "java/src/org/chromium/net/CronetUploadDataStream.java",
-    "java/src/org/chromium/net/CronetUrlRequest.java",
-    "java/src/org/chromium/net/CronetUrlRequestContext.java",
+    "java/src/org/chromium/net/impl/ChromiumUrlRequest.java",
+    "java/src/org/chromium/net/impl/ChromiumUrlRequestContext.java",
+    "java/src/org/chromium/net/impl/CronetBidirectionalStream.java",
+    "java/src/org/chromium/net/impl/CronetLibraryLoader.java",
+    "java/src/org/chromium/net/impl/CronetUploadDataStream.java",
+    "java/src/org/chromium/net/impl/CronetUrlRequest.java",
+    "java/src/org/chromium/net/impl/CronetUrlRequestContext.java",
   ]
   jni_package = "cronet"
 }
@@ -76,23 +76,45 @@
   package_name = "org/chromium/net"
 }
 
-_generated_version_java_dir = "$target_gen_dir/templates/cronet_version_java"
-_generated_version_java =
-    "$_generated_version_java_dir/org/chromium/net/Version.java"
+_generated_api_version_java_dir =
+    "$target_gen_dir/templates/cronet_api_version_java"
+_generated_api_version_java =
+    "$_generated_api_version_java_dir/org/chromium/net/ApiVersion.java"
 
-process_version("cronet_version_java") {
-  template_file = "java/src/org/chromium/net/Version.template"
-  output = _generated_version_java
+process_version("cronet_api_version_java") {
+  template_file = "api/src/org/chromium/net/ApiVersion.template"
+  output = _generated_api_version_java
 }
 
-zip("cronet_version_srcjar") {
+zip("cronet_api_version_srcjar") {
   inputs = [
-    _generated_version_java,
+    _generated_api_version_java,
   ]
   output = "$target_gen_dir/$target_name.srcjar"
-  base_dir = _generated_version_java_dir
+  base_dir = _generated_api_version_java_dir
   deps = [
-    ":cronet_version_java",
+    ":cronet_api_version_java",
+  ]
+}
+
+_generated_impl_version_java_dir =
+    "$target_gen_dir/templates/cronet_impl_version_java"
+_generated_impl_version_java =
+    "$_generated_impl_version_java_dir/org/chromium/net/impl/ImplVersion.java"
+
+process_version("cronet_impl_version_java") {
+  template_file = "java/src/org/chromium/net/impl/ImplVersion.template"
+  output = _generated_impl_version_java
+}
+
+zip("cronet_impl_version_srcjar") {
+  inputs = [
+    _generated_impl_version_java,
+  ]
+  output = "$target_gen_dir/$target_name.srcjar"
+  base_dir = _generated_impl_version_java_dir
+  deps = [
+    ":cronet_impl_version_java",
   ]
 }
 
@@ -278,7 +300,7 @@
   ]
 
   srcjar_deps = [
-    ":cronet_version_srcjar",
+    ":cronet_api_version_srcjar",
     ":http_cache_type_java",
     ":url_request_error_java",
     ":load_states_list",
@@ -290,14 +312,14 @@
 
 android_library("cronet_java") {
   java_files = [
-    "java/src/org/chromium/net/ChromiumUrlRequest.java",
-    "java/src/org/chromium/net/ChromiumUrlRequestContext.java",
-    "java/src/org/chromium/net/ChromiumUrlRequestFactory.java",
-    "java/src/org/chromium/net/CronetBidirectionalStream.java",
-    "java/src/org/chromium/net/CronetLibraryLoader.java",
-    "java/src/org/chromium/net/CronetUploadDataStream.java",
-    "java/src/org/chromium/net/CronetUrlRequest.java",
-    "java/src/org/chromium/net/CronetUrlRequestContext.java",
+    "java/src/org/chromium/net/impl/ChromiumUrlRequest.java",
+    "java/src/org/chromium/net/impl/ChromiumUrlRequestContext.java",
+    "java/src/org/chromium/net/impl/ChromiumUrlRequestFactory.java",
+    "java/src/org/chromium/net/impl/CronetBidirectionalStream.java",
+    "java/src/org/chromium/net/impl/CronetLibraryLoader.java",
+    "java/src/org/chromium/net/impl/CronetUploadDataStream.java",
+    "java/src/org/chromium/net/impl/CronetUrlRequest.java",
+    "java/src/org/chromium/net/impl/CronetUrlRequestContext.java",
     "java/src/org/chromium/net/urlconnection/CronetBufferedOutputStream.java",
     "java/src/org/chromium/net/urlconnection/CronetChunkedOutputStream.java",
     "java/src/org/chromium/net/urlconnection/CronetFixedModeOutputStream.java",
@@ -318,6 +340,7 @@
   ]
 
   srcjar_deps = [
+    ":cronet_impl_version_srcjar",
     ":chromium_url_request_java",
     ":net_request_priority_java",
   ]
diff --git a/components/cronet/android/java/src/org/chromium/net/Version.template b/components/cronet/android/api/src/org/chromium/net/ApiVersion.template
similarity index 94%
rename from components/cronet/android/java/src/org/chromium/net/Version.template
rename to components/cronet/android/api/src/org/chromium/net/ApiVersion.template
index df25c2f..f404b81 100644
--- a/components/cronet/android/java/src/org/chromium/net/Version.template
+++ b/components/cronet/android/api/src/org/chromium/net/ApiVersion.template
@@ -5,7 +5,7 @@
 package org.chromium.net;
 
 // Version based on chrome/VERSION.
-public class Version {
+public class ApiVersion {
     public static final String CRONET_VERSION = "@MAJOR@.@MINOR@.@BUILD@.@PATCH@";
     public static final String LAST_CHANGE = "@LASTCHANGE@";
 
diff --git a/components/cronet/android/api/src/org/chromium/net/CronetEngine.java b/components/cronet/android/api/src/org/chromium/net/CronetEngine.java
index 17b5b7d..a5752d8e 100644
--- a/components/cronet/android/api/src/org/chromium/net/CronetEngine.java
+++ b/components/cronet/android/api/src/org/chromium/net/CronetEngine.java
@@ -61,14 +61,17 @@
             public abstract void loadLibrary(String libName);
         }
 
-        // A hint that a host supports QUIC.
-        static class QuicHint {
+        /**
+         * A hint that a host supports QUIC.
+         * @hide only used by internal implementation.
+         */
+        public static class QuicHint {
             // The host.
-            final String mHost;
+            public final String mHost;
             // Port of the server that supports QUIC.
-            final int mPort;
+            public final int mPort;
             // Alternate protocol port.
-            final int mAlternatePort;
+            public final int mAlternatePort;
 
             QuicHint(String host, int port, int alternatePort) {
                 mHost = host;
@@ -77,16 +80,19 @@
             }
         }
 
-        // A public key pin.
-        static class Pkp {
+        /**
+         * A public key pin.
+         * @hide only used by internal implementation.
+         */
+        public static class Pkp {
             // Host to pin for.
-            final String mHost;
+            public final String mHost;
             // Array of SHA-256 hashes of keys.
-            final byte[][] mHashes;
+            public final byte[][] mHashes;
             // Should pin apply to subdomains?
-            final boolean mIncludeSubdomains;
+            public final boolean mIncludeSubdomains;
             // When the pin expires.
-            final Date mExpirationDate;
+            public final Date mExpirationDate;
 
             Pkp(String host, byte[][] hashes, boolean includeSubdomains, Date expirationDate) {
                 mHost = host;
@@ -164,7 +170,10 @@
             return this;
         }
 
-        String getUserAgent() {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public String getUserAgent() {
             return mUserAgent;
         }
 
@@ -188,7 +197,10 @@
             return this;
         }
 
-        String storagePath() {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public String storagePath() {
             return mStoragePath;
         }
 
@@ -216,7 +228,10 @@
             return this;
         }
 
-        boolean legacyMode() {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public boolean legacyMode() {
             return mLegacyModeEnabled;
         }
 
@@ -224,13 +239,21 @@
          * Overrides the name of the native library backing Cronet.
          * @param libName the name of the native library backing Cronet.
          * @return the builder to facilitate chaining.
+         * @hide only used by internal implementation.
          */
-        Builder setLibraryName(String libName) {
+        public Builder setLibraryName(String libName) {
             mLibraryName = libName;
             return this;
         }
 
         /**
+         * @hide only used by internal implementation.
+         */
+        public String libraryName() {
+            return mLibraryName;
+        }
+
+        /**
          * Sets a {@link LibraryLoader} to be used to load the native library.
          * If not set, the library will be loaded using {@link System#loadLibrary}.
          * @param loader {@code LibraryLoader} to be used to load the native library.
@@ -241,12 +264,11 @@
             return this;
         }
 
-        void loadLibrary() {
-            if (mLibraryLoader == null) {
-                System.loadLibrary(mLibraryName);
-            } else {
-                mLibraryLoader.loadLibrary(mLibraryName);
-            }
+        /**
+         * @hide only used by internal implementation.
+         */
+        public LibraryLoader libraryLoader() {
+            return mLibraryLoader;
         }
 
         /**
@@ -261,7 +283,10 @@
             return this;
         }
 
-        boolean quicEnabled() {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public boolean quicEnabled() {
             return mQuicEnabled;
         }
 
@@ -271,9 +296,10 @@
          *
          * @param context Android {@link Context} to get package name from.
          * @return QUIC User Agent ID string.
+         * @hide only used by internal implementation.
          */
         // TODO(mef): remove |context| parameter when legacy ChromiumUrlRequestContext is removed.
-        String getDefaultQuicUserAgentId(Context context) {
+        public String getDefaultQuicUserAgentId(Context context) {
             return mQuicEnabled ? UserAgent.getQuicUserAgentIdFrom(context) : "";
         }
 
@@ -288,7 +314,10 @@
             return this;
         }
 
-        boolean http2Enabled() {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public boolean http2Enabled() {
             return mHttp2Enabled;
         }
 
@@ -305,7 +334,10 @@
             return this;
         }
 
-        boolean sdchEnabled() {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public boolean sdchEnabled() {
             return mSdchEnabled;
         }
 
@@ -321,7 +353,10 @@
             return this;
         }
 
-        String dataReductionProxyKey() {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public String dataReductionProxyKey() {
             return mDataReductionProxyKey;
         }
 
@@ -351,15 +386,24 @@
             return this;
         }
 
-        String dataReductionProxyPrimaryProxy() {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public String dataReductionProxyPrimaryProxy() {
             return mDataReductionProxyPrimaryProxy;
         }
 
-        String dataReductionProxyFallbackProxy() {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public String dataReductionProxyFallbackProxy() {
             return mDataReductionProxyFallbackProxy;
         }
 
-        String dataReductionProxySecureProxyCheckUrl() {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public String dataReductionProxySecureProxyCheckUrl() {
             return mDataReductionProxySecureProxyCheckUrl;
         }
 
@@ -436,15 +480,24 @@
             return this;
         }
 
-        boolean cacheDisabled() {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public boolean cacheDisabled() {
             return mDisableCache;
         }
 
-        long httpCacheMaxSize() {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public long httpCacheMaxSize() {
             return mHttpCacheMaxSize;
         }
 
-        int httpCacheMode() {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public int httpCacheMode() {
             return mHttpCacheMode;
         }
 
@@ -467,7 +520,10 @@
             return this;
         }
 
-        List<QuicHint> quicHints() {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public List<QuicHint> quicHints() {
             return mQuicHints;
         }
 
@@ -541,8 +597,9 @@
         /**
          * Returns list of public key pins.
          * @return list of public key pins.
+         * @hide only used by internal implementation.
          */
-        List<Pkp> publicKeyPins() {
+        public List<Pkp> publicKeyPins() {
             return mPkps;
         }
 
@@ -564,7 +621,10 @@
             return this;
         }
 
-        boolean publicKeyPinningBypassForLocalTrustAnchorsEnabled() {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public boolean publicKeyPinningBypassForLocalTrustAnchorsEnabled() {
             return mPublicKeyPinningBypassForLocalTrustAnchorsEnabled;
         }
 
@@ -608,7 +668,10 @@
             return this;
         }
 
-        String experimentalOptions() {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public String experimentalOptions() {
             return mExperimentalOptions;
         }
 
@@ -626,7 +689,10 @@
             return this;
         }
 
-        long mockCertVerifier() {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public long mockCertVerifier() {
             return mMockCertVerifier;
         }
 
@@ -652,9 +718,9 @@
         /**
          * @return true if the network quality estimator has been enabled for
          * this builder.
-         * @hide as it's a prototype.
+         * @hide as it's a prototype and only used by internal implementation.
          */
-        boolean networkQualityEstimatorEnabled() {
+        public boolean networkQualityEstimatorEnabled() {
             return mNetworkQualityEstimatorEnabled;
         }
 
@@ -670,7 +736,10 @@
             return this;
         }
 
-        String certVerifierData() {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public String certVerifierData() {
             return mCertVerifierData;
         }
 
@@ -678,8 +747,9 @@
          * Returns {@link Context} for builder.
          *
          * @return {@link Context} for builder.
+         * @hide only used by internal implementation.
          */
-        Context getContext() {
+        public Context getContext() {
             return mContext;
         }
 
@@ -708,7 +778,7 @@
 
     private static final String TAG = "UrlRequestFactory";
     private static final String CRONET_URL_REQUEST_CONTEXT =
-            "org.chromium.net.CronetUrlRequestContext";
+            "org.chromium.net.impl.CronetUrlRequestContext";
 
     /**
      * Creates a {@link UrlRequest} object. All callbacks will
@@ -801,8 +871,9 @@
      *         headers until flush() is called, and try to combine them
      *         with the next data frame.
      * @return a new stream.
+     * @hide only used by internal implementation.
      */
-    abstract BidirectionalStream createBidirectionalStream(String url,
+    public abstract BidirectionalStream createBidirectionalStream(String url,
             BidirectionalStream.Callback callback, Executor executor, String httpMethod,
             List<Map.Entry<String, String>> requestHeaders,
             @BidirectionalStream.Builder.StreamPriority int priority, boolean disableAutoFlush,
@@ -810,8 +881,9 @@
 
     /**
      * @return {@code true} if the engine is enabled.
+     * @hide only used by internal implementation.
      */
-    abstract boolean isEnabled();
+    public abstract boolean isEnabled();
 
     /**
      * @return a human-readable version string of the engine.
@@ -923,7 +995,7 @@
      * estimates.
      * @hide as it's a prototype.
      */
-    abstract void configureNetworkQualityEstimatorForTesting(
+    public abstract void configureNetworkQualityEstimatorForTesting(
             boolean useLocalHostRequests, boolean useSmallerResponses);
 
     /**
@@ -1035,7 +1107,8 @@
         CronetEngine cronetEngine = null;
         try {
             Class<? extends CronetEngine> engineClass =
-                    CronetEngine.class.getClassLoader()
+                    builder.getContext()
+                            .getClassLoader()
                             .loadClass(CRONET_URL_REQUEST_CONTEXT)
                             .asSubclass(CronetEngine.class);
             Constructor<? extends CronetEngine> constructor =
@@ -1091,7 +1164,10 @@
         private final UrlRequestMetrics mMetrics;
         @Nullable private final UrlResponseInfo mResponseInfo;
 
-        UrlRequestInfo(String url, Collection<Object> annotations, UrlRequestMetrics metrics,
+        /**
+         * @hide only used by internal implementation.
+         */
+        public UrlRequestInfo(String url, Collection<Object> annotations, UrlRequestMetrics metrics,
                 @Nullable UrlResponseInfo responseInfo) {
             mUrl = url;
             mAnnotations = annotations;
diff --git a/components/cronet/android/api/src/org/chromium/net/CronetException.java b/components/cronet/android/api/src/org/chromium/net/CronetException.java
index 6b2e805..8eec778 100644
--- a/components/cronet/android/api/src/org/chromium/net/CronetException.java
+++ b/components/cronet/android/api/src/org/chromium/net/CronetException.java
@@ -9,11 +9,17 @@
  */
 // TODO(mef): Will replace UrlRequestException soon.
 public class CronetException extends UrlRequestException {
-    CronetException(String message, Throwable cause) {
+    /**
+     * @hide only used by internal implementation.
+     */
+    public CronetException(String message, Throwable cause) {
         super(message, cause);
     }
 
-    CronetException(String message, int errorCode, int cronetInternalErrorCode) {
+    /**
+     * @hide only used by internal implementation.
+     */
+    public CronetException(String message, int errorCode, int cronetInternalErrorCode) {
         super(message, errorCode, cronetInternalErrorCode);
     }
 }
diff --git a/components/cronet/android/api/src/org/chromium/net/HttpUrlConnectionUrlRequestFactory.java b/components/cronet/android/api/src/org/chromium/net/HttpUrlConnectionUrlRequestFactory.java
index 28b0974..e5d2ff2 100644
--- a/components/cronet/android/api/src/org/chromium/net/HttpUrlConnectionUrlRequestFactory.java
+++ b/components/cronet/android/api/src/org/chromium/net/HttpUrlConnectionUrlRequestFactory.java
@@ -38,7 +38,7 @@
 
     @Override
     public String getName() {
-        return "HttpUrlConnection/" + Version.getVersion();
+        return "HttpUrlConnection/" + ApiVersion.getVersion();
     }
 
     @Override
diff --git a/components/cronet/android/api/src/org/chromium/net/HttpUrlRequestFactory.java b/components/cronet/android/api/src/org/chromium/net/HttpUrlRequestFactory.java
index fd92f7e..1f1014d 100644
--- a/components/cronet/android/api/src/org/chromium/net/HttpUrlRequestFactory.java
+++ b/components/cronet/android/api/src/org/chromium/net/HttpUrlRequestFactory.java
@@ -21,7 +21,7 @@
     private static final String TAG = "HttpUrlRequestFactory";
 
     private static final String CHROMIUM_URL_REQUEST_FACTORY =
-            "org.chromium.net.ChromiumUrlRequestFactory";
+            "org.chromium.net.impl.ChromiumUrlRequestFactory";
 
     public static HttpUrlRequestFactory createFactory(
             Context context, CronetEngine.Builder config) {
diff --git a/components/cronet/android/api/src/org/chromium/net/JavaCronetEngine.java b/components/cronet/android/api/src/org/chromium/net/JavaCronetEngine.java
index 9a94454..f12904f 100644
--- a/components/cronet/android/api/src/org/chromium/net/JavaCronetEngine.java
+++ b/components/cronet/android/api/src/org/chromium/net/JavaCronetEngine.java
@@ -63,8 +63,9 @@
     }
 
     @Override
-    BidirectionalStream createBidirectionalStream(String url, BidirectionalStream.Callback callback,
-            Executor executor, String httpMethod, List<Map.Entry<String, String>> requestHeaders,
+    public BidirectionalStream createBidirectionalStream(String url,
+            BidirectionalStream.Callback callback, Executor executor, String httpMethod,
+            List<Map.Entry<String, String>> requestHeaders,
             @BidirectionalStream.Builder.StreamPriority int priority, boolean disableAutoFlush,
             boolean delayRequestHeadersUntilFirstFlush) {
         throw new UnsupportedOperationException(
@@ -72,13 +73,13 @@
     }
 
     @Override
-    boolean isEnabled() {
+    public boolean isEnabled() {
         return true;
     }
 
     @Override
     public String getVersionString() {
-        return "CronetHttpURLConnection/" + Version.getVersion();
+        return "CronetHttpURLConnection/" + ApiVersion.getVersion();
     }
 
     @Override
@@ -109,7 +110,7 @@
     public void enableNetworkQualityEstimator(Executor executor) {}
 
     @Override
-    void configureNetworkQualityEstimatorForTesting(
+    public void configureNetworkQualityEstimatorForTesting(
             boolean useLocalHostRequests, boolean useSmallerResponses) {}
 
     @Override
diff --git a/components/cronet/android/api/src/org/chromium/net/Preconditions.java b/components/cronet/android/api/src/org/chromium/net/Preconditions.java
index babce374..4abe961 100644
--- a/components/cronet/android/api/src/org/chromium/net/Preconditions.java
+++ b/components/cronet/android/api/src/org/chromium/net/Preconditions.java
@@ -6,16 +6,19 @@
 
 import java.nio.ByteBuffer;
 
-final class Preconditions {
+/**
+ * {@hide only used by internal implementation.}
+ */
+public final class Preconditions {
     private Preconditions() {}
 
-    static void checkDirect(ByteBuffer buffer) {
+    public static void checkDirect(ByteBuffer buffer) {
         if (!buffer.isDirect()) {
             throw new IllegalArgumentException("byteBuffer must be a direct ByteBuffer.");
         }
     }
 
-    static void checkHasRemaining(ByteBuffer buffer) {
+    public static void checkHasRemaining(ByteBuffer buffer) {
         if (!buffer.hasRemaining()) {
             throw new IllegalArgumentException("ByteBuffer is already full.");
         }
diff --git a/components/cronet/android/api/src/org/chromium/net/UrlRequest.java b/components/cronet/android/api/src/org/chromium/net/UrlRequest.java
index cb8c45a..2b2957c 100644
--- a/components/cronet/android/api/src/org/chromium/net/UrlRequest.java
+++ b/components/cronet/android/api/src/org/chromium/net/UrlRequest.java
@@ -505,9 +505,10 @@
          * Convert a LoadState int to one of values listed above.
          * @param loadState a LoadState to convert.
          * @return static int Status.
+         * @hide only used by internal implementation.
          */
         @StatusValues
-        static int convertLoadState(int loadState) {
+        public static int convertLoadState(int loadState) {
             assert loadState >= LoadState.IDLE && loadState <= LoadState.READING_RESPONSE;
             switch (loadState) {
                 case (LoadState.IDLE):
diff --git a/components/cronet/android/api/src/org/chromium/net/UrlResponseInfo.java b/components/cronet/android/api/src/org/chromium/net/UrlResponseInfo.java
index 37cd30c8..e0fbfb79 100644
--- a/components/cronet/android/api/src/org/chromium/net/UrlResponseInfo.java
+++ b/components/cronet/android/api/src/org/chromium/net/UrlResponseInfo.java
@@ -35,7 +35,10 @@
         private final List<Map.Entry<String, String>> mAllHeadersList;
         private Map<String, List<String>> mHeadersMap;
 
-        HeaderBlock(List<Map.Entry<String, String>> allHeadersList) {
+        /**
+         * @hide only used by internal implementation.
+         */
+        public HeaderBlock(List<Map.Entry<String, String>> allHeadersList) {
             mAllHeadersList = allHeadersList;
         }
 
@@ -215,8 +218,11 @@
                 getProxyServer(), getReceivedBytesCount());
     }
 
-    // Sets mReceivedBytesCount. Must not be called after request completion or cancellation.
-    void setReceivedBytesCount(long currentReceivedBytesCount) {
+    /**
+     * Sets mReceivedBytesCount. Must not be called after request completion or cancellation.
+     * @hide only used by internal implementation.
+     */
+    public void setReceivedBytesCount(long currentReceivedBytesCount) {
         mReceivedBytesCount.set(currentReceivedBytesCount);
     }
 }
diff --git a/components/cronet/android/api/src/org/chromium/net/UserAgent.java b/components/cronet/android/api/src/org/chromium/net/UserAgent.java
index 6dbab542..5abd81c 100644
--- a/components/cronet/android/api/src/org/chromium/net/UserAgent.java
+++ b/components/cronet/android/api/src/org/chromium/net/UserAgent.java
@@ -57,8 +57,8 @@
             builder.append(id);
         }
 
-        builder.append("; Cronet/");
-        builder.append(Version.CRONET_VERSION);
+        builder.append(";");
+        appendCronetVersion(builder);
 
         builder.append(')');
 
@@ -76,8 +76,7 @@
 
         // Application name and cronet version.
         builder.append(context.getPackageName());
-        builder.append(" Cronet/");
-        builder.append(Version.CRONET_VERSION);
+        appendCronetVersion(builder);
 
         return builder.toString();
     }
@@ -99,4 +98,14 @@
             return sVersionCode;
         }
     }
+
+    private static void appendCronetVersion(StringBuilder builder) {
+        builder.append(" Cronet/");
+        // TODO(pauljensen): This is the API version not the implementation
+        // version. The implementation version may be more appropriate for the
+        // UserAgent but is not available until after the CronetEngine is
+        // instantiated. Down the road, if the implementation is loaded via
+        // other means, this should be replaced with the implementation version.
+        builder.append(ApiVersion.CRONET_VERSION);
+    }
 }
diff --git a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java b/components/cronet/android/java/src/org/chromium/net/impl/ChromiumUrlRequest.java
similarity index 89%
rename from components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java
rename to components/cronet/android/java/src/org/chromium/net/impl/ChromiumUrlRequest.java
index 5b08182..aff3f7a 100644
--- a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/ChromiumUrlRequest.java
@@ -2,13 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.net;
+package org.chromium.net.impl;
 
 import android.util.Log;
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.SuppressFBWarnings;
+import org.chromium.net.ChromiumUrlRequestError;
+import org.chromium.net.ChromiumUrlRequestPriority;
+import org.chromium.net.ChunkedWritableByteChannel;
+import org.chromium.net.HttpUrlRequest;
+import org.chromium.net.HttpUrlRequestListener;
+import org.chromium.net.ResponseTooLargeException;
 
 import java.io.IOException;
 import java.net.MalformedURLException;
@@ -85,11 +91,9 @@
     // Protects access of mUrlRequestAdapter, mStarted, mCanceled, and mFinished.
     private final Object mLock = new Object();
 
-    public ChromiumUrlRequest(ChromiumUrlRequestContext requestContext,
-            String url, int priority, Map<String, String> headers,
-            HttpUrlRequestListener listener) {
-        this(requestContext, url, priority, headers,
-                new ChunkedWritableByteChannel(), listener);
+    public ChromiumUrlRequest(ChromiumUrlRequestContext requestContext, String url, int priority,
+            Map<String, String> headers, HttpUrlRequestListener listener) {
+        this(requestContext, url, priority, headers, new ChunkedWritableByteChannel(), listener);
         mBufferFullResponse = true;
     }
 
@@ -103,9 +107,9 @@
      * @param sink The output channel into which downloaded content will be
      *            written.
      */
-    public ChromiumUrlRequest(ChromiumUrlRequestContext requestContext,
-            String url, int priority, Map<String, String> headers,
-            WritableByteChannel sink, HttpUrlRequestListener listener) {
+    public ChromiumUrlRequest(ChromiumUrlRequestContext requestContext, String url, int priority,
+            Map<String, String> headers, WritableByteChannel sink,
+            HttpUrlRequestListener listener) {
         if (requestContext == null) {
             throw new NullPointerException("Context is required");
         }
@@ -199,8 +203,7 @@
                 return new IOException("Request failed because there were too "
                         + "many redirects or redirects have been disabled");
             default:
-                throw new IllegalStateException(
-                        "Unrecognized error code: " + mErrorCode);
+                throw new IllegalStateException("Unrecognized error code: " + mErrorCode);
         }
     }
 
@@ -257,8 +260,8 @@
      * @param contentLength The length of data to upload.
      */
     @Override
-    public void setUploadChannel(String contentType,
-            ReadableByteChannel channel, long contentLength) {
+    public void setUploadChannel(
+            String contentType, ReadableByteChannel channel, long contentLength) {
         synchronized (mLock) {
             validateNotStarted();
             validateContentType(contentType);
@@ -297,11 +300,9 @@
      *            chunks must be non-empty.
      * @param isLastChunk Whether this chunk is the last one.
      */
-    public void appendChunk(ByteBuffer chunk, boolean isLastChunk)
-            throws IOException {
+    public void appendChunk(ByteBuffer chunk, boolean isLastChunk) throws IOException {
         if (!isLastChunk && !chunk.hasRemaining()) {
-            throw new IllegalArgumentException(
-                    "Attempted to write empty buffer.");
+            throw new IllegalArgumentException("Attempted to write empty buffer.");
         }
         if (chunk.position() != 0) {
             throw new IllegalArgumentException("The position must be zero.");
@@ -311,14 +312,12 @@
                 throw new IllegalStateException("Request not yet started.");
             }
             if (!mChunkedUpload) {
-                throw new IllegalStateException(
-                        "Request not set for chunked uploadind.");
+                throw new IllegalStateException("Request not set for chunked uploadind.");
             }
             if (mUrlRequestAdapter == 0) {
                 throw new IOException("Native peer destroyed.");
             }
-            nativeAppendChunk(mUrlRequestAdapter, chunk, chunk.limit(),
-                    isLastChunk);
+            nativeAppendChunk(mUrlRequestAdapter, chunk, chunk.limit(), isLastChunk);
         }
     }
 
@@ -356,28 +355,23 @@
 
             if (mHeaders != null && !mHeaders.isEmpty()) {
                 for (Entry<String, String> entry : mHeaders.entrySet()) {
-                    nativeAddHeader(mUrlRequestAdapter, entry.getKey(),
-                            entry.getValue());
+                    nativeAddHeader(mUrlRequestAdapter, entry.getKey(), entry.getValue());
                 }
             }
 
             if (mAdditionalHeaders != null) {
-                for (Entry<String, String> entry :
-                        mAdditionalHeaders.entrySet()) {
-                    nativeAddHeader(mUrlRequestAdapter, entry.getKey(),
-                            entry.getValue());
+                for (Entry<String, String> entry : mAdditionalHeaders.entrySet()) {
+                    nativeAddHeader(mUrlRequestAdapter, entry.getKey(), entry.getValue());
                 }
             }
 
             if (mUploadData != null && mUploadData.length > 0) {
-                nativeSetUploadData(mUrlRequestAdapter, mUploadContentType,
-                                    mUploadData);
+                nativeSetUploadData(mUrlRequestAdapter, mUploadContentType, mUploadData);
             } else if (mUploadChannel != null) {
-                nativeSetUploadChannel(mUrlRequestAdapter, mUploadContentType,
-                                       mUploadContentLength);
+                nativeSetUploadChannel(
+                        mUrlRequestAdapter, mUploadContentType, mUploadContentLength);
             } else if (mChunkedUpload) {
-                nativeEnableChunkedUpload(mUrlRequestAdapter,
-                                          mUploadContentType);
+                nativeEnableChunkedUpload(mUrlRequestAdapter, mUploadContentType);
             }
 
             // Note:  The above functions to set the upload body also set the
@@ -533,15 +527,13 @@
      * and exception could be retrieved from request using getException().
      */
     private void onCalledByNativeException(Exception e) {
-        mSinkException = new IOException(
-                "CalledByNative method has thrown an exception", e);
-        Log.e(ChromiumUrlRequestContext.LOG_TAG,
-                "Exception in CalledByNative method", e);
+        mSinkException = new IOException("CalledByNative method has thrown an exception", e);
+        Log.e(ChromiumUrlRequestContext.LOG_TAG, "Exception in CalledByNative method", e);
         try {
             cancel();
         } catch (Exception cancel_exception) {
-            Log.e(ChromiumUrlRequestContext.LOG_TAG,
-                    "Exception trying to cancel request", cancel_exception);
+            Log.e(ChromiumUrlRequestContext.LOG_TAG, "Exception trying to cancel request",
+                    cancel_exception);
         }
     }
 
@@ -557,17 +549,14 @@
             mContentLength = nativeGetContentLength(mUrlRequestAdapter);
             mHeadersAvailable = true;
 
-            if (mContentLengthLimit > 0
-                    && mContentLength > mContentLengthLimit
+            if (mContentLengthLimit > 0 && mContentLength > mContentLengthLimit
                     && mCancelIfContentLengthOverLimit) {
                 onContentLengthOverLimit();
                 return;
             }
 
-            if (mBufferFullResponse && mContentLength != -1
-                    && !mContentLengthOverLimit) {
-                ((ChunkedWritableByteChannel) getSink()).setCapacity(
-                        (int) mContentLength);
+            if (mBufferFullResponse && mContentLength != -1 && !mContentLengthOverLimit) {
+                ((ChunkedWritableByteChannel) getSink()).setCapacity((int) mContentLength);
             }
 
             if (mOffset != 0) {
@@ -674,8 +663,7 @@
      */
     @SuppressWarnings("unused")
     @CalledByNative
-    private void onAppendResponseHeader(ResponseHeadersMap headersMap,
-            String name, String value) {
+    private void onAppendResponseHeader(ResponseHeadersMap headersMap, String name, String value) {
         try {
             if (!headersMap.containsKey(name)) {
                 headersMap.put(name, new ArrayList<String>());
@@ -714,24 +702,22 @@
     private native long nativeCreateRequestAdapter(
             long urlRequestContextAdapter, String url, int priority);
 
-    private native void nativeAddHeader(long urlRequestAdapter, String name,
-            String value);
+    private native void nativeAddHeader(long urlRequestAdapter, String name, String value);
 
     private native void nativeSetMethod(long urlRequestAdapter, String method);
 
-    private native void nativeSetUploadData(long urlRequestAdapter,
-            String contentType, byte[] content);
+    private native void nativeSetUploadData(
+            long urlRequestAdapter, String contentType, byte[] content);
 
-    private native void nativeSetUploadChannel(long urlRequestAdapter,
-            String contentType, long contentLength);
+    private native void nativeSetUploadChannel(
+            long urlRequestAdapter, String contentType, long contentLength);
 
-    private native void nativeEnableChunkedUpload(long urlRequestAdapter,
-            String contentType);
+    private native void nativeEnableChunkedUpload(long urlRequestAdapter, String contentType);
 
     private native void nativeDisableRedirects(long urlRequestAdapter);
 
-    private native void nativeAppendChunk(long urlRequestAdapter,
-            ByteBuffer chunk, int chunkSize, boolean isLastChunk);
+    private native void nativeAppendChunk(
+            long urlRequestAdapter, ByteBuffer chunk, int chunkSize, boolean isLastChunk);
 
     private native void nativeStart(long urlRequestAdapter);
 
@@ -753,15 +739,12 @@
 
     private native String nativeGetHeader(long urlRequestAdapter, String name);
 
-    private native void nativeGetAllHeaders(long urlRequestAdapter,
-            ResponseHeadersMap headers);
+    private native void nativeGetAllHeaders(long urlRequestAdapter, ResponseHeadersMap headers);
 
     private native String nativeGetNegotiatedProtocol(long urlRequestAdapter);
 
     private native boolean nativeGetWasCached(long urlRequestAdapter);
 
     // Explicit class to work around JNI-generator generics confusion.
-    private static class ResponseHeadersMap extends
-            HashMap<String, List<String>> {
-    }
+    private static class ResponseHeadersMap extends HashMap<String, List<String>> {}
 }
diff --git a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/impl/ChromiumUrlRequestContext.java
similarity index 86%
rename from components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java
rename to components/cronet/android/java/src/org/chromium/net/impl/ChromiumUrlRequestContext.java
index fdf9daf..df897b5f 100644
--- a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/ChromiumUrlRequestContext.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.net;
+package org.chromium.net.impl;
 
 import android.content.Context;
 import android.os.Handler;
@@ -10,8 +10,10 @@
 import android.os.Process;
 import android.util.Log;
 
+import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.net.CronetEngine;
 
 /**
  * Provides context for the native HTTP operations.
@@ -20,9 +22,9 @@
 @JNINamespace("cronet")
 @Deprecated
 public class ChromiumUrlRequestContext {
-    private static final int LOG_NONE = 3;  // LOG(FATAL), no VLOG.
-    private static final int LOG_DEBUG = -1;  // LOG(FATAL...INFO), VLOG(1)
-    private static final int LOG_VERBOSE = -2;  // LOG(FATAL...INFO), VLOG(2)
+    private static final int LOG_NONE = 3; // LOG(FATAL), no VLOG.
+    private static final int LOG_DEBUG = -1; // LOG(FATAL...INFO), VLOG(1)
+    private static final int LOG_VERBOSE = -2; // LOG(FATAL...INFO), VLOG(2)
     static final String LOG_TAG = "ChromiumNetwork";
 
     /**
@@ -48,8 +50,7 @@
         // API to handle the case where we are already on main thread.
         Runnable task = new Runnable() {
             public void run() {
-                nativeInitRequestContextOnMainThread(
-                        mChromiumUrlRequestContextAdapter);
+                nativeInitRequestContextOnMainThread(mChromiumUrlRequestContextAdapter);
             }
         };
         new Handler(Looper.getMainLooper()).post(task);
@@ -60,7 +61,7 @@
      * N.N.N.N is the version of Chromium and X is the revision number.
      */
     public static String getVersion() {
-        return Version.getVersion();
+        return ImplVersion.getVersion();
     }
 
     /**
@@ -92,8 +93,7 @@
      *            false, NetLogCaptureMode::Default() is used instead.
      */
     public void startNetLogToFile(String fileName, boolean logAll) {
-        nativeStartNetLogToFile(mChromiumUrlRequestContextAdapter, fileName,
-                logAll);
+        nativeStartNetLogToFile(mChromiumUrlRequestContextAdapter, fileName, logAll);
     }
 
     /**
@@ -118,7 +118,8 @@
         super.finalize();
     }
 
-    protected long getUrlRequestContextAdapter() {
+    @VisibleForTesting
+    public long getUrlRequestContextAdapter() {
         return mChromiumUrlRequestContextAdapter;
     }
 
@@ -143,19 +144,16 @@
     private native long nativeCreateRequestContextAdapter(
             String userAgent, int loggingLevel, long config);
 
-    private native void nativeReleaseRequestContextAdapter(
-            long chromiumUrlRequestContextAdapter);
+    private native void nativeReleaseRequestContextAdapter(long chromiumUrlRequestContextAdapter);
 
     private native void nativeInitializeStatistics();
 
     private native String nativeGetStatisticsJSON(String filter);
 
     private native void nativeStartNetLogToFile(
-            long chromiumUrlRequestContextAdapter, String fileName,
-            boolean logAll);
+            long chromiumUrlRequestContextAdapter, String fileName, boolean logAll);
 
     private native void nativeStopNetLog(long chromiumUrlRequestContextAdapter);
 
-    private native void nativeInitRequestContextOnMainThread(
-            long chromiumUrlRequestContextAdapter);
+    private native void nativeInitRequestContextOnMainThread(long chromiumUrlRequestContextAdapter);
 }
diff --git a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestFactory.java b/components/cronet/android/java/src/org/chromium/net/impl/ChromiumUrlRequestFactory.java
similarity index 86%
rename from components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestFactory.java
rename to components/cronet/android/java/src/org/chromium/net/impl/ChromiumUrlRequestFactory.java
index c0d9bd7..4da3849 100644
--- a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestFactory.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/ChromiumUrlRequestFactory.java
@@ -2,12 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.net;
+package org.chromium.net.impl;
 
 import android.content.Context;
 import android.os.Build;
 
 import org.chromium.base.annotations.UsedByReflection;
+import org.chromium.net.CronetEngine;
+import org.chromium.net.HttpUrlRequestFactory;
+import org.chromium.net.HttpUrlRequestListener;
 
 import java.nio.channels.WritableByteChannel;
 import java.util.Map;
@@ -29,8 +32,7 @@
                 // Cannot use config.getDefaultUserAgent() as config.mContext may be null.
                 userAgent = new CronetEngine.Builder(context).getDefaultUserAgent();
             }
-            mRequestContext = new ChromiumUrlRequestContext(context,
-                    userAgent, config);
+            mRequestContext = new ChromiumUrlRequestContext(context, userAgent, config);
         }
     }
 
@@ -47,16 +49,15 @@
     @Override
     public ChromiumUrlRequest createRequest(String url, int requestPriority,
             Map<String, String> headers, HttpUrlRequestListener listener) {
-        return new ChromiumUrlRequest(mRequestContext, url, requestPriority,
-                headers, listener);
+        return new ChromiumUrlRequest(mRequestContext, url, requestPriority, headers, listener);
     }
 
     @Override
     public ChromiumUrlRequest createRequest(String url, int requestPriority,
             Map<String, String> headers, WritableByteChannel channel,
             HttpUrlRequestListener listener) {
-        return new ChromiumUrlRequest(mRequestContext, url, requestPriority,
-                headers, channel, listener);
+        return new ChromiumUrlRequest(
+                mRequestContext, url, requestPriority, headers, channel, listener);
     }
 
     @Override
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetBidirectionalStream.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java
similarity index 98%
rename from components/cronet/android/java/src/org/chromium/net/CronetBidirectionalStream.java
rename to components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java
index 86bde7ec..31d50516 100644
--- a/components/cronet/android/java/src/org/chromium/net/CronetBidirectionalStream.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetBidirectionalStream.java
@@ -2,13 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.net;
+package org.chromium.net.impl;
 
 import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeClassQualifiedName;
+import org.chromium.net.BidirectionalStream;
+import org.chromium.net.CronetException;
+import org.chromium.net.Preconditions;
+import org.chromium.net.QuicException;
+import org.chromium.net.RequestPriority;
+import org.chromium.net.UrlRequestException;
+import org.chromium.net.UrlResponseInfo;
 
 import java.nio.ByteBuffer;
 import java.util.AbstractMap;
@@ -29,7 +36,8 @@
  * stream is called on Executor thread and posts native tasks to the native network thread.
  */
 @JNINamespace("cronet")
-class CronetBidirectionalStream extends BidirectionalStream {
+@VisibleForTesting
+public class CronetBidirectionalStream extends BidirectionalStream {
     /**
      * States of BidirectionalStream are tracked in mReadState and mWriteState.
      * The write state is separated out as it changes independently of the read state.
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetLibraryLoader.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java
similarity index 77%
rename from components/cronet/android/java/src/org/chromium/net/CronetLibraryLoader.java
rename to components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java
index 7c358d9..78579af 100644
--- a/components/cronet/android/java/src/org/chromium/net/CronetLibraryLoader.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetLibraryLoader.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.net;
+package org.chromium.net.impl;
 
 import android.content.Context;
 import android.os.Handler;
@@ -10,13 +10,17 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
+import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.net.CronetEngine;
+import org.chromium.net.NetworkChangeNotifier;
 
 /**
  * CronetLibraryLoader loads and initializes native library on main thread.
  */
 @JNINamespace("cronet")
-class CronetLibraryLoader {
+@VisibleForTesting
+public class CronetLibraryLoader {
     // Synchronize initialization.
     private static final Object sLoadLock = new Object();
     private static final String TAG = "CronetLibraryLoader";
@@ -29,24 +33,27 @@
      * Ensure that native library is loaded and initialized. Can be called from
      * any thread, the load and initialization is performed on main thread.
      */
-    static void ensureInitialized(final Context context, final CronetEngine.Builder builder) {
+    public static void ensureInitialized(
+            final Context context, final CronetEngine.Builder builder) {
         synchronized (sLoadLock) {
             if (sInitStarted) {
                 return;
             }
             sInitStarted = true;
             ContextUtils.initApplicationContext(context.getApplicationContext());
-            builder.loadLibrary();
-            ContextUtils.initApplicationContextForNative();
-            if (!Version.CRONET_VERSION.equals(nativeGetCronetVersion())) {
-                throw new RuntimeException(String.format(
-                      "Expected Cronet version number %s, "
-                              + "actual version number %s.",
-                      Version.CRONET_VERSION,
-                      nativeGetCronetVersion()));
+            if (builder.libraryLoader() != null) {
+                builder.libraryLoader().loadLibrary(builder.libraryName());
+            } else {
+                System.loadLibrary(builder.libraryName());
             }
-            Log.i(TAG, "Cronet version: %s, arch: %s",
-                    Version.CRONET_VERSION, System.getProperty("os.arch"));
+            ContextUtils.initApplicationContextForNative();
+            if (!ImplVersion.CRONET_VERSION.equals(nativeGetCronetVersion())) {
+                throw new RuntimeException(String.format("Expected Cronet version number %s, "
+                                + "actual version number %s.",
+                        ImplVersion.CRONET_VERSION, nativeGetCronetVersion()));
+            }
+            Log.i(TAG, "Cronet version: %s, arch: %s", ImplVersion.CRONET_VERSION,
+                    System.getProperty("os.arch"));
             // Init native Chromium CronetEngine on Main UI thread.
             Runnable task = new Runnable() {
                 @Override
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetUploadDataStream.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetUploadDataStream.java
similarity index 95%
rename from components/cronet/android/java/src/org/chromium/net/CronetUploadDataStream.java
rename to components/cronet/android/java/src/org/chromium/net/impl/CronetUploadDataStream.java
index 162376fc..3b2601e6 100644
--- a/components/cronet/android/java/src/org/chromium/net/CronetUploadDataStream.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetUploadDataStream.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.net;
+package org.chromium.net.impl;
 
 import android.util.Log;
 
@@ -10,6 +10,8 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeClassQualifiedName;
+import org.chromium.net.UploadDataProvider;
+import org.chromium.net.UploadDataSink;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -28,7 +30,8 @@
  * passed into its constructor.
  */
 @JNINamespace("cronet")
-final class CronetUploadDataStream implements UploadDataSink {
+@VisibleForTesting
+public final class CronetUploadDataStream implements UploadDataSink {
     private static final String TAG = "CronetUploadDataStream";
     // These are never changed, once a request starts.
     private final Executor mExecutor;
@@ -47,8 +50,7 @@
                 }
                 checkState(UserCallback.NOT_IN_CALLBACK);
                 if (mByteBuffer == null) {
-                    throw new IllegalStateException(
-                            "Unexpected readData call. Buffer is null");
+                    throw new IllegalStateException("Unexpected readData call. Buffer is null");
                 }
                 mInWhichUserCallback = UserCallback.READ;
             }
@@ -183,8 +185,7 @@
         synchronized (mLock) {
             checkState(UserCallback.READ);
             if (lastChunk && mLength >= 0) {
-                throw new IllegalArgumentException(
-                        "Non-chunked upload can't have last chunk");
+                throw new IllegalArgumentException("Non-chunked upload can't have last chunk");
             }
             int bytesRead = mByteBuffer.position();
             mRemainingLength -= bytesRead;
@@ -201,8 +202,7 @@
             if (mUploadDataStreamAdapter == 0) {
                 return;
             }
-            nativeOnReadSucceeded(mUploadDataStreamAdapter, bytesRead,
-                    lastChunk);
+            nativeOnReadSucceeded(mUploadDataStreamAdapter, bytesRead, lastChunk);
         }
     }
 
@@ -338,7 +338,7 @@
      * @return the address of the native CronetUploadDataStream object.
      */
     @VisibleForTesting
-    long createUploadDataStreamForTesting() throws IOException {
+    public long createUploadDataStreamForTesting() throws IOException {
         synchronized (mLock) {
             mUploadDataStreamAdapter = nativeCreateAdapterForTesting();
             mLength = mDataProvider.getLength();
@@ -354,17 +354,14 @@
 
     // Native methods are implemented in upload_data_stream_adapter.cc.
 
-    private native long nativeAttachUploadDataToRequest(long urlRequestAdapter,
-            long length);
+    private native long nativeAttachUploadDataToRequest(long urlRequestAdapter, long length);
 
     private native long nativeCreateAdapterForTesting();
 
-    private native long nativeCreateUploadDataStreamForTesting(long length,
-            long adapter);
+    private native long nativeCreateUploadDataStreamForTesting(long length, long adapter);
 
     @NativeClassQualifiedName("CronetUploadDataStreamAdapter")
-    private native void nativeOnReadSucceeded(long nativePtr,
-            int bytesRead, boolean finalChunk);
+    private native void nativeOnReadSucceeded(long nativePtr, int bytesRead, boolean finalChunk);
 
     @NativeClassQualifiedName("CronetUploadDataStreamAdapter")
     private native void nativeOnRewindSucceeded(long nativePtr);
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequest.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java
similarity index 94%
rename from components/cronet/android/java/src/org/chromium/net/CronetUrlRequest.java
rename to components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java
index af4ea34..fd13de1 100644
--- a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequest.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.net;
+package org.chromium.net.impl;
 
 import android.os.SystemClock;
 import android.support.annotation.Nullable;
@@ -15,6 +15,13 @@
 import org.chromium.base.annotations.NativeClassQualifiedName;
 import org.chromium.net.CronetEngine.UrlRequestInfo;
 import org.chromium.net.CronetEngine.UrlRequestMetrics;
+import org.chromium.net.Preconditions;
+import org.chromium.net.QuicException;
+import org.chromium.net.RequestPriority;
+import org.chromium.net.UploadDataProvider;
+import org.chromium.net.UrlRequest;
+import org.chromium.net.UrlRequestException;
+import org.chromium.net.UrlResponseInfo;
 
 import java.nio.ByteBuffer;
 import java.util.AbstractMap;
@@ -39,7 +46,8 @@
 @JNINamespace("cronet")
 // Qualifies UrlRequest.StatusListener which is used in onStatus, a JNI method.
 @JNIAdditionalImport(UrlRequest.class)
-final class CronetUrlRequest implements UrlRequest {
+@VisibleForTesting
+public final class CronetUrlRequest implements UrlRequest {
     private static final UrlRequestMetrics EMPTY_METRICS =
             new UrlRequestMetrics(null, null, null, null);
 
@@ -338,12 +346,13 @@
     }
 
     @VisibleForTesting
-    void setOnDestroyedUploadCallbackForTesting(Runnable onDestroyedUploadCallbackForTesting) {
+    public void setOnDestroyedUploadCallbackForTesting(
+            Runnable onDestroyedUploadCallbackForTesting) {
         mUploadDataStream.setOnDestroyedCallbackForTesting(onDestroyedUploadCallbackForTesting);
     }
 
     @VisibleForTesting
-    long getUrlRequestAdapterForTesting() {
+    public long getUrlRequestAdapterForTesting() {
         synchronized (mUrlRequestAdapterLock) {
             return mUrlRequestAdapter;
         }
@@ -357,8 +366,8 @@
         try {
             mExecutor.execute(task);
         } catch (RejectedExecutionException failException) {
-            Log.e(CronetUrlRequestContext.LOG_TAG,
-                    "Exception posting task to executor", failException);
+            Log.e(CronetUrlRequestContext.LOG_TAG, "Exception posting task to executor",
+                    failException);
             // If posting a task throws an exception, then there is no choice
             // but to destroy the request without invoking the callback.
             destroyRequestAdapter(false);
@@ -428,8 +437,7 @@
     private void onCallbackException(Exception e) {
         UrlRequestException requestError =
                 new UrlRequestException("Exception received from UrlRequest.Callback", e);
-        Log.e(CronetUrlRequestContext.LOG_TAG,
-                "Exception in CalledByNative method", e);
+        Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in CalledByNative method", e);
         // Do not call into listener if request is finished.
         synchronized (mUrlRequestAdapterLock) {
             if (isDoneLocked()) {
@@ -440,8 +448,8 @@
         try {
             mCallback.onFailed(this, mResponseInfo, requestError);
         } catch (Exception failException) {
-            Log.e(CronetUrlRequestContext.LOG_TAG,
-                    "Exception notifying of failed request", failException);
+            Log.e(CronetUrlRequestContext.LOG_TAG, "Exception notifying of failed request",
+                    failException);
         }
     }
 
@@ -471,8 +479,7 @@
                 try {
                     mCallback.onFailed(CronetUrlRequest.this, mResponseInfo, exception);
                 } catch (Exception e) {
-                    Log.e(CronetUrlRequestContext.LOG_TAG,
-                            "Exception in onError method", e);
+                    Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onError method", e);
                 }
             }
         };
@@ -586,8 +593,8 @@
             int initialLimit, long receivedBytesCount) {
         mResponseInfo.setReceivedBytesCount(mReceivedBytesCountFromRedirects + receivedBytesCount);
         if (byteBuffer.position() != initialPosition || byteBuffer.limit() != initialLimit) {
-            failWithException(new UrlRequestException(
-                    "ByteBuffer modified externally during read", null));
+            failWithException(
+                    new UrlRequestException("ByteBuffer modified externally during read", null));
             return;
         }
         if (mOnReadCompletedTask == null) {
@@ -622,8 +629,7 @@
                 try {
                     mCallback.onSucceeded(CronetUrlRequest.this, mResponseInfo);
                 } catch (Exception e) {
-                    Log.e(CronetUrlRequestContext.LOG_TAG,
-                            "Exception in onComplete method", e);
+                    Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onComplete method", e);
                 }
             }
         };
@@ -699,9 +705,12 @@
     }
 
     private final class UrlRequestMetricsAccumulator {
-        @Nullable private Long mRequestStartTime;
-        @Nullable private Long mTtfbMs;
-        @Nullable private Long mTotalTimeMs;
+        @Nullable
+        private Long mRequestStartTime;
+        @Nullable
+        private Long mTtfbMs;
+        @Nullable
+        private Long mTotalTimeMs;
 
         private UrlRequestMetrics getRequestMetrics() {
             return new UrlRequestMetrics(mTtfbMs, mTotalTimeMs,
@@ -747,8 +756,8 @@
     private native void nativeFollowDeferredRedirect(long nativePtr);
 
     @NativeClassQualifiedName("CronetURLRequestAdapter")
-    private native boolean nativeReadData(long nativePtr, ByteBuffer byteBuffer,
-            int position, int capacity);
+    private native boolean nativeReadData(
+            long nativePtr, ByteBuffer byteBuffer, int position, int capacity);
 
     @NativeClassQualifiedName("CronetURLRequestAdapter")
     private native void nativeDestroy(long nativePtr, boolean sendOnCanceled);
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java
similarity index 94%
rename from components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
rename to components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java
index bbd522a..345692d 100644
--- a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
+++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.net;
+package org.chromium.net.impl;
 
 import android.content.Context;
 import android.os.Build;
@@ -18,6 +18,11 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeClassQualifiedName;
 import org.chromium.base.annotations.UsedByReflection;
+import org.chromium.net.BidirectionalStream;
+import org.chromium.net.CronetEngine;
+import org.chromium.net.NetworkQualityRttListener;
+import org.chromium.net.NetworkQualityThroughputListener;
+import org.chromium.net.UrlRequest;
 import org.chromium.net.urlconnection.CronetHttpURLConnection;
 import org.chromium.net.urlconnection.CronetURLStreamHandlerFactory;
 
@@ -39,10 +44,11 @@
  */
 @JNINamespace("cronet")
 @UsedByReflection("CronetEngine.java")
-class CronetUrlRequestContext extends CronetEngine {
-    private static final int LOG_NONE = 3;  // LOG(FATAL), no VLOG.
-    private static final int LOG_DEBUG = -1;  // LOG(FATAL...INFO), VLOG(1)
-    private static final int LOG_VERBOSE = -2;  // LOG(FATAL...INFO), VLOG(2)
+@VisibleForTesting
+public class CronetUrlRequestContext extends CronetEngine {
+    private static final int LOG_NONE = 3; // LOG(FATAL), no VLOG.
+    private static final int LOG_DEBUG = -1; // LOG(FATAL...INFO), VLOG(1)
+    private static final int LOG_VERBOSE = -2; // LOG(FATAL...INFO), VLOG(2)
     static final String LOG_TAG = "ChromiumNetwork";
 
     /**
@@ -117,7 +123,8 @@
         }
     }
 
-    static long createNativeUrlRequestContextConfig(
+    @VisibleForTesting
+    public static long createNativeUrlRequestContextConfig(
             final Context context, CronetEngine.Builder builder) {
         final long urlRequestContextConfig = nativeCreateRequestContextConfig(
                 builder.getUserAgent(), builder.storagePath(), builder.quicEnabled(),
@@ -158,8 +165,9 @@
     }
 
     @Override
-    BidirectionalStream createBidirectionalStream(String url, BidirectionalStream.Callback callback,
-            Executor executor, String httpMethod, List<Map.Entry<String, String>> requestHeaders,
+    public BidirectionalStream createBidirectionalStream(String url,
+            BidirectionalStream.Callback callback, Executor executor, String httpMethod,
+            List<Map.Entry<String, String>> requestHeaders,
             @BidirectionalStream.Builder.StreamPriority int priority, boolean disableAutoFlush,
             boolean delayRequestHeadersUntilFirstFlush) {
         synchronized (mLock) {
@@ -177,7 +185,7 @@
 
     @Override
     public String getVersionString() {
-        return "Cronet/" + Version.getVersion();
+        return "Cronet/" + ImplVersion.getVersion();
     }
 
     @Override
@@ -185,14 +193,12 @@
         synchronized (mLock) {
             checkHaveAdapter();
             if (mActiveRequestCount.get() != 0) {
-                throw new IllegalStateException(
-                        "Cannot shutdown with active requests.");
+                throw new IllegalStateException("Cannot shutdown with active requests.");
             }
             // Destroying adapter stops the network thread, so it cannot be
             // called on network thread.
             if (Thread.currentThread() == mNetworkThread) {
-                throw new IllegalThreadStateException(
-                        "Cannot shutdown from network thread.");
+                throw new IllegalThreadStateException("Cannot shutdown from network thread.");
             }
         }
         // Wait for init to complete on main and network thread (without lock,
@@ -213,8 +219,7 @@
     public void startNetLogToFile(String fileName, boolean logAll) {
         synchronized (mLock) {
             checkHaveAdapter();
-            nativeStartNetLogToFile(mUrlRequestContextAdapter, fileName,
-                    logAll);
+            nativeStartNetLogToFile(mUrlRequestContextAdapter, fileName, logAll);
         }
     }
 
@@ -291,7 +296,7 @@
 
     @VisibleForTesting
     @Override
-    void configureNetworkQualityEstimatorForTesting(
+    public void configureNetworkQualityEstimatorForTesting(
             boolean useLocalHostRequests, boolean useSmallerResponses) {
         if (!mNetworkQualityEstimatorEnabled) {
             throw new IllegalStateException("Network quality estimator must be enabled");
@@ -438,7 +443,7 @@
     }
 
     @VisibleForTesting
-    long getUrlRequestContextAdapter() {
+    public long getUrlRequestContextAdapter() {
         synchronized (mLock) {
             checkHaveAdapter();
             return mUrlRequestContextAdapter;
@@ -584,8 +589,7 @@
     private native void nativeDestroy(long nativePtr);
 
     @NativeClassQualifiedName("CronetURLRequestContextAdapter")
-    private native void nativeStartNetLogToFile(long nativePtr,
-            String fileName, boolean logAll);
+    private native void nativeStartNetLogToFile(long nativePtr, String fileName, boolean logAll);
 
     @NativeClassQualifiedName("CronetURLRequestContextAdapter")
     private native void nativeStopNetLog(long nativePtr);
diff --git a/components/cronet/android/java/src/org/chromium/net/Version.template b/components/cronet/android/java/src/org/chromium/net/impl/ImplVersion.template
similarity index 88%
copy from components/cronet/android/java/src/org/chromium/net/Version.template
copy to components/cronet/android/java/src/org/chromium/net/impl/ImplVersion.template
index df25c2f..babb441 100644
--- a/components/cronet/android/java/src/org/chromium/net/Version.template
+++ b/components/cronet/android/java/src/org/chromium/net/impl/ImplVersion.template
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.net;
+package org.chromium.net.impl;
 
 // Version based on chrome/VERSION.
-public class Version {
+public class ImplVersion {
     public static final String CRONET_VERSION = "@MAJOR@.@MINOR@.@BUILD@.@PATCH@";
     public static final String LAST_CHANGE = "@LASTCHANGE@";
 
diff --git a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetChunkedOutputStream.java b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetChunkedOutputStream.java
index 26f3774..0c170879 100644
--- a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetChunkedOutputStream.java
+++ b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetChunkedOutputStream.java
@@ -145,5 +145,6 @@
         checkNotClosed();
         mBuffer.flip();
         mMessageLoop.loop();
+        checkNoException();
     }
 }
diff --git a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetFixedModeOutputStream.java b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetFixedModeOutputStream.java
index 3da3291..60bee37 100644
--- a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetFixedModeOutputStream.java
+++ b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetFixedModeOutputStream.java
@@ -131,6 +131,7 @@
         checkNotClosed();
         mBuffer.flip();
         mMessageLoop.loop();
+        checkNoException();
     }
 
     /**
diff --git a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java
index 191494f..227ae12 100644
--- a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java
+++ b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetHttpURLConnection.java
@@ -509,6 +509,7 @@
             if (mOutputStream != null) {
                 mOutputStream.setRequestCompleted(exception);
             }
+            mHasResponse = true;
             mMessageLoop.quit();
         }
     }
diff --git a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetOutputStream.java b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetOutputStream.java
index 03c4b4b..c73426a 100644
--- a/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetOutputStream.java
+++ b/components/cronet/android/java/src/org/chromium/net/urlconnection/CronetOutputStream.java
@@ -54,13 +54,21 @@
      */
     protected void checkNotClosed() throws IOException {
         if (mRequestCompleted) {
-            if (mException != null) {
-                throw mException;
-            }
+            checkNoException();
             throw new IOException("Writing after request completed.");
         }
         if (mClosed) {
             throw new IOException("Stream has been closed.");
         }
     }
+
+    /**
+     * Throws the same IOException that the request is failed with. If there
+     * is no exception reported, this method is no-op.
+     */
+    protected void checkNoException() throws IOException {
+        if (mException != null) {
+            throw mException;
+        }
+    }
 }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamTest.java
index 3135a87..2cef0df 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/BidirectionalStreamTest.java
@@ -12,6 +12,7 @@
 import org.chromium.net.CronetTestBase.OnlyRunNativeCronet;
 import org.chromium.net.TestBidirectionalStreamCallback.FailureType;
 import org.chromium.net.TestBidirectionalStreamCallback.ResponseStep;
+import org.chromium.net.impl.CronetBidirectionalStream;
 
 import java.nio.ByteBuffer;
 import java.util.AbstractMap;
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUploadTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUploadTest.java
index f2101cc1..6c3ace28 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUploadTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUploadTest.java
@@ -8,6 +8,7 @@
 
 import org.chromium.base.test.util.Feature;
 import org.chromium.net.CronetTestBase.OnlyRunNativeCronet;
+import org.chromium.net.impl.CronetUploadDataStream;
 
 import java.util.Arrays;
 import java.util.List;
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
index 0beee033..e682a14 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestContextTest.java
@@ -20,6 +20,8 @@
 import org.chromium.base.test.util.FlakyTest;
 import org.chromium.net.CronetEngine.UrlRequestInfo;
 import org.chromium.net.TestUrlRequestCallback.ResponseStep;
+import org.chromium.net.impl.CronetLibraryLoader;
+import org.chromium.net.impl.CronetUrlRequestContext;
 import org.chromium.net.test.EmbeddedTestServer;
 
 import java.io.BufferedReader;
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
index b8b1db2d..d5dc3924 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
@@ -13,6 +13,7 @@
 import org.chromium.base.test.util.FlakyTest;
 import org.chromium.net.TestUrlRequestCallback.FailureType;
 import org.chromium.net.TestUrlRequestCallback.ResponseStep;
+import org.chromium.net.impl.CronetUrlRequest;
 import org.chromium.net.test.FailurePhase;
 
 import java.io.IOException;
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/SdchTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/SdchTest.java
index 379403c7..76fa33c 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/SdchTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/SdchTest.java
@@ -9,6 +9,8 @@
 
 import org.chromium.base.test.util.Feature;
 import org.chromium.net.CronetTestBase.OnlyRunNativeCronet;
+import org.chromium.net.impl.ChromiumUrlRequestFactory;
+import org.chromium.net.impl.CronetUrlRequestContext;
 
 import java.io.BufferedReader;
 import java.io.FileReader;
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/UploadTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/UploadTest.java
index 7472bd5..1cd856f 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/UploadTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/UploadTest.java
@@ -7,6 +7,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import org.chromium.base.test.util.Feature;
+import org.chromium.net.impl.ChromiumUrlRequest;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetChunkedOutputStreamTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetChunkedOutputStreamTest.java
index 890be05..32bd8b0 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetChunkedOutputStreamTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetChunkedOutputStreamTest.java
@@ -115,6 +115,47 @@
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
+    public void testGetResponseAfterWriteFailed() throws Exception {
+        URL url = new URL(NativeTestServer.getEchoBodyURL());
+        NativeTestServer.shutdownNativeTestServer();
+        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+        connection.setDoOutput(true);
+        connection.setRequestMethod("POST");
+        // Set 1 byte as chunk size so internally Cronet will try upload when
+        // 1 byte is filled.
+        connection.setChunkedStreamingMode(1);
+        try {
+            OutputStream out = connection.getOutputStream();
+            out.write(1);
+            out.write(1);
+            fail();
+        } catch (IOException e) {
+            if (!testingSystemHttpURLConnection()) {
+                UrlRequestException requestException = (UrlRequestException) e;
+                assertEquals(UrlRequestException.ERROR_CONNECTION_REFUSED,
+                        requestException.getErrorCode());
+            }
+        }
+        // Make sure IOException is reported again when trying to read response
+        // from the connection.
+        try {
+            connection.getResponseCode();
+            fail();
+        } catch (IOException e) {
+            // Expected.
+            if (!testingSystemHttpURLConnection()) {
+                UrlRequestException requestException = (UrlRequestException) e;
+                assertEquals(UrlRequestException.ERROR_CONNECTION_REFUSED,
+                        requestException.getErrorCode());
+            }
+        }
+        // Restarting server to run the test for a second time.
+        assertTrue(NativeTestServer.startNativeTestServer(getContext()));
+    }
+
+    @SmallTest
+    @Feature({"Cronet"})
+    @CompareDefaultWithCronet
     public void testPost() throws Exception {
         URL url = new URL(NativeTestServer.getEchoBodyURL());
         HttpURLConnection connection = (HttpURLConnection) url.openConnection();
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java
index 72f74ae..d7765f1 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/urlconnection/CronetFixedModeOutputStreamTest.java
@@ -97,6 +97,46 @@
     @SmallTest
     @Feature({"Cronet"})
     @CompareDefaultWithCronet
+    public void testGetResponseAfterWriteFailed() throws Exception {
+        URL url = new URL(NativeTestServer.getEchoBodyURL());
+        NativeTestServer.shutdownNativeTestServer();
+        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+        connection.setDoOutput(true);
+        connection.setRequestMethod("POST");
+        // Set content-length as 1 byte, so Cronet will upload once that 1 byte
+        // is passed to it.
+        connection.setFixedLengthStreamingMode(1);
+        try {
+            OutputStream out = connection.getOutputStream();
+            out.write(1);
+            fail();
+        } catch (IOException e) {
+            if (!testingSystemHttpURLConnection()) {
+                UrlRequestException requestException = (UrlRequestException) e;
+                assertEquals(UrlRequestException.ERROR_CONNECTION_REFUSED,
+                        requestException.getErrorCode());
+            }
+        }
+        // Make sure IOException is reported again when trying to read response
+        // from the connection.
+        try {
+            connection.getResponseCode();
+            fail();
+        } catch (IOException e) {
+            // Expected.
+            if (!testingSystemHttpURLConnection()) {
+                UrlRequestException requestException = (UrlRequestException) e;
+                assertEquals(UrlRequestException.ERROR_CONNECTION_REFUSED,
+                        requestException.getErrorCode());
+            }
+        }
+        // Restarting server to run the test for a second time.
+        assertTrue(NativeTestServer.startNativeTestServer(getContext()));
+    }
+
+    @SmallTest
+    @Feature({"Cronet"})
+    @CompareDefaultWithCronet
     public void testFixedLengthStreamingModeZeroContentLength() throws Exception {
         // Check content length is set.
         URL echoLength = new URL(NativeTestServer.getEchoHeaderURL("Content-Length"));
diff --git a/components/cronet/android/test/src/org/chromium/net/CronetTestUtil.java b/components/cronet/android/test/src/org/chromium/net/CronetTestUtil.java
index efd61ea..ea7938a95 100644
--- a/components/cronet/android/test/src/org/chromium/net/CronetTestUtil.java
+++ b/components/cronet/android/test/src/org/chromium/net/CronetTestUtil.java
@@ -8,6 +8,9 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.net.impl.ChromiumUrlRequestFactory;
+import org.chromium.net.impl.CronetUrlRequest;
+import org.chromium.net.impl.CronetUrlRequestContext;
 
 /**
  * Utilities for Cronet testing
diff --git a/components/cronet/cronet_static.gypi b/components/cronet/cronet_static.gypi
index 36dc15f..9ae9442 100644
--- a/components/cronet/cronet_static.gypi
+++ b/components/cronet/cronet_static.gypi
@@ -11,7 +11,8 @@
     'chromium_url_request_java',
     'cronet_android_cert_proto',
     'cronet_jni_headers',
-    'cronet_version',
+    'cronet_api_version',
+    'cronet_impl_version',
     'cronet_version_header',
     'metrics',
     'url_request_error_java',
diff --git a/components/memory_coordinator/browser/BUILD.gn b/components/memory_coordinator/browser/BUILD.gn
index 836ef4b4..0afa65e 100644
--- a/components/memory_coordinator/browser/BUILD.gn
+++ b/components/memory_coordinator/browser/BUILD.gn
@@ -17,3 +17,16 @@
     "//components/memory_coordinator/common",
   ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = [
+    "memory_coordinator_unittest.cc",
+  ]
+
+  deps = [
+    ":browser",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/memory_coordinator/browser/memory_coordinator.cc b/components/memory_coordinator/browser/memory_coordinator.cc
index 634ebf8..b4e714a 100644
--- a/components/memory_coordinator/browser/memory_coordinator.cc
+++ b/components/memory_coordinator/browser/memory_coordinator.cc
@@ -4,6 +4,8 @@
 
 #include "components/memory_coordinator/browser/memory_coordinator.h"
 
+#include "base/memory/memory_pressure_listener.h"
+
 namespace memory_coordinator {
 
 // The implementation of MemoryCoordinatorHandle. See memory_coordinator.mojom
@@ -31,9 +33,23 @@
   DISALLOW_COPY_AND_ASSIGN(MemoryCoordinatorHandleImpl);
 };
 
-MemoryCoordinator::MemoryCoordinator() {}
+MemoryCoordinator::MemoryCoordinator()
+    : pressure_level_dispatcher_(
+          base::Bind(&MemoryCoordinator::OnMemoryPressure,
+                     base::Unretained(this))) {
+  auto* monitor = base::MemoryPressureMonitor::Get();
+  if (monitor) {
+    monitor->SetDispatchCallback(pressure_level_dispatcher_);
+  }
+}
 
-MemoryCoordinator::~MemoryCoordinator() {}
+MemoryCoordinator::~MemoryCoordinator() {
+  auto* monitor = base::MemoryPressureMonitor::Get();
+  if (monitor) {
+    monitor->SetDispatchCallback(
+        base::Bind(&base::MemoryPressureListener::NotifyMemoryPressure));
+  }
+}
 
 void MemoryCoordinator::CreateHandle(
     int render_process_id,
@@ -53,4 +69,19 @@
   children_.erase(render_process_id);
 }
 
+void MemoryCoordinator::OnMemoryPressure(
+    base::MemoryPressureMonitor::MemoryPressureLevel level) {
+  // TODO(bashi): The current implementation just translates memory pressure
+  // levels to memory coordinator states. The logic will be replaced with
+  // the priority tracker.
+  if (level == base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE) {
+    clients()->Notify(FROM_HERE, &MemoryCoordinatorClient::OnMemoryStateChange,
+                      mojom::MemoryState::THROTTLED);
+  } else if (level ==
+             base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) {
+    clients()->Notify(FROM_HERE, &MemoryCoordinatorClient::OnMemoryStateChange,
+                      mojom::MemoryState::SUSPENDED);
+  }
+}
+
 }  // namespace memory_coordinator
diff --git a/components/memory_coordinator/browser/memory_coordinator.h b/components/memory_coordinator/browser/memory_coordinator.h
index 3cf4fb5..51dcb5d 100644
--- a/components/memory_coordinator/browser/memory_coordinator.h
+++ b/components/memory_coordinator/browser/memory_coordinator.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_MEMORY_COORDINATOR_BROWSER_MEMORY_COORDINATOR_H_
 #define COMPONENTS_MEMORY_COORDINATOR_BROWSER_MEMORY_COORDINATOR_H_
 
+#include "base/memory/memory_pressure_monitor.h"
 #include "components/memory_coordinator/common/client_registry.h"
 #include "components/memory_coordinator/public/interfaces/memory_coordinator.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
@@ -30,6 +31,12 @@
  private:
   void OnConnectionError(int render_process_id);
 
+  // Called when MemoryPressureMonitor detects memory pressure.
+  void OnMemoryPressure(base::MemoryPressureMonitor::MemoryPressureLevel level);
+
+  // A callback invoked when MemoryPressureMonitor detects memory pressure.
+  base::MemoryPressureMonitor::DispatchCallback pressure_level_dispatcher_;
+
   // Mappings of RenderProcessHost::GetID() -> MemoryCoordinatorHandleImpl.
   // A mapping is added when a renderer connects to MemoryCoordinator and
   // removed automatically when a underlying binding is disconnected.
diff --git a/components/memory_coordinator/browser/memory_coordinator_unittest.cc b/components/memory_coordinator/browser/memory_coordinator_unittest.cc
new file mode 100644
index 0000000..5fe205e
--- /dev/null
+++ b/components/memory_coordinator/browser/memory_coordinator_unittest.cc
@@ -0,0 +1,91 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/memory_coordinator/browser/memory_coordinator.h"
+
+#include "base/memory/memory_pressure_monitor.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace memory_coordinator {
+
+namespace {
+
+class MockMemoryPressureMonitor : public base::MemoryPressureMonitor {
+ public:
+  ~MockMemoryPressureMonitor() override {}
+
+  void Dispatch(MemoryPressureLevel level) {
+    DCHECK(!callback_.is_null());
+    callback_.Run(level);
+  }
+
+  // MemoryPressureMonitor implementations:
+  MemoryPressureLevel GetCurrentPressureLevel() const override {
+    return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
+  }
+
+  void SetDispatchCallback(const DispatchCallback& callback) override {
+    callback_ = callback;
+  }
+
+ private:
+  DispatchCallback callback_;
+};
+
+class MockMemoryCoordinatorClient final : public MemoryCoordinatorClient {
+public:
+  void OnMemoryStateChange(mojom::MemoryState state) override {
+    last_state_ = state;
+  }
+
+  mojom::MemoryState last_state() const { return last_state_; }
+
+ private:
+  mojom::MemoryState last_state_ = mojom::MemoryState::UNKNOWN;
+};
+
+}  // namespace
+
+class MemoryCoordinatorTest : public testing::Test {
+ public:
+  MemoryCoordinatorTest() : message_loop_(new base::MessageLoop) {
+    // MemoryCoordinator needs to be initialized after an instance of
+    // MemoryPressureMonitor is created.
+    coordinator_.reset(new MemoryCoordinator);
+  }
+
+  MockMemoryPressureMonitor& monitor() { return monitor_; }
+  MemoryCoordinator& coordinator() { return *coordinator_.get(); }
+
+ private:
+  std::unique_ptr<base::MessageLoop> message_loop_;
+  MockMemoryPressureMonitor monitor_;
+  std::unique_ptr<MemoryCoordinator> coordinator_;
+};
+
+TEST_F(MemoryCoordinatorTest, ReceivePressureLevels) {
+  MockMemoryCoordinatorClient client;
+  coordinator().RegisterClient(&client);
+
+  {
+    base::RunLoop loop;
+    monitor().Dispatch(
+        base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
+    loop.RunUntilIdle();
+    EXPECT_EQ(mojom::MemoryState::THROTTLED, client.last_state());
+  }
+
+  {
+    base::RunLoop loop;
+    monitor().Dispatch(
+        base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
+    loop.RunUntilIdle();
+    EXPECT_EQ(mojom::MemoryState::SUSPENDED, client.last_state());
+  }
+}
+
+}  // namespace memory_coordinator
diff --git a/components/memory_coordinator/public/interfaces/child_memory_coordinator.mojom b/components/memory_coordinator/public/interfaces/child_memory_coordinator.mojom
index 59c653eb..f54a3da2 100644
--- a/components/memory_coordinator/public/interfaces/child_memory_coordinator.mojom
+++ b/components/memory_coordinator/public/interfaces/child_memory_coordinator.mojom
@@ -8,8 +8,7 @@
   UNKNOWN = -1,
   NORMAL = 0,
   THROTTLED = 1,
-  // TODO(bashi): Define SUSPENDED when we need it.
-  // SUSPENDED = 2,
+  SUSPENDED = 2,
 };
 
 // ChildMemoryCoordinator lives in a child process and receives memory events
diff --git a/components/ntp_snippets/BUILD.gn b/components/ntp_snippets/BUILD.gn
index 7a0d99c..9dd2e2d4 100644
--- a/components/ntp_snippets/BUILD.gn
+++ b/components/ntp_snippets/BUILD.gn
@@ -73,7 +73,7 @@
 if (is_android) {
   java_cpp_enum("ntp_snippets_java_enums_srcjar") {
     sources = [
-      "ntp_snippets_status_service.h",
+      "content_suggestions_category_status.h",
     ]
   }
 }
diff --git a/components/ntp_snippets/content_suggestion.h b/components/ntp_snippets/content_suggestion.h
index f187b337..d6369dde 100644
--- a/components/ntp_snippets/content_suggestion.h
+++ b/components/ntp_snippets/content_suggestion.h
@@ -17,8 +17,6 @@
 
 // A content suggestion for the new tab page, which can be an article or an
 // offline page, for example.
-// NOTE: This class is not yet in use, please use NTPSnippet for now
-// (see ntp_snippet.h).
 class ContentSuggestion {
  public:
   // Creates a new ContentSuggestion. The caller must ensure that the |id|
diff --git a/components/ntp_snippets/content_suggestions_category_status.cc b/components/ntp_snippets/content_suggestions_category_status.cc
index 7c7b8666..83f9c5479 100644
--- a/components/ntp_snippets/content_suggestions_category_status.cc
+++ b/components/ntp_snippets/content_suggestions_category_status.cc
@@ -8,8 +8,16 @@
 
 bool IsContentSuggestionsCategoryStatusAvailable(
     ContentSuggestionsCategoryStatus status) {
+  // Note: This code is duplicated in SnippetsBridge.java.
   return status == ContentSuggestionsCategoryStatus::AVAILABLE_LOADING ||
          status == ContentSuggestionsCategoryStatus::AVAILABLE;
 }
 
+bool IsContentSuggestionsCategoryStatusInitOrAvailable(
+    ContentSuggestionsCategoryStatus status) {
+  // Note: This code is duplicated in SnippetsBridge.java.
+  return status == ContentSuggestionsCategoryStatus::INITIALIZING ||
+         IsContentSuggestionsCategoryStatusAvailable(status);
+}
+
 }  // namespace ntp_snippets
diff --git a/components/ntp_snippets/content_suggestions_category_status.h b/components/ntp_snippets/content_suggestions_category_status.h
index 16b65af0..83b5fa2 100644
--- a/components/ntp_snippets/content_suggestions_category_status.h
+++ b/components/ntp_snippets/content_suggestions_category_status.h
@@ -50,6 +50,12 @@
 bool IsContentSuggestionsCategoryStatusAvailable(
     ContentSuggestionsCategoryStatus status);
 
+// Determines whether the given status is INITIALIZING or one of the AVAILABLE
+// statuses. All of these statuses have in common that there is or will soon be
+// content.
+bool IsContentSuggestionsCategoryStatusInitOrAvailable(
+    ContentSuggestionsCategoryStatus status);
+
 }  // namespace ntp_snippets
 
 #endif  // COMPONENTS_NTP_SNIPPETS_CONTENT_SUGGESTIONS_CATEGORY_STATUS_H_
diff --git a/components/ntp_snippets/content_suggestions_provider.h b/components/ntp_snippets/content_suggestions_provider.h
index 43f211bd..995db450 100644
--- a/components/ntp_snippets/content_suggestions_provider.h
+++ b/components/ntp_snippets/content_suggestions_provider.h
@@ -71,11 +71,11 @@
   virtual ContentSuggestionsCategoryStatus GetCategoryStatus(
       ContentSuggestionsCategory category) = 0;
 
-  // Discards the suggestion with the given ID. A provider needs to ensure that
-  // a once-discarded suggestion is never delivered again (through the
+  // Dismisses the suggestion with the given ID. A provider needs to ensure that
+  // a once-dismissed suggestion is never delivered again (through the
   // Observer). The provider must not call Observer::OnSuggestionsChanged if the
-  // removal of the discarded suggestion is the only change.
-  virtual void DiscardSuggestion(const std::string& suggestion_id) = 0;
+  // removal of the dismissed suggestion is the only change.
+  virtual void DismissSuggestion(const std::string& suggestion_id) = 0;
 
   // Fetches the image for the suggestion with the given ID and returns it
   // through the callback. This fetch may occur locally or from the internet.
@@ -88,11 +88,11 @@
   // fetch starts from scratch.
   virtual void ClearCachedSuggestionsForDebugging() = 0;
 
-  // Used only for debugging purposes. Clears the cache of discarded
+  // Used only for debugging purposes. Clears the cache of dismissed
   // suggestions, if present, so that no suggestions are suppressed. This does
-  // not necessarily make previously discarded suggestions reappear, as they may
+  // not necessarily make previously dismissed suggestions reappear, as they may
   // have been permanently deleted, depending on the provider implementation.
-  virtual void ClearDiscardedSuggestionsForDebugging() = 0;
+  virtual void ClearDismissedSuggestionsForDebugging() = 0;
 
   const std::vector<ContentSuggestionsCategory>& provided_categories() const {
     return provided_categories_;
diff --git a/components/ntp_snippets/content_suggestions_service.cc b/components/ntp_snippets/content_suggestions_service.cc
index 3ce179f4..778bb9e 100644
--- a/components/ntp_snippets/content_suggestions_service.cc
+++ b/components/ntp_snippets/content_suggestions_service.cc
@@ -77,25 +77,25 @@
   FOR_EACH_OBSERVER(Observer, observers_, OnNewSuggestions());
 }
 
-void ContentSuggestionsService::ClearDiscardedSuggestionsForDebugging() {
+void ContentSuggestionsService::ClearDismissedSuggestionsForDebugging() {
   for (auto& category_provider_pair : providers_) {
-    category_provider_pair.second->ClearDiscardedSuggestionsForDebugging();
+    category_provider_pair.second->ClearDismissedSuggestionsForDebugging();
   }
 }
 
-void ContentSuggestionsService::DiscardSuggestion(
+void ContentSuggestionsService::DismissSuggestion(
     const std::string& suggestion_id) {
   if (!id_category_map_.count(suggestion_id)) {
-    LOG(WARNING) << "Discarded unknown suggestion " << suggestion_id;
+    LOG(WARNING) << "Dismissed unknown suggestion " << suggestion_id;
     return;
   }
   ContentSuggestionsCategory category = id_category_map_[suggestion_id];
   if (!providers_.count(category)) {
-    LOG(WARNING) << "Discarded suggestion " << suggestion_id
+    LOG(WARNING) << "Dismissed suggestion " << suggestion_id
                  << " for unavailable category " << static_cast<int>(category);
     return;
   }
-  providers_[category]->DiscardSuggestion(suggestion_id);
+  providers_[category]->DismissSuggestion(suggestion_id);
 
   // Remove the suggestion locally.
   id_category_map_.erase(suggestion_id);
@@ -106,7 +106,10 @@
                    [&suggestion_id](const ContentSuggestion& suggestion) {
                      return suggestion_id == suggestion.id();
                    });
-  DCHECK(position != suggestions->end());
+  DCHECK(position != suggestions->end())
+      << "The dismissed suggestion " << suggestion_id
+      << " has already been removed. Providers must not call OnNewSuggestions"
+         " in response to DismissSuggestion.";
   suggestions->erase(position);
 }
 
diff --git a/components/ntp_snippets/content_suggestions_service.h b/components/ntp_snippets/content_suggestions_service.h
index 71e455a..56666f2 100644
--- a/components/ntp_snippets/content_suggestions_service.h
+++ b/components/ntp_snippets/content_suggestions_service.h
@@ -25,8 +25,6 @@
 
 // Retrieves suggestions from a number of ContentSuggestionsProviders and serves
 // them grouped into categories. There can be at most one provider per category.
-// NOTE: This class is not yet in use, please use NTPSnippetsService for now
-// (see ntp_snippets_service.h).
 class ContentSuggestionsService : public KeyedService,
                                   public ContentSuggestionsProvider::Observer {
  public:
@@ -94,9 +92,9 @@
   void FetchSuggestionImage(const std::string& suggestion_id,
                             const ImageFetchedCallback& callback);
 
-  // Discards the suggestion with the given |suggestion_id|, if it exists.
+  // Dismisses the suggestion with the given |suggestion_id|, if it exists.
   // This will not trigger an update through the observers.
-  void DiscardSuggestion(const std::string& suggestion_id);
+  void DismissSuggestion(const std::string& suggestion_id);
 
   // Observer accessors.
   void AddObserver(Observer* observer);
@@ -112,14 +110,14 @@
   // providers. It does, however, not remove any suggestions from the provider's
   // sources, so if their configuration hasn't changed, they should return the
   // same results when they fetch the next time. In particular, calling this
-  // method will not mark any suggestions as discarded.
+  // method will not mark any suggestions as dismissed.
   void ClearCachedSuggestionsForDebugging();
 
   // Only for debugging use through the internals page. Some providers
-  // internally store a list of discarded suggestions to prevent them from
+  // internally store a list of dismissed suggestions to prevent them from
   // reappearing. This function clears all such lists in all providers, making
-  // discarded suggestions reappear (only for certain providers).
-  void ClearDiscardedSuggestionsForDebugging();
+  // dismissed suggestions reappear (only for certain providers).
+  void ClearDismissedSuggestionsForDebugging();
 
  private:
   friend class ContentSuggestionsServiceTest;
diff --git a/components/ntp_snippets/content_suggestions_service_unittest.cc b/components/ntp_snippets/content_suggestions_service_unittest.cc
index 6d5d85b..02bbdbd 100644
--- a/components/ntp_snippets/content_suggestions_service_unittest.cc
+++ b/components/ntp_snippets/content_suggestions_service_unittest.cc
@@ -86,8 +86,8 @@
   }
 
   MOCK_METHOD0(ClearCachedSuggestionsForDebugging, void());
-  MOCK_METHOD0(ClearDiscardedSuggestionsForDebugging, void());
-  MOCK_METHOD1(DiscardSuggestion, void(const std::string& suggestion_id));
+  MOCK_METHOD0(ClearDismissedSuggestionsForDebugging, void());
+  MOCK_METHOD1(DismissSuggestion, void(const std::string& suggestion_id));
   MOCK_METHOD2(FetchSuggestionImage,
                void(const std::string& suggestion_id,
                     const ImageFetchedCallback& callback));
@@ -305,7 +305,7 @@
                                 base::Unretained(this)));
 }
 
-TEST_F(ContentSuggestionsServiceTest, ShouldRedirectDiscardSuggestion) {
+TEST_F(ContentSuggestionsServiceTest, ShouldRedirectDismissSuggestion) {
   MockProvider provider1(ContentSuggestionsCategory::ARTICLES);
   MockProvider provider2(ContentSuggestionsCategory::OFFLINE_PAGES);
   service()->RegisterProvider(&provider1);
@@ -315,9 +315,9 @@
                                    {11});
   std::string suggestion_id = CreateSuggestion(11).id();
 
-  EXPECT_CALL(provider1, DiscardSuggestion(_)).Times(0);
-  EXPECT_CALL(provider2, DiscardSuggestion(suggestion_id)).Times(1);
-  service()->DiscardSuggestion(suggestion_id);
+  EXPECT_CALL(provider1, DismissSuggestion(_)).Times(0);
+  EXPECT_CALL(provider2, DismissSuggestion(suggestion_id)).Times(1);
+  service()->DismissSuggestion(suggestion_id);
   provider1.FireShutdown();
   provider2.FireShutdown();
 }
diff --git a/components/ntp_snippets/ntp_snippet.cc b/components/ntp_snippets/ntp_snippet.cc
index 101367f..6e3e0176 100644
--- a/components/ntp_snippets/ntp_snippet.cc
+++ b/components/ntp_snippets/ntp_snippet.cc
@@ -39,7 +39,7 @@
 namespace ntp_snippets {
 
 NTPSnippet::NTPSnippet(const std::string& id)
-    : id_(id), score_(0), is_discarded_(false), best_source_index_(0) {}
+    : id_(id), score_(0), is_dismissed_(false), best_source_index_(0) {}
 
 NTPSnippet::~NTPSnippet() {}
 
@@ -184,7 +184,7 @@
       base::Time::FromInternalValue(proto.publish_date()));
   snippet->set_expiry_date(base::Time::FromInternalValue(proto.expiry_date()));
   snippet->set_score(proto.score());
-  snippet->set_discarded(proto.discarded());
+  snippet->set_dismissed(proto.dismissed());
 
   for (int i = 0; i < proto.sources_size(); ++i) {
     const SnippetSourceProto& source_proto = proto.sources(i);
@@ -230,7 +230,7 @@
   if (!expiry_date_.is_null())
     result.set_expiry_date(expiry_date_.ToInternalValue());
   result.set_score(score_);
-  result.set_discarded(is_discarded_);
+  result.set_dismissed(is_dismissed_);
 
   for (const SnippetSource& source : sources_) {
     SnippetSourceProto* source_proto = result.add_sources();
diff --git a/components/ntp_snippets/ntp_snippet.h b/components/ntp_snippets/ntp_snippet.h
index 2d92bd3..18b0729 100644
--- a/components/ntp_snippets/ntp_snippet.h
+++ b/components/ntp_snippets/ntp_snippet.h
@@ -122,8 +122,8 @@
   float score() const { return score_; }
   void set_score(float score) { score_ = score; }
 
-  bool is_discarded() const { return is_discarded_; }
-  void set_discarded(bool discarded) { is_discarded_ = discarded; }
+  bool is_dismissed() const { return is_dismissed_; }
+  void set_dismissed(bool dismissed) { is_dismissed_ = dismissed; }
 
   // Public for testing.
   static base::Time TimeFromJsonString(const std::string& timestamp_str);
@@ -139,7 +139,7 @@
   base::Time publish_date_;
   base::Time expiry_date_;
   float score_;
-  bool is_discarded_;
+  bool is_dismissed_;
 
   size_t best_source_index_;
 
diff --git a/components/ntp_snippets/ntp_snippets_database_unittest.cc b/components/ntp_snippets/ntp_snippets_database_unittest.cc
index 1640d43e..341eeb12a 100644
--- a/components/ntp_snippets/ntp_snippets_database_unittest.cc
+++ b/components/ntp_snippets/ntp_snippets_database_unittest.cc
@@ -37,7 +37,7 @@
          lhs.expiry_date() == rhs.expiry_date() &&
          lhs.source_index() == rhs.source_index() &&
          lhs.sources() == rhs.sources() && lhs.score() == rhs.score() &&
-         lhs.is_discarded() == rhs.is_discarded();
+         lhs.is_dismissed() == rhs.is_dismissed();
 }
 
 namespace {
diff --git a/components/ntp_snippets/ntp_snippets_service.cc b/components/ntp_snippets/ntp_snippets_service.cc
index 736eb493..dfa66ce8 100644
--- a/components/ntp_snippets/ntp_snippets_service.cc
+++ b/components/ntp_snippets/ntp_snippets_service.cc
@@ -280,10 +280,11 @@
 void NTPSnippetsService::FetchSuggestionImage(
     const std::string& suggestion_id,
     const ImageFetchedCallback& callback) {
+  std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id);
   database_->LoadImage(
-      suggestion_id,
+      snippet_id,
       base::Bind(&NTPSnippetsService::OnSnippetImageFetchedFromDatabase,
-                 base::Unretained(this), suggestion_id, callback));
+                 base::Unretained(this), snippet_id, callback));
 }
 
 void NTPSnippetsService::ClearCachedSuggestionsForDebugging() {
@@ -309,38 +310,38 @@
       suggestions_service_->GetSuggestionsDataFromCache());
 }
 
-void NTPSnippetsService::DiscardSuggestion(const std::string& suggestion_id) {
+void NTPSnippetsService::DismissSuggestion(const std::string& suggestion_id) {
   if (!ready())
     return;
 
-  auto it = std::find_if(
-      snippets_.begin(), snippets_.end(),
-      [&suggestion_id](const std::unique_ptr<NTPSnippet>& snippet) {
-        return snippet->id() == suggestion_id;
-      });
+  std::string snippet_id = GetWithinCategoryIDFromUniqueID(suggestion_id);
+
+  auto it =
+      std::find_if(snippets_.begin(), snippets_.end(),
+                   [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) {
+                     return snippet->id() == snippet_id;
+                   });
   if (it == snippets_.end())
     return;
 
-  (*it)->set_discarded(true);
+  (*it)->set_dismissed(true);
 
   database_->SaveSnippet(**it);
   database_->DeleteImage((*it)->id());
 
-  discarded_snippets_.push_back(std::move(*it));
+  dismissed_snippets_.push_back(std::move(*it));
   snippets_.erase(it);
-
-  NotifyNewSuggestions();
 }
 
-void NTPSnippetsService::ClearDiscardedSuggestionsForDebugging() {
+void NTPSnippetsService::ClearDismissedSuggestionsForDebugging() {
   if (!initialized())
     return;
 
-  if (discarded_snippets_.empty())
+  if (dismissed_snippets_.empty())
     return;
 
-  database_->DeleteSnippets(discarded_snippets_);
-  discarded_snippets_.clear();
+  database_->DeleteSnippets(dismissed_snippets_);
+  dismissed_snippets_.clear();
 }
 
 void NTPSnippetsService::SetObserver(Observer* observer) {
@@ -393,10 +394,10 @@
     return;
 
   DCHECK(snippets_.empty());
-  DCHECK(discarded_snippets_.empty());
+  DCHECK(dismissed_snippets_.empty());
   for (std::unique_ptr<NTPSnippet>& snippet : snippets) {
-    if (snippet->is_discarded())
-      discarded_snippets_.emplace_back(std::move(snippet));
+    if (snippet->is_dismissed())
+      dismissed_snippets_.emplace_back(std::move(snippet));
     else
       snippets_.emplace_back(std::move(snippet));
   }
@@ -469,9 +470,9 @@
 
   UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles",
                               snippets_.size());
-  if (snippets_.empty() && !discarded_snippets_.empty()) {
+  if (snippets_.empty() && !dismissed_snippets_.empty()) {
     UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded",
-                         discarded_snippets_.size());
+                         dismissed_snippets_.size());
   }
 
   NotifyNewSuggestions();
@@ -480,9 +481,9 @@
 void NTPSnippetsService::MergeSnippets(NTPSnippet::PtrVector new_snippets) {
   DCHECK(ready());
 
-  // Remove new snippets that we already have, or that have been discarded.
+  // Remove new snippets that we already have, or that have been dismissed.
   std::set<std::string> old_snippet_ids;
-  InsertAllIDs(discarded_snippets_, &old_snippet_ids);
+  InsertAllIDs(dismissed_snippets_, &old_snippet_ids);
   InsertAllIDs(snippets_, &old_snippet_ids);
   new_snippets.erase(
       std::remove_if(
@@ -522,12 +523,12 @@
                          return !snippet->is_complete();
                        }),
         new_snippets.end());
-    int num_snippets_discarded = num_new_snippets - new_snippets.size();
+    int num_snippets_dismissed = num_new_snippets - new_snippets.size();
     UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch",
-                          num_snippets_discarded > 0);
-    if (num_snippets_discarded > 0) {
+                          num_snippets_dismissed > 0);
+    if (num_snippets_dismissed > 0) {
       UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumIncompleteSnippets",
-                                  num_snippets_discarded);
+                                  num_snippets_dismissed);
     }
   }
 
@@ -571,18 +572,18 @@
   }
   Compact(&snippets_);
 
-  // Move expired discarded snippets over into |to_delete| as well.
-  for (std::unique_ptr<NTPSnippet>& snippet : discarded_snippets_) {
+  // Move expired dismissed snippets over into |to_delete| as well.
+  for (std::unique_ptr<NTPSnippet>& snippet : dismissed_snippets_) {
     if (snippet->expiry_date() <= expiry)
       to_delete.emplace_back(std::move(snippet));
   }
-  Compact(&discarded_snippets_);
+  Compact(&dismissed_snippets_);
 
   // Finally, actually delete the removed snippets from the DB.
   database_->DeleteSnippets(to_delete);
 
   // If there are any snippets left, schedule a timer for the next expiry.
-  if (snippets_.empty() && discarded_snippets_.empty())
+  if (snippets_.empty() && dismissed_snippets_.empty())
     return;
 
   base::Time next_expiry = base::Time::Max();
@@ -590,7 +591,7 @@
     if (snippet->expiry_date() < next_expiry)
       next_expiry = snippet->expiry_date();
   }
-  for (const auto& snippet : discarded_snippets_) {
+  for (const auto& snippet : dismissed_snippets_) {
     if (snippet->expiry_date() < next_expiry)
       next_expiry = snippet->expiry_date();
   }
@@ -622,7 +623,8 @@
     const ImageFetchedCallback& callback,
     const gfx::Image& image) {
   if (!image.IsEmpty()) {
-    callback.Run(snippet_id, image);
+    callback.Run(MakeUniqueID(ContentSuggestionsCategory::ARTICLES, snippet_id),
+                 image);
     return;
   }
 
@@ -641,7 +643,8 @@
                      return snippet->id() == snippet_id;
                    });
   if (it == snippets_.end()) {
-    callback.Run(snippet_id, gfx::Image());
+    callback.Run(MakeUniqueID(ContentSuggestionsCategory::ARTICLES, snippet_id),
+                 gfx::Image());
     return;
   }
 
@@ -667,7 +670,7 @@
 
 void NTPSnippetsService::EnterStateDisabled() {
   ClearCachedSuggestionsForDebugging();
-  ClearDiscardedSuggestionsForDebugging();
+  ClearDismissedSuggestionsForDebugging();
 
   expiry_timer_.Stop();
   suggestions_service_subscription_.reset();
diff --git a/components/ntp_snippets/ntp_snippets_service.h b/components/ntp_snippets/ntp_snippets_service.h
index 9b2562d..90bd292 100644
--- a/components/ntp_snippets/ntp_snippets_service.h
+++ b/components/ntp_snippets/ntp_snippets_service.h
@@ -61,8 +61,9 @@
 class NTPSnippetsDatabase;
 class NTPSnippetsServiceObserver;
 
-// Stores and vends fresh content data for the NTP.
-// TODO(pke): Rename this service to ArticleSuggestionsService and move to
+// Retrieves fresh content data (articles) from the server, stores them and
+// provides them as content suggestions.
+// TODO(pke): Rename this service to ArticleSuggestionsProvider and move to
 // a subdirectory.
 class NTPSnippetsService : public KeyedService,
                            public image_fetcher::ImageFetcherDelegate,
@@ -91,7 +92,7 @@
   void Shutdown() override;
 
   // Returns whether the service is ready. While this is false, the list of
-  // snippets will be empty, and all modifications to it (fetch, discard, etc)
+  // snippets will be empty, and all modifications to it (fetch, dismiss, etc)
   // will be ignored.
   bool ready() const { return state_ == State::READY; }
 
@@ -110,10 +111,10 @@
   // Available snippets.
   const NTPSnippet::PtrVector& snippets() const { return snippets_; }
 
-  // Returns the list of snippets previously discarded by the user (that are
+  // Returns the list of snippets previously dismissed by the user (that are
   // not expired yet).
-  const NTPSnippet::PtrVector& discarded_snippets() const {
-    return discarded_snippets_;
+  const NTPSnippet::PtrVector& dismissed_snippets() const {
+    return dismissed_snippets_;
   }
 
   const NTPSnippetsFetcher* snippets_fetcher() const {
@@ -136,11 +137,11 @@
   void SetObserver(Observer* observer) override;
   ContentSuggestionsCategoryStatus GetCategoryStatus(
       ContentSuggestionsCategory category) override;
-  void DiscardSuggestion(const std::string& suggestion_id) override;
+  void DismissSuggestion(const std::string& suggestion_id) override;
   void FetchSuggestionImage(const std::string& suggestion_id,
                             const ImageFetchedCallback& callback) override;
   void ClearCachedSuggestionsForDebugging() override;
-  void ClearDiscardedSuggestionsForDebugging() override;
+  void ClearDismissedSuggestionsForDebugging() override;
 
   // Returns the lists of suggestion hosts the snippets are restricted to.
   std::set<std::string> GetSuggestionsHosts() const;
@@ -153,6 +154,7 @@
   static int GetMaxSnippetCountForTesting();
 
  private:
+  friend class NTPSnippetsServiceTest;
   FRIEND_TEST_ALL_PREFIXES(NTPSnippetsServiceTest, HistorySyncStateChanges);
 
   // TODO(pke): As soon as the DisabledReason is replaced with the new status,
@@ -214,7 +216,7 @@
   std::set<std::string> GetSnippetHostsFromPrefs() const;
   void StoreSnippetHostsToPrefs(const std::set<std::string>& hosts);
 
-  // Removes the expired snippets (including discarded) from the service and the
+  // Removes the expired snippets (including dismissed) from the service and the
   // database, and schedules another pass for the next expiration.
   void ClearExpiredSnippets();
 
@@ -270,12 +272,12 @@
 
   suggestions::SuggestionsService* suggestions_service_;
 
-  // All current suggestions (i.e. not discarded ones).
+  // All current suggestions (i.e. not dismissed ones).
   NTPSnippet::PtrVector snippets_;
 
-  // Suggestions that the user discarded. We keep these around until they expire
+  // Suggestions that the user dismissed. We keep these around until they expire
   // so we won't re-add them on the next fetch.
-  NTPSnippet::PtrVector discarded_snippets_;
+  NTPSnippet::PtrVector dismissed_snippets_;
 
   // The ISO 639-1 code of the language used by the application.
   const std::string application_language_code_;
diff --git a/components/ntp_snippets/ntp_snippets_service_unittest.cc b/components/ntp_snippets/ntp_snippets_service_unittest.cc
index b5019c9..160a967 100644
--- a/components/ntp_snippets/ntp_snippets_service_unittest.cc
+++ b/components/ntp_snippets/ntp_snippets_service_unittest.cc
@@ -334,6 +334,11 @@
       WaitForDBLoad(service_.get());
   }
 
+  std::string MakeUniqueID(const std::string& within_category_id) {
+    return NTPSnippetsService::MakeUniqueID(
+        ContentSuggestionsCategory::ARTICLES, within_category_id);
+  }
+
  protected:
   const GURL& test_url() { return test_url_; }
   NTPSnippetsService* service() { return service_.get(); }
@@ -486,7 +491,7 @@
   EXPECT_THAT(service()->snippets(), SizeIs(1));
 }
 
-TEST_F(NTPSnippetsServiceTest, Discard) {
+TEST_F(NTPSnippetsServiceTest, Dismiss) {
   std::vector<std::string> source_urls, publishers, amp_urls;
   source_urls.push_back(std::string("http://site.com"));
   publishers.push_back(std::string("Source 1"));
@@ -498,44 +503,44 @@
 
   ASSERT_THAT(service()->snippets(), SizeIs(1));
 
-  // Discarding a non-existent snippet shouldn't do anything.
-  service()->DiscardSuggestion("http://othersite.com");
+  // Dismissing a non-existent snippet shouldn't do anything.
+  service()->DismissSuggestion(MakeUniqueID("http://othersite.com"));
   EXPECT_THAT(service()->snippets(), SizeIs(1));
 
-  // Discard the snippet.
-  service()->DiscardSuggestion(kSnippetUrl);
+  // Dismiss the snippet.
+  service()->DismissSuggestion(MakeUniqueID(kSnippetUrl));
   EXPECT_THAT(service()->snippets(), IsEmpty());
 
   // Make sure that fetching the same snippet again does not re-add it.
   LoadFromJSONString(json_str);
   EXPECT_THAT(service()->snippets(), IsEmpty());
 
-  // The snippet should stay discarded even after re-creating the service.
+  // The snippet should stay dismissed even after re-creating the service.
   EXPECT_CALL(mock_scheduler(), Schedule(_, _, _, _)).Times(1);
   CreateSnippetsService(/*enabled=*/true);
   LoadFromJSONString(json_str);
   EXPECT_THAT(service()->snippets(), IsEmpty());
 
-  // The snippet can be added again after clearing discarded snippets.
-  service()->ClearDiscardedSuggestionsForDebugging();
+  // The snippet can be added again after clearing dismissed snippets.
+  service()->ClearDismissedSuggestionsForDebugging();
   EXPECT_THAT(service()->snippets(), IsEmpty());
   LoadFromJSONString(json_str);
   EXPECT_THAT(service()->snippets(), SizeIs(1));
 }
 
-TEST_F(NTPSnippetsServiceTest, GetDiscarded) {
+TEST_F(NTPSnippetsServiceTest, GetDismissed) {
   LoadFromJSONString(GetTestJson({GetSnippet()}));
 
-  service()->DiscardSuggestion(kSnippetUrl);
-  const NTPSnippet::PtrVector& snippets = service()->discarded_snippets();
+  service()->DismissSuggestion(MakeUniqueID(kSnippetUrl));
+  const NTPSnippet::PtrVector& snippets = service()->dismissed_snippets();
   EXPECT_EQ(1u, snippets.size());
   for (auto& snippet : snippets) {
     EXPECT_EQ(kSnippetUrl, snippet->id());
   }
 
-  // There should be no discarded snippet after clearing the list.
-  service()->ClearDiscardedSuggestionsForDebugging();
-  EXPECT_EQ(0u, service()->discarded_snippets().size());
+  // There should be no dismissed snippet after clearing the list.
+  service()->ClearDismissedSuggestionsForDebugging();
+  EXPECT_EQ(0u, service()->dismissed_snippets().size());
 }
 
 TEST_F(NTPSnippetsServiceTest, CreationTimestampParseFail) {
@@ -810,14 +815,14 @@
   EXPECT_THAT(
       tester.GetAllSamples("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded"),
       IsEmpty());
-  // Discarding a snippet should decrease the list size. This will only be
+  // Dismissing a snippet should decrease the list size. This will only be
   // logged after the next fetch.
-  service()->DiscardSuggestion(kSnippetUrl);
+  service()->DismissSuggestion(MakeUniqueID(kSnippetUrl));
   LoadFromJSONString(GetTestJson({GetSnippet()}));
   EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticles"),
               ElementsAre(base::Bucket(/*min=*/0, /*count=*/3),
                           base::Bucket(/*min=*/1, /*count=*/2)));
-  // Discarded snippets shouldn't influence NumArticlesFetched.
+  // Dismissed snippets shouldn't influence NumArticlesFetched.
   EXPECT_THAT(tester.GetAllSamples("NewTabPage.Snippets.NumArticlesFetched"),
               ElementsAre(base::Bucket(/*min=*/0, /*count=*/1),
                           base::Bucket(/*min=*/1, /*count=*/3)));
@@ -831,7 +836,7 @@
   tester.ExpectTotalCount("NewTabPage.Snippets.NumArticlesFetched", 4);
 }
 
-TEST_F(NTPSnippetsServiceTest, DiscardShouldRespectAllKnownUrls) {
+TEST_F(NTPSnippetsServiceTest, DismissShouldRespectAllKnownUrls) {
   const std::string creation =
       NTPSnippet::TimeToJsonString(GetDefaultCreationTime());
   const std::string expiry =
@@ -850,11 +855,11 @@
   LoadFromJSONString(GetTestJson({GetSnippetWithUrlAndTimesAndSources(
       source_urls[0], creation, expiry, source_urls, publishers, amp_urls)}));
   ASSERT_THAT(service()->snippets(), SizeIs(1));
-  // Discard the snippet via the mashable source corpus ID.
-  service()->DiscardSuggestion(source_urls[0]);
+  // Dismiss the snippet via the mashable source corpus ID.
+  service()->DismissSuggestion(MakeUniqueID(source_urls[0]));
   EXPECT_THAT(service()->snippets(), IsEmpty());
 
-  // The same article from the AOL domain should now be detected as discarded.
+  // The same article from the AOL domain should now be detected as dismissed.
   LoadFromJSONString(GetTestJson({GetSnippetWithUrlAndTimesAndSources(
       source_urls[1], creation, expiry, source_urls, publishers, amp_urls)}));
   ASSERT_THAT(service()->snippets(), IsEmpty());
diff --git a/components/ntp_snippets/ntp_snippets_status_service.h b/components/ntp_snippets/ntp_snippets_status_service.h
index 5048246..5b0fcb8 100644
--- a/components/ntp_snippets/ntp_snippets_status_service.h
+++ b/components/ntp_snippets/ntp_snippets_status_service.h
@@ -21,8 +21,6 @@
 
 namespace ntp_snippets {
 
-// On Android builds, a Java counterpart will be generated for this enum.
-// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.ntp.snippets
 enum class DisabledReason : int {
   // Snippets are enabled
   NONE,
diff --git a/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.cc b/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.cc
index ebed24c..ed0dd39 100644
--- a/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.cc
+++ b/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.cc
@@ -46,10 +46,10 @@
   return category_status_;
 }
 
-void OfflinePageSuggestionsProvider::DiscardSuggestion(
+void OfflinePageSuggestionsProvider::DismissSuggestion(
     const std::string& suggestion_id) {
   // TODO(pke): Implement some "dont show on NTP anymore" behaviour,
-  // then also implement ClearDiscardedSuggestionsForDebugging.
+  // then also implement ClearDismissedSuggestionsForDebugging.
 }
 
 void OfflinePageSuggestionsProvider::FetchSuggestionImage(
@@ -62,8 +62,8 @@
   // Ignored.
 }
 
-void OfflinePageSuggestionsProvider::ClearDiscardedSuggestionsForDebugging() {
-  // TODO(pke): Implement when discarded suggestions are supported.
+void OfflinePageSuggestionsProvider::ClearDismissedSuggestionsForDebugging() {
+  // TODO(pke): Implement when dismissed suggestions are supported.
 }
 
 void OfflinePageSuggestionsProvider::OfflinePageModelLoaded(
diff --git a/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.h b/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.h
index 2abc638..651342d7 100644
--- a/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.h
+++ b/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.h
@@ -43,11 +43,11 @@
   void SetObserver(ContentSuggestionsProvider::Observer* observer) override;
   ContentSuggestionsCategoryStatus GetCategoryStatus(
       ContentSuggestionsCategory category) override;
-  void DiscardSuggestion(const std::string& suggestion_id) override;
+  void DismissSuggestion(const std::string& suggestion_id) override;
   void FetchSuggestionImage(const std::string& suggestion_id,
                             const ImageFetchedCallback& callback) override;
   void ClearCachedSuggestionsForDebugging() override;
-  void ClearDiscardedSuggestionsForDebugging() override;
+  void ClearDismissedSuggestionsForDebugging() override;
 
   // OfflinePageModel::Observer implementation.
   void OfflinePageModelLoaded(offline_pages::OfflinePageModel* model) override;
diff --git a/components/ntp_snippets/proto/ntp_snippets.proto b/components/ntp_snippets/proto/ntp_snippets.proto
index 0225dc69..74076b7 100644
--- a/components/ntp_snippets/proto/ntp_snippets.proto
+++ b/components/ntp_snippets/proto/ntp_snippets.proto
@@ -23,7 +23,7 @@
   optional int64 expiry_date = 6;
   optional float score = 7;
   repeated SnippetSourceProto sources = 8;
-  optional bool discarded = 9;
+  optional bool dismissed = 9;
 }
 
 message SnippetImageProto {
diff --git a/components/password_manager/core/browser/login_database_ios_unittest.cc b/components/password_manager/core/browser/login_database_ios_unittest.cc
index 201bafe5..b558013c 100644
--- a/components/password_manager/core/browser/login_database_ios_unittest.cc
+++ b/components/password_manager/core/browser/login_database_ios_unittest.cc
@@ -82,6 +82,13 @@
 }
 
 TEST_F(LoginDatabaseIOSTest, KeychainStorage) {
+#if TARGET_IPHONE_SIMULATOR
+  // TODO(crbug.com/619982): Broken by iOS10.
+  if (base::ios::IsRunningOnIOS10OrLater()) {
+    return;
+  }
+#endif
+
   base::string16 test_passwords[] = {
       base::ASCIIToUTF16("foo"), base::ASCIIToUTF16("bar"),
       base::WideToUTF16(L"\u043F\u0430\u0440\u043E\u043B\u044C"),
@@ -101,6 +108,13 @@
 }
 
 TEST_F(LoginDatabaseIOSTest, UpdateLogin) {
+#if TARGET_IPHONE_SIMULATOR
+  // TODO(crbug.com/619982): Broken by iOS10.
+  if (base::ios::IsRunningOnIOS10OrLater()) {
+    return;
+  }
+#endif
+
   PasswordForm form;
   form.origin = GURL("http://0.com");
   form.signon_realm = "http://www.example.com";
@@ -125,6 +139,13 @@
 }
 
 TEST_F(LoginDatabaseIOSTest, RemoveLogin) {
+#if TARGET_IPHONE_SIMULATOR
+  // TODO(crbug.com/619982): Broken by iOS10.
+  if (base::ios::IsRunningOnIOS10OrLater()) {
+    return;
+  }
+#endif
+
   PasswordForm form;
   form.signon_realm = "www.example.com";
   form.action = GURL("www.example.com/action");
@@ -143,6 +164,13 @@
 }
 
 TEST_F(LoginDatabaseIOSTest, RemoveLoginsCreatedBetween) {
+#if TARGET_IPHONE_SIMULATOR
+  // TODO(crbug.com/619982): Broken by iOS10.
+  if (base::ios::IsRunningOnIOS10OrLater()) {
+    return;
+  }
+#endif
+
   PasswordForm forms[3];
   forms[0].origin = GURL("http://0.com");
   forms[0].signon_realm = "http://www.example.com";
diff --git a/components/password_manager/core/browser/login_database_unittest.cc b/components/password_manager/core/browser/login_database_unittest.cc
index bb2ea4d..4cac13d 100644
--- a/components/password_manager/core/browser/login_database_unittest.cc
+++ b/components/password_manager/core/browser/login_database_unittest.cc
@@ -30,6 +30,10 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/origin.h"
 
+#if defined(OS_IOS)
+#include "base/ios/ios_util.h"
+#endif
+
 using autofill::PasswordForm;
 using base::ASCIIToUTF16;
 using ::testing::Eq;
@@ -210,6 +214,14 @@
 };
 
 TEST_F(LoginDatabaseTest, Logins) {
+#if defined(OS_IOS) && TARGET_IPHONE_SIMULATOR
+  // TODO(crbug.com/619982): This test is currently failing on the latest iOS10
+  // simulator due to a bug we expect to be fixed shortly.  Temporarilly
+  // disabling the test for iOS10 simulator runs only.
+  if (base::ios::IsRunningOnIOS10OrLater()) {
+    return;
+  }
+#endif
   ScopedVector<autofill::PasswordForm> result;
 
   // Verify the database is empty.
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 40c9990..98a8cff 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -137,7 +137,7 @@
 #   persistent IDs for all fields (but not for groups!) are needed. These are
 #   specified by the 'id' keys of each policy. NEVER CHANGE EXISTING IDs,
 #   because doing so would break the deployed wire format!
-#   For your editing convenience: highest ID currently used: 336
+#   For your editing convenience: highest ID currently used: 337
 #
 # Placeholders:
 #   The following placeholder strings are automatically substituted:
@@ -8749,6 +8749,28 @@
       'tags': [],
       'desc': '''If this is set to true or is not set, users will be able to cast tabs, sites or the desktop from the browser. If set to false, this option will be disabled.'''
     },
+    {
+      'name': 'ArcBackupRestoreEnabled',
+      'type': 'main',
+      'schema': { 'type': 'boolean' },
+      'supported_on': ['chrome_os:53-'],
+      'features': {
+        'dynamic_refresh': True,
+        'per_profile': False,
+      },
+      'example_value': False,
+      'id': 337,
+      'caption': '''Enable Android Backup Service''',
+      'tags': [],
+      'desc':
+      '''When this policy is set to true, Android app data is uploaded to Android Backup servers and restored from them upon app re-installations for compatible apps.
+
+      When this policy is set to false, Android Backup Service will be switched off.
+
+      If this setting is configured then users are not able change it themselves.
+
+      If this setting is not configured then users are able to turn Android Backup Service on and off in the Android Settings app.''',
+    },
   ],
   'messages': {
     # Messages that are not associated to any policies.
diff --git a/components/spellcheck.gypi b/components/spellcheck.gypi
new file mode 100644
index 0000000..064f3c7e
--- /dev/null
+++ b/components/spellcheck.gypi
@@ -0,0 +1,20 @@
+# 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.
+
+{
+  'targets': [
+    {
+      # GN version: //components/spellcheck/common
+      'target_name': 'spellcheck_common',
+      'type': 'static_library',
+      'include_dirs': [
+        '..',
+      ],
+      'sources': [
+        'spellcheck/common/spellcheck_switches.cc',
+        'spellcheck/common/spellcheck_switches.h',
+      ],
+    },
+  ],
+}
diff --git a/components/spellcheck/OWNERS b/components/spellcheck/OWNERS
new file mode 100644
index 0000000..e06a48fb
--- /dev/null
+++ b/components/spellcheck/OWNERS
@@ -0,0 +1,5 @@
+groby@chromium.org
+rouslan@chromium.org
+
+# Android, component, refactoring
+timvolodine@chromium.org
diff --git a/components/spellcheck/common/BUILD.gn b/components/spellcheck/common/BUILD.gn
new file mode 100644
index 0000000..d9fa04b
--- /dev/null
+++ b/components/spellcheck/common/BUILD.gn
@@ -0,0 +1,10 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("common") {
+  sources = [
+    "spellcheck_switches.cc",
+    "spellcheck_switches.h",
+  ]
+}
diff --git a/components/spellcheck/common/spellcheck_switches.cc b/components/spellcheck/common/spellcheck_switches.cc
new file mode 100644
index 0000000..132761a
--- /dev/null
+++ b/components/spellcheck/common/spellcheck_switches.cc
@@ -0,0 +1,38 @@
+// 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 "build/build_config.h"
+#include "components/spellcheck/common/spellcheck_switches.h"
+
+namespace spellcheck {
+namespace switches {
+
+#if defined(ENABLE_SPELLCHECK)
+#if defined(OS_ANDROID)
+// Enables use of the Android spellchecker.
+const char kEnableAndroidSpellChecker[] = "enable-android-spellchecker";
+#endif  // defined(OS_ANDROID)
+
+// Enables participation in the field trial for user feedback to spelling
+// service.
+const char kEnableSpellingFeedbackFieldTrial[] =
+    "enable-spelling-feedback-field-trial";
+
+// Specifies the number of seconds between sending batches of feedback to
+// spelling service. The default is 30 minutes. The minimum is 5 seconds. This
+// switch is for temporary testing only.
+// TODO(rouslan): Remove this flag when feedback testing is complete. Revisit by
+// August 2013.
+const char kSpellingServiceFeedbackIntervalSeconds[] =
+    "spelling-service-feedback-interval-seconds";
+
+// Specifies the URL where spelling service feedback data will be sent instead
+// of the default URL. This switch is for temporary testing only.
+// TODO(rouslan): Remove this flag when feedback testing is complete. Revisit by
+// August 2013.
+const char kSpellingServiceFeedbackUrl[] = "spelling-service-feedback-url";
+#endif  // defined(ENABLE_SPELLCHECK)
+
+}  // namespace switches
+}  // namespace spellcheck
diff --git a/components/spellcheck/common/spellcheck_switches.h b/components/spellcheck/common/spellcheck_switches.h
new file mode 100644
index 0000000..4e85faa4
--- /dev/null
+++ b/components/spellcheck/common/spellcheck_switches.h
@@ -0,0 +1,25 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SPELLCHECK_COMMON_SPELLCHECK_SWITCHES_H_
+#define COMPONENTS_SPELLCHECK_COMMON_SPELLCHECK_SWITCHES_H_
+
+#include "build/build_config.h"
+
+namespace spellcheck {
+namespace switches {
+
+#if defined(ENABLE_SPELLCHECK)
+#if defined(OS_ANDROID)
+extern const char kEnableAndroidSpellChecker[];
+#endif  // defined(OS_ANDROID)
+extern const char kEnableSpellingFeedbackFieldTrial[];
+extern const char kSpellingServiceFeedbackIntervalSeconds[];
+extern const char kSpellingServiceFeedbackUrl[];
+#endif  // defined(ENABLE_SPELLCHECK)
+
+}  // namespace switches
+}  // namespace spellcheck
+
+#endif  // COMPONENTS_SPELLCHECK_COMMON_SPELLCHECK_SWITCHES_H_
diff --git a/components/test/data/cast_certificate/certificates/cast_crl_test_root_ca.pem b/components/test/data/cast_certificate/certificates/cast_crl_test_root_ca.pem
new file mode 100644
index 0000000..95d8cf7
--- /dev/null
+++ b/components/test/data/cast_certificate/certificates/cast_crl_test_root_ca.pem
@@ -0,0 +1,84 @@
+$ openssl x509 -text -noout < [CERTIFICATE]
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 145 (0x91)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: C=US, ST=California, L=Mountain View, O=Google Inc, OU=Cast, CN=Cast CRL Test Untrusted Root CA
+        Validity
+            Not Before: May 25 21:00:06 2016 GMT
+            Not After : May 20 21:00:06 2036 GMT
+        Subject: C=US, ST=California, L=Mountain View, O=Google Inc, OU=Cast, CN=Cast CRL Test Untrusted Root CA
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:b5:c1:be:d4:34:2c:d7:df:99:21:a9:4b:56:c3:
+                    2e:78:21:0d:a8:a9:a0:4c:2d:62:cb:4f:c5:a0:a7:
+                    47:1c:9d:2a:eb:61:d1:d4:cc:24:23:e5:11:0f:18:
+                    81:2a:56:41:30:6f:84:04:09:ba:0f:ec:d7:2f:c6:
+                    c4:c4:91:2d:ca:6d:76:54:b2:b2:5a:59:c1:3d:17:
+                    f2:7f:80:22:b9:39:c5:c0:74:74:9d:70:b9:c7:60:
+                    e3:84:95:a0:49:ab:6d:8f:cc:b9:3a:c9:dd:4f:50:
+                    37:9e:b0:c0:6f:22:8c:a9:82:56:26:8f:e1:b3:e6:
+                    b4:b9:be:4d:83:e0:f4:d4:2f:10:9b:7f:c9:4f:77:
+                    6f:a0:02:34:1a:ce:be:e3:0e:25:03:ba:db:b1:bc:
+                    fa:ec:01:c2:c0:f4:f5:55:b9:7b:ef:c0:8a:52:fe:
+                    f5:07:cf:2d:fa:37:fe:4c:54:f2:87:e9:32:ee:04:
+                    6c:d6:fa:fc:51:94:21:e5:43:cb:89:02:07:b3:5b:
+                    dc:09:18:cc:55:9c:89:3e:ff:32:6c:93:87:f7:18:
+                    73:7c:6f:ca:57:41:bd:d2:5b:12:60:75:a7:44:c3:
+                    78:35:98:12:b6:1e:3c:25:72:13:2c:9d:fa:74:81:
+                    11:28:cc:55:93:46:42:17:5a:46:ee:57:17:26:83:
+                    4c:1b
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:TRUE, pathlen:1
+            X509v3 Subject Key Identifier: 
+                56:47:A3:12:A6:DB:B7:F3:F1:E4:68:62:CB:01:45:FD:2B:02:73:68
+            X509v3 Authority Key Identifier: 
+                keyid:56:47:A3:12:A6:DB:B7:F3:F1:E4:68:62:CB:01:45:FD:2B:02:73:68
+
+            X509v3 Key Usage: 
+                Certificate Sign
+    Signature Algorithm: sha256WithRSAEncryption
+         22:ce:56:31:cd:e0:ac:b4:af:70:8a:0a:60:4d:a8:16:81:11:
+         cf:b6:cf:89:87:86:ec:8e:72:b3:bc:01:b4:29:b1:88:48:65:
+         cd:7c:8f:a0:6f:05:b8:60:28:60:1b:8d:61:eb:e5:a5:c0:b5:
+         11:8d:4d:73:73:3a:82:a9:39:fe:5a:54:28:69:1c:ec:9e:b7:
+         1c:3d:02:d1:33:1c:82:cc:14:0d:c9:c7:ab:7d:c0:89:31:ff:
+         02:17:8a:d0:37:e5:dc:03:34:d5:07:a7:0f:8c:ec:3e:47:5d:
+         a9:e9:12:6d:0c:8d:89:27:73:9c:4c:88:97:66:9f:4f:76:5b:
+         af:e6:40:de:83:b1:de:35:2e:ae:9e:91:da:a3:37:a5:89:2a:
+         57:a9:98:40:f2:e5:8e:ad:44:4d:f6:c0:55:c8:71:dc:a5:81:
+         4a:3b:17:3c:fd:40:77:0c:26:65:da:ec:da:97:19:a0:16:99:
+         11:d0:2b:0d:2c:58:30:9a:76:18:4f:d0:3c:24:4b:96:51:7e:
+         82:ce:a1:6c:fa:fb:5a:3c:c2:1d:3c:27:f2:ea:17:2c:5e:ab:
+         91:45:d3:35:d0:dc:a7:41:e0:e3:3e:23:db:63:a8:fc:fb:76:
+         37:59:f8:b2:31:e5:6d:cf:fa:17:84:d9:16:2f:97:90:8c:83:
+         f2:6f:82:ed
+-----BEGIN CERTIFICATE-----
+MIID7jCCAtagAwIBAgICAJEwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVT
+MRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRMw
+EQYDVQQKDApHb29nbGUgSW5jMQ0wCwYDVQQLDARDYXN0MSgwJgYDVQQDDB9DYXN0
+IENSTCBUZXN0IFVudHJ1c3RlZCBSb290IENBMB4XDTE2MDUyNTIxMDAwNloXDTM2
+MDUyMDIxMDAwNlowgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh
+MRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKDApHb29nbGUgSW5jMQ0w
+CwYDVQQLDARDYXN0MSgwJgYDVQQDDB9DYXN0IENSTCBUZXN0IFVudHJ1c3RlZCBS
+b290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtcG+1DQs19+Z
+IalLVsMueCENqKmgTC1iy0/FoKdHHJ0q62HR1MwkI+URDxiBKlZBMG+EBAm6D+zX
+L8bExJEtym12VLKyWlnBPRfyf4AiuTnFwHR0nXC5x2DjhJWgSattj8y5OsndT1A3
+nrDAbyKMqYJWJo/hs+a0ub5Ng+D01C8Qm3/JT3dvoAI0Gs6+4w4lA7rbsbz67AHC
+wPT1Vbl778CKUv71B88t+jf+TFTyh+ky7gRs1vr8UZQh5UPLiQIHs1vcCRjMVZyJ
+Pv8ybJOH9xhzfG/KV0G90lsSYHWnRMN4NZgSth48JXITLJ36dIERKMxVk0ZCF1pG
+7lcXJoNMGwIDAQABo2AwXjAPBgNVHRMECDAGAQH/AgEBMB0GA1UdDgQWBBRWR6MS
+ptu38/HkaGLLAUX9KwJzaDAfBgNVHSMEGDAWgBRWR6MSptu38/HkaGLLAUX9KwJz
+aDALBgNVHQ8EBAMCAgQwDQYJKoZIhvcNAQELBQADggEBACLOVjHN4Ky0r3CKCmBN
+qBaBEc+2z4mHhuyOcrO8AbQpsYhIZc18j6BvBbhgKGAbjWHr5aXAtRGNTXNzOoKp
+Of5aVChpHOyetxw9AtEzHILMFA3Jx6t9wIkx/wIXitA35dwDNNUHpw+M7D5HXanp
+Em0MjYknc5xMiJdmn092W6/mQN6Dsd41Lq6ekdqjN6WJKlepmEDy5Y6tRE32wFXI
+cdylgUo7Fzz9QHcMJmXa7NqXGaAWmRHQKw0sWDCadhhP0DwkS5ZRfoLOoWz6+1o8
+wh08J/LqFyxeq5FF0zXQ3KdB4OM+I9tjqPz7djdZ+LIx5W3P+heE2RYvl5CMg/Jv
+gu0=
+-----END CERTIFICATE-----
diff --git a/components/test/data/cast_certificate/certificates/cast_test_root_ca.pem b/components/test/data/cast_certificate/certificates/cast_test_root_ca.pem
new file mode 100644
index 0000000..c3131373
--- /dev/null
+++ b/components/test/data/cast_certificate/certificates/cast_test_root_ca.pem
@@ -0,0 +1,83 @@
+$ openssl x509 -text -noout < [CERTIFICATE]
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 28 (0x1c)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: C=US, ST=California, L=Mountain View, O=Google Inc, OU=Cast, CN=Cast Test Untrusted Root CA
+        Validity
+            Not Before: Jan 21 23:41:26 2015 GMT
+            Not After : Jan 16 23:41:26 2035 GMT
+        Subject: C=US, ST=California, L=Mountain View, O=Google Inc, OU=Cast, CN=Cast Test Untrusted Root CA
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:c1:e4:4c:2e:e0:ac:5c:da:e1:b7:e0:fe:f5:19:
+                    2a:6b:eb:b5:8d:9c:dd:f6:d9:5f:84:64:f8:a9:4b:
+                    07:55:78:73:f7:14:38:38:94:13:70:d9:87:23:d4:
+                    91:c2:93:d1:3f:86:ad:a7:f6:39:e9:78:c0:b8:3e:
+                    32:40:ec:83:4c:a2:47:96:75:af:fc:1d:35:1b:00:
+                    da:9a:a5:6c:cd:1b:04:0a:e6:bd:a9:e4:5d:67:71:
+                    7d:60:c8:e1:59:27:c3:f1:87:85:69:b1:e8:e4:39:
+                    92:84:db:df:96:71:b8:5b:a9:ef:b3:de:d4:a6:c6:
+                    4c:cb:4b:02:d9:84:d1:47:1a:45:d8:5d:9f:ae:09:
+                    69:39:1c:4a:d1:f0:9a:88:59:54:44:8e:58:96:58:
+                    24:0c:d5:9a:bc:7b:81:2c:2c:55:52:ac:06:37:7d:
+                    52:89:58:19:cd:fe:f5:55:17:57:e3:c2:47:c3:be:
+                    61:59:9f:86:fc:51:20:11:13:ad:62:d1:3b:b4:55:
+                    83:5e:3d:26:d0:c8:0d:36:2e:6b:86:af:6c:cc:5d:
+                    99:8f:93:18:47:df:f9:29:cd:6b:c6:5d:3f:01:15:
+                    b5:06:0d:f4:ce:4a:21:aa:36:4f:45:a0:ca:b1:94:
+                    78:49:b7:3a:c9:23:01:58:87:10:85:4f:6e:ba:90:
+                    40:ef
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:TRUE, pathlen:2
+            X509v3 Subject Key Identifier: 
+                CC:C4:CE:8E:D4:73:22:4D:0F:BC:8C:FB:F5:55:F1:61:A5:38:90:C9
+            X509v3 Authority Key Identifier: 
+                keyid:CC:C4:CE:8E:D4:73:22:4D:0F:BC:8C:FB:F5:55:F1:61:A5:38:90:C9
+
+            X509v3 Key Usage: 
+                Certificate Sign, CRL Sign
+    Signature Algorithm: sha256WithRSAEncryption
+         a9:bc:38:09:a4:2b:56:2c:ad:ed:32:fb:9a:71:62:8d:12:3d:
+         ff:2d:9a:84:70:9a:09:5a:0b:29:a8:7f:6f:91:93:9d:93:cc:
+         4c:a3:d8:9d:75:81:27:c4:16:c4:8e:25:97:a5:ce:3e:33:2a:
+         1a:6f:03:68:b9:35:0e:ee:99:d5:e0:96:d9:59:7b:a7:b9:8d:
+         86:7d:5d:54:7d:b9:48:ac:fe:57:dd:50:94:c7:6b:f4:29:f4:
+         eb:9a:aa:63:bb:91:64:52:b4:56:7d:e2:73:3b:65:9e:0e:c0:
+         8c:2c:44:f3:12:b5:1c:4e:41:80:3f:a2:17:c6:f2:53:e6:48:
+         94:5d:ae:b9:7c:72:4f:7f:77:43:37:f8:dc:05:8d:ac:0b:7d:
+         61:7d:7d:fc:59:6f:04:52:35:b0:dc:ea:ad:ce:fe:15:36:0a:
+         2e:79:0e:b7:a1:93:61:34:9a:47:0d:c1:6b:b6:ae:d2:9b:01:
+         f3:6b:ff:5b:d4:f9:03:5e:81:1a:e7:90:28:e8:e5:7d:76:15:
+         c2:73:46:a3:c2:bd:41:51:fe:57:ef:58:6e:b1:94:82:ea:c0:
+         f1:9a:6f:f8:31:00:c3:ce:22:4f:28:5b:a1:e2:17:b5:1c:ba:
+         d8:de:73:ec:a2:3d:cc:12:fe:f5:ae:c9:fa:a7:4e:e3:0b:4e:
+         24:18:a7:f0
+-----BEGIN CERTIFICATE-----
+MIID5TCCAs2gAwIBAgIBHDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEzAR
+BgNVBAoMCkdvb2dsZSBJbmMxDTALBgNVBAsMBENhc3QxJDAiBgNVBAMMG0Nhc3Qg
+VGVzdCBVbnRydXN0ZWQgUm9vdCBDQTAeFw0xNTAxMjEyMzQxMjZaFw0zNTAxMTYy
+MzQxMjZaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQG
+A1UEBwwNTW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzENMAsGA1UE
+CwwEQ2FzdDEkMCIGA1UEAwwbQ2FzdCBUZXN0IFVudHJ1c3RlZCBSb290IENBMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAweRMLuCsXNrht+D+9Rkqa+u1
+jZzd9tlfhGT4qUsHVXhz9xQ4OJQTcNmHI9SRwpPRP4atp/Y56XjAuD4yQOyDTKJH
+lnWv/B01GwDamqVszRsECua9qeRdZ3F9YMjhWSfD8YeFabHo5DmShNvflnG4W6nv
+s97UpsZMy0sC2YTRRxpF2F2frglpORxK0fCaiFlURI5YllgkDNWavHuBLCxVUqwG
+N31SiVgZzf71VRdX48JHw75hWZ+G/FEgEROtYtE7tFWDXj0m0MgNNi5rhq9szF2Z
+j5MYR9/5Kc1rxl0/ARW1Bg30zkohqjZPRaDKsZR4Sbc6ySMBWIcQhU9uupBA7wID
+AQABo2AwXjAPBgNVHRMECDAGAQH/AgECMB0GA1UdDgQWBBTMxM6O1HMiTQ+8jPv1
+VfFhpTiQyTAfBgNVHSMEGDAWgBTMxM6O1HMiTQ+8jPv1VfFhpTiQyTALBgNVHQ8E
+BAMCAQYwDQYJKoZIhvcNAQELBQADggEBAKm8OAmkK1Ysre0y+5pxYo0SPf8tmoRw
+mglaCymof2+Rk52TzEyj2J11gSfEFsSOJZelzj4zKhpvA2i5NQ7umdXgltlZe6e5
+jYZ9XVR9uUis/lfdUJTHa/Qp9OuaqmO7kWRStFZ94nM7ZZ4OwIwsRPMStRxOQYA/
+ohfG8lPmSJRdrrl8ck9/d0M3+NwFjawLfWF9ffxZbwRSNbDc6q3O/hU2Ci55Dreh
+k2E0mkcNwWu2rtKbAfNr/1vU+QNegRrnkCjo5X12FcJzRqPCvUFR/lfvWG6xlILq
+wPGab/gxAMPOIk8oW6HiF7Ucutjec+yiPcwS/vWuyfqnTuMLTiQYp/A=
+-----END CERTIFICATE-----
diff --git a/components/test/data/cast_certificate/testsuite/README b/components/test/data/cast_certificate/testsuite/README
new file mode 100644
index 0000000..585877e
--- /dev/null
+++ b/components/test/data/cast_certificate/testsuite/README
@@ -0,0 +1,4 @@
+The contents of the test suite can be examined using gqui.
+Example command:
+gqui from testsuite1.pb proto ../../../../cast_certificate/proto/test_suite.proto:cast_certificate.DeviceCertTestSuite
+
diff --git a/components/test/data/cast_certificate/testsuite/testsuite1.pb b/components/test/data/cast_certificate/testsuite/testsuite1.pb
new file mode 100644
index 0000000..c85079c
--- /dev/null
+++ b/components/test/data/cast_certificate/testsuite/testsuite1.pb
Binary files differ
diff --git a/components/test/data/cast_certificate/testsuite/testsuite1.pb_text b/components/test/data/cast_certificate/testsuite/testsuite1.pb_text
new file mode 100644
index 0000000..dcebb0eb
--- /dev/null
+++ b/components/test/data/cast_certificate/testsuite/testsuite1.pb_text
@@ -0,0 +1,219 @@
+tests {
+  description: "Valid cert, valid path, no revocation checking."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  expected_result: SUCCESS
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Invalid cert (bad signature), valid path, no revocation checking."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026>\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  expected_result: PATH_VERIFICATION_FAILED
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Valid cert, invalid ICA (bad signature), no revocation checking."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\352F!\330\370"
+  expected_result: PATH_VERIFICATION_FAILED
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Invalid cert (expired), valid path, no revocation checking."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  expected_result: PATH_VERIFICATION_FAILED
+  cert_verification_time_seconds: 2082758400
+}
+tests {
+  description: "Invalid cert (not yet valid), valid path, no revocation checking."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  expected_result: PATH_VERIFICATION_FAILED
+  cert_verification_time_seconds: 946684800
+}
+tests {
+  description: "Valid cert, missing path, no revocation checking."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  expected_result: PATH_VERIFICATION_FAILED
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Valid cert, valid path, valid empty CRL."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  crl_bundle: "\n\377\t\n\f\020\340\274\220\273\005\030\342\274\220\273\005\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\002\033N\250\251\367\262\212\230\234\253\016_\330\0227\272\347w\327Y#\t\350\243v\375\224\210>b9)\037Nj\220\366d\311u\202L\023+h3\001,\031\362\363\201\261\334v\323?\237\306\017Q\226\337\225mJ\302\365\020\033\247)&\263\035\330\017\006.\252\307\341\v\fSf`\177\'Y\267\216P\002\034\327\302\226\363\".\023`\3343W,\315\003\f\371\033ts/m\226\237\260\335\332\355\227\237/\335\233P\201\377\354e\362\234\033\262u\027!\254\000(\335\214\374v3XQ\274}}\230\220>\333\034\3700\307\241R\377a\377\0214\274\263\rG\022\360\307\037\006\346N{\326YL\t\351\214\306\321v=\017EF\374g\023\320\003\030\267\324n\272C\377q7\025\265@\034\351\320\311p1t\035z\3513\323\374\316\323G{\000\2268=\224\375\253\177\243^\224\207\273\30079\333\355\302\2755\345\036\334{\315x}\202\a"
+  expected_result: SUCCESS
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Invalid CRL: CRL signer cert does not chain up to trusted CRL root."
+  crl_bundle: "\n\377\t\n\f\020\340\274\220\273\005\030\342\274\220\273\005\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\000\202V\252\317\032\200\002\033N\250\251\367\262\212\230\234\253\016_\330\0227\272\347w\327Y#\t\350\243v\375\224\210>b9)\037Nj\220\366d\311u\202L\023+h3\001,\031\362\363\201\261\334v\323?\237\306\017Q\226\337\225mJ\302\365\020\033\247)&\263\035\330\017\006.\252\307\341\v\fSf`\177\'Y\267\216P\002\034\327\302\226\363\".\023`\3343W,\315\003\f\371\033ts/m\226\237\260\335\332\355\227\237/\335\233P\201\377\354e\362\234\033\262u\027!\254\000(\335\214\374v3XQ\274}}\230\220>\333\034\3700\307\241R\377a\377\0214\274\263\rG\022\360\307\037\006\346N{\326YL\t\351\214\306\321v=\017EF\374g\023\320\003\030\267\324n\272C\377q7\025\265@\034\351\320\311p1t\035z\3513\323\374\316\323G{\000\2268=\224\375\253\177\243^\224\207\273\30079\333\355\302\2755\345\036\334{\315x}\202\a"
+  expected_result: CRL_VERIFICATION_FAILED
+  crl_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Invalid CRL: unsupported version."
+  crl_bundle: "\n\212\n\n\027\b\377\377\377\377\377\377\377\377\377\001\020\340\274\220\273\005\030\342\274\220\273\005\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\002Fw\252\025\221\023\377P\316\330\315\276\221\022B_\322O\332~\002\347`#\217\035\363\033B}\257U\034\373,\240G\320\345\332\035\243\336\372_\275\\0d^<\374\345?\350\343%1\032M\023\261\245f\002yvz\a]\316\2724\002\331+7\031\304\006|\330;w\257\023\037\322\361i\204C\374\016V\311\020\230\336OJQ\353\262\336\226\b\030\3651\350g9y\361\340\017o\364\337l\246\322V\320}\206\320\301\003\202E\216\244O7\324\234\005\362\aC\240\315\342\330;\036\000\366\366\025D\3615\323\036F\245\277|\215\024\346\036\307V\237\215\250\245\205A$\310\343\221\255P\003\357\223^R\327n\210\204U\006\'\006(0n\350\322G\031m)\301\035\377\253\2009js]Io%\312\354\225\211\261o\202\033)UO~\177\360\2160\203\252\vb`\353\311:\f\0364{@\346\215.`\024f3s\313\226\003\272/J"
+  expected_result: CRL_VERIFICATION_FAILED
+  crl_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Invalid CRL: bad signature on the CRL."
+  crl_bundle: "\n\377\t\n\f\020\340\274\220\273\005\030\342\274\220\273\005\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\002\033N\250\251\367\262\212\230\234\253\016_\330\0227\272\347w\327Y#\t\350\243v\375\224\210>b9)\037Nj\220\366d\311u\202L\023+h3\001,\031\362\363\201\261\334v\323?\237\306\017Q\226\337\225mJ\302\365\020\033\247)&\263\035\330\017\006.\252\307\341\v\fSf`\177\'Y\267\216P\002\034\327\302\226\363\".\023`\3343W,\315\003\f\371\033ts/m\226\237\260\335\332\355\227\237/\335\233P\201\377\354e\362\234\033\262u\027!\254\000(\335\214\374v3XQ\274}}\230\220>\333\034\3700\307\241R\377a\377\0214\274\263\rG\022\360\307\037\006\346N{\326YL\t\351\214\306\321v=\017EF\374g\023\320\003\030\267\324n\272C\377q7\025\265@\034\351\320\311p1t\035z\3513\323\374\316\323G{\000\2268=\224\375\253\177\243^\224\207\273\30079\333\355\302\2755\345\036\334{\316x}\202\a"
+  expected_result: CRL_VERIFICATION_FAILED
+  crl_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Invalid CRL: expired."
+  crl_bundle: "\n\377\t\n\f\020\337\274\220\273\005\030\340\274\220\273\005\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\002\026\016\275\r\251\311\203t\030\213\t\324ilWI\321\360\257\237$?Z\003]\313(\202S\036J\264\255\n~\027\337\366\263h\233R[ZG\0260Z*\261\250\366M_\353\201\375\216\036\305\212~8\243.HN\312\002jx\332\\(L\2619\272\310\000\257\371PZGwvv\322,\322[D\276)yS\220}\'\366l\025\375\244\t\232\370\263\246RT\272\204\201\b\336\214\321P\003 \031\253\323\246`\334leg\327/pi\342\2662\025\370\230\351jA\217\b{\a\256\005:E\254\266u\036m!\037\226,\255N8fE;\345\3200\v\274\305\3456\310\rn\220\356\2714@\216Y\026\030\244\036\231`m/\216\027\335\367\357\240x\021YN\306\227\357\206\037\254QR\006p\351R\221Q\2705w(h)\177\357\006\201\221\264\205\312\364O8\233\022\254v\236\305qKxA\022r\177\267\276h\000\251&}\v\017"
+  expected_result: CRL_VERIFICATION_FAILED
+  crl_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Invalid CRL: not yet valid."
+  crl_bundle: "\n\377\t\n\f\020\342\274\220\273\005\030\343\274\220\273\005\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\002>\250.*\260\215ZVcg\367\342\345f\302\v\022\362\255}^\031C\334\367gc^\273{K\376\036\210thg\306\003\223\255\021\024#\032v\334\260\231iH7s\213\023\340\274\325\275\377\377&y\303\323\370I\003{CW\244\332\2620\305\237z\261\305\277\3058\312)\317\354\317\353%!R-\323=+e\237\346\223\363\204\206\367\365\267`\0342fc\220\215 dZ9\350\327\017\205\247\365i\365\352\274;\3567\027l|\000&\207=\2169YQd>\v\251T;\033Wr\372u#OV\005\3568n\027\2476\234\254\305\224\335\240{\220\366\263\243\227hi\023\375|\261\350\237\352Aa\213\276\217\274\030%\017\232\316\\5o\003W\256\305\221\017\345\371\214\277*\201\032\257\224\363u\306\337\2547\223M\316\273p\314L\346\350\177\372*g\275\333\343\207\260\345Z\340@\205lP\317v\257\017\326\263-\367\350\322\215E\305"
+  expected_result: CRL_VERIFICATION_FAILED
+  crl_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Invalid CRL: CRL signer cert expired."
+  crl_bundle: "\n\212\n\n\f\020\263\340\254\273\005\030\265\340\254\273\005\022\366\a0\202\003\3620\202\002\332\240\003\002\001\002\002\002\000\2270\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160622004124Z\027\r160623004124Z0\201\2221\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1200\006\003U\004\003\f)Short Term Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\250wB!d\3772~yr\367\f\207\217\005\336\266\250\020\214\370\241\240V &\317b\341%I=\344\341\363v{\237\300\362\200xj\352Q\005\366\337e\023Z\276\372ldm\205]XV\354\252RR\301p%\023\271\201\335\000\372\262\372}\372\331,/\017\314\212\177g\336\035\302\021\323\201c!\372\242\3273\344\370\206\3340\275\234\201_\005>\245\303S.\311\300\326\370\355\232#\037s;\353[\216\'\324\304\257y\325^\343\027}a\322_O\035|\177\363\303a;G\336\364wGRV_\r\361\334M\001\270\036)d\032Qo5\217\bp;=\375\327\370\327\aR\371(\373\021Jic\327\361\327z\275\362#\376\271]\304\225Z\372\247\277\374nq:7\2225_\006oR\343HK\354\253\026<\002\251\254@\374K\330\036h\217\242,;\325\337;\v\361\315\313D\351/\365\016\005\303U\247\"\035\265?M\351\267\217\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\222\016\315\233\347\204\306\020\217{<\221\234Ttty\367.70\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000h\335\370w\213\037>\aZ\246\354U:.09\223Z\202>F\367z\320+\272\257_I\302\325\022\260m 7\353\003p\247w\360\364\265eB\t\321\312T\351\324\216\215,w\356n6\'l=S\250\022ri\344N\247\353\353\a\354\314\342i\217\244\245\1770\202\025\026\n5W\237P\372\346-\331\375\372\321\373\037\216\372\226\272\004\326cx\253k\353\247H|4S\343\v\ra\247\247`\356\304\324\337U\374\342INVH\251A.\234\231\016\212\006D\177\b\321\312E\260\006\346O\304\236\3106\026Yk@.\2279\vWA\v\242\025\365\031\234\233\355\301\350\334\352\360V\032\035\233\024s\030g\204v\342}\"\220\311\256\320\257\226\362Q\251I>\360bJA\223\362\034\324\313\256\270\r\303\273PR\265\271\365\234\336\300Z\373\250\267\345\276\b\rU}[u\216\243j\236fU\375[\333\032\243\214\022K\t\360\033\243\365\235\032\200\002\241\200`Z[\237\206P\371R\270TXg\004\rQ\255\356|\312\006\031Cl\373Zy\244\316\206\254\b\006\316\020q\325V\301\267\331\317mqP\246\301\312\303\262w\262\344\030\021\351\313%\373>\004\311\036 \033<\275Y\314\377!S\304\245\323\351H\251\234\ne\307\030Ud\006\373\341\2078\211\215\232\237/\201\324\334\203\005\372\v\257\313\036yO\030\307\263\241\'\277L\222\023\200L.\204R\375\035\2551I\205\363\333\0033:_\323#\r*\2316\276\315\253\330\215{gF2\332\307\332k\302\364R\024\205\305~\257\254\203me\020T\215NF\374\251q\2134\315\024q\024\354#\"\021\264y\016G_G.\rT\006\227V\227\3037\204\241\250W\222\222\310\314T\376\333\207\024//g\247\334#q\363\310\331EWL\f\260\263\237\250\362\"&[\030\266y81\301\325\260\303N?Qw\373d\223\205\213\354\326\240)^"
+  expected_result: CRL_VERIFICATION_FAILED
+  crl_verification_time_seconds: 1466642485
+}
+tests {
+  description: "Bad CRL with an invalid serial number range: firstSerialNumber is larger than lastSerialNumber."
+  crl_bundle: "\n\247\n\n4\020\340\274\220\273\005\030\342\274\220\273\005*&\n 00000000000000000000000000000000\020\n\030\001\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\002\254\275V\252\312\367\022\214_\351a\376\240Z\017P\230m/\274\355s6Z\373[Y,\225\'+V\226g)`\366\261=\343\346\303\337\322(\274U(\3658\317\006=\311\323w\026\343\353\362Y\355h\024\316\252(\375|\374&\317\327\200LF&W@\023iHg-\311\3373\245\226\247=HF\251\376\226C\223()\322\275\263\303\330\\\366\330\023\023\236\246G\224\300\aw\023sJ5r\237e\356;T7\333\230K\'d\035\3505\237\316\r\316Ei\236M\357\352\320hb\r1*\237U)V\214\274lol\027\314X\334\034H-\264|\000M&*s\354\3166\337\205w5\0301\035f\271\257\a\a\264\303M\327\020\322f\235\333\026\267>\a\000\316}\227\361\331A\255\367\257\262\022\356\341\221\331y.\vu\021\247;*\257X\vY\031\250p\301..v\fM\362\354i\031(\034\261\336\355~\213!\315\325\341\344"
+  expected_result: CRL_VERIFICATION_FAILED
+  crl_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Valid cert/path, valid CRL with one SPKI hash that does not match anything."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  crl_bundle: "\n\241\n\n.\020\340\274\220\273\005\030\342\274\220\273\005\" 00000000000000000000000000000000\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\002\215\035>6\253\v\017\225)!\242\347\371\036\243c\322\34466\032r\260J\0322\267D1G\221\033^0X\263be\224\bR?\231>\221\030^\")\033\347d\352o\244S\377\304Hxb\305\034\177G\305j\006\354\027#\021$\244\267\321\244\004\234\034\'\030\353\025s\313\332X\220\215\247L\331\223q\235\334\241\264\016\304\035\220\217\025VF,\204-\005J\271L\001\231\373J\356\002\"\222dT\355\rn E\a,\346\242[\001z\203\325\226\273\274T\216B\025\022\277\351u>x\332%\234\005rv\353V\214\200\363\245V\034\252\t\252\361\021\217\373\354F\250\021\227\264\005\r\3740\232g\262\000\016\223\363\206K\224%UQ\257:}\342\305\177\313\2743m\311\361fx\253\226\206\023u[c\277k\307Z\343e\200.2\303\371B\225:\2551j4\205\317\004\352s\3701\177\322\322}_\333 \255\326\3431a\016\272\377"
+  expected_result: SUCCESS
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Valid cert, valid path, valid CRL that revokes the ICA by SPKI hash."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  crl_bundle: "\n\241\n\n.\020\340\274\220\273\005\030\342\274\220\273\005\" \005{\224\250\354\377X\232#T\222Q\366\361\214\343\026\344vR\306\252f\313\261\314\334\\\3731\032\371\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\002\232W:\203\"E\0338\2178\226\2733(\0350?GU.\331\225K\247\367z\002{tF\227\267\333\022g\001\265\207\247\367\034\302\200\3363\333z\242B\221\031\333\206\321\231\035\027\312\327\221\316\357\244c\352\324\r\240\361:\030m*>\221\003\"\220\024\236p\317Y\267 \376\316\301\311l\330A\327\226\316G|\237>\2708\003|t\221\027\224\260-\343h\307\303\266or\207p@\245>F4=\200\242\006E\372\333%Y>\306h>v\267\304G!:\313\337\177\340\352\276P\b\330\346S\260\363\207\364\377\017\224w\253\354\267y\341=\017\001\212\030\234v\\\202\006wgg?\235\220\200\032\370\"\225\325\207\001?Q\360\206\2222?Y\t\356!\3476\317\225u8\3741uO\325\266`\366\377\202\341\312\250\030\301p\375bf{|/\v\0171|\217^()[X\301\001\255]\020\230O\302\016w\365\030\362\b\230Ot"
+  expected_result: REVOCATION_CHECK_FAILED
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Valid cert, valid path, valid CRL that revokes the device cert by SPKI hash."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  crl_bundle: "\n\241\n\n.\020\340\274\220\273\005\030\342\274\220\273\005\" ;\340&\236: \360\337 \207]h\333\0034\345j\324\213Z\301-\346\017\341\261W\306\241:\004\206\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\002 e\246\361\325S9\320\b\360,\n\005\276M^\224\206yc\256\322\a\236\201?\352-\214\2113\326/\325\261\270X\210\331\3375\331\v[Ji\026\353I\275g\345DtfY\240bQf\241\360\274\2023\n\273n\364\024\017`\213\263\021:t\365\317\246\263\211\240\036\335G\353\321\\L\vk\356\001Wu\024\262\262\271\022\332\032\b,\372\330\274\355-\237\332z\3173\223\236\256\374\214\021\256n\247\202K1\364\177[l\360\231@0P\302\036\325\365}n3\204%\250i\346\307\306:\\<\216\ro\344\361kY\336\262k\a\361\026\311\336\361\314\b\350\324%\347\217\270I\266\366\004^Q$/_V\325\242S\203\362\367\027iL\305q&~K\342\021o<\005\250\217\304\362\233i=\203\367\263\363\265\374\347\334i\344E\274\n\022k\267\374\035\363\204G~\334\263\207\241\211\237\033+\212\217\237\265\030\223\240\237\'\277\211I\004"
+  expected_result: REVOCATION_CHECK_FAILED
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "CRL with a serial number range that is just above the ICA\'s serial number."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  crl_bundle: "\n\247\n\n4\020\340\274\220\273\005\030\342\274\220\273\005*&\n \273\362\000z\206#\324\325\367\005\230\345c\226\006C4-\251e\347\341\360\031\030\325-\200d\377\242i\020\036\030\037\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\002\037\251MU\312\275\265n\303\246\334]e\271\252w\3078\200\213\255?\bE;b\b\357{\ng9G48\301\2276\3038\234C\032\355\246\035\301S\000+\371\021i\273\241\035\213 \022\343\031\232=\264^\334g\364w\0002]M\216a$\350\210g\241\254hx\230\326W\311U\301\te#\336\214\207\334D+\031\261\304OOg\210\331L\334\363\243\016@Az9&\257#m\240\2641\271\373-\203f\213\002\003/n\366E\256\223_H\253Y\237\322^\005Ke\321{\256)\034\304\207e>\234\276c\227\346\234\342\206w}\264\211\213JrQ{8\022\036\250~9\355\377\366\326I.\336\252\303\316\337\362\247w\\\366\':\254\035/\360\256\267\372C\235a\356\2473\271\201\027\377\311\244\343\351\023\254\274G\tR~\t\346{\304_\250RA#\367\347\262/-\2744\205f\201\273\003h\344\204U\254\330\006`\222\235Y"
+  expected_result: SUCCESS
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "CRL with a serial number range that is just below the ICA\'s serial number."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  crl_bundle: "\n\247\n\n4\020\340\274\220\273\005\030\342\274\220\273\005*&\n \273\362\000z\206#\324\325\367\005\230\345c\226\006C4-\251e\347\341\360\031\030\325-\200d\377\242i\020\033\030\034\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\002l3 4\223\031\'\317\357\330\311\225R;\vkl\226Of\272M\b\251\3126\214\301\231\241\216\036\2233\341H\bD}\366\275\346\260\252\216\316\312^\301\316j\235\247l\"\004\005\306R\334Mc\302K-/\231\215\230Z5\000\177\bC\210A\004\275\337(\n\242\021\260B\000\327\274\357b\243\3021\027\031\223hn>m[u@\211\373d:t\361;\000\274\341\217\020\tzB[\353\347\202m>j\r,\032\023!>\316\037M\316v=\253\322\016\362\241\034\031\344\261H\373\0248\371\3771\351\260\245\3028S\n\370+\226?\2266:?q\264\033\a\rO\3775\257\356\364;D\350\335Dh\'\227\335}\336_\207\361\325\307U\215\300\337>\001\270\274\276\375\331\344\347\212g0\'N\252\262NC\006\250eX\003|c\316w5\256u\232w\242ea\257\035\332\222\030\220\355\370i\336c\fG\260Yt\251\353\341\025\266"
+  expected_result: SUCCESS
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Valid cert/path, valid CRL with a serial number range that includes the device cert, but the issuer SPKI hash does not match."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  crl_bundle: "\n\247\n\n4\020\340\274\220\273\005\030\342\274\220\273\005*&\n 00000000000000000000000000000000\020\035\030\037\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\002\006\367j^\001-\242\006\213\302\311\343\2733\375\324\257\215q\372\306\221\304 j\006\237bpH\212\274\027j\260\314\026\234\252\247\334\311\001\340~\322d\'\222\347\266\346\377\250y*\375\370$o\354h_\2353\377[W\351\3314\177\323\215S_\276\327j\264J$\275\'p!?\367\373 \355\016\354\002M\0328\320\b\251\350\311i\3759\3356\205\237z}\366j\364y\372ik\226\305\021\202\256u|\340!\373J\022T\032\342V\031\320~\330\231N22U\246\307\275F+\331\364C\036\024D\001[\375\016J\234\253J\000\364}go\202-&\263\352i\025mq7\371\311%\225\024V\361\311\202\001\204S\351\340\235\306\033\262\233\334e\f\037\001X\335\306\370\206b\240\270\304P\033\365f\226C\327\024\020\364p\253\a\335\300`\275\233o>\316\017{\261\265\371\201\322\330\304Y\341*\230\332\032\277\265F\232c\347u/\304\310"
+  expected_result: SUCCESS
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Valid CRL with a serial number range that ends with the ICA."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  crl_bundle: "\n\247\n\n4\020\340\274\220\273\005\030\342\274\220\273\005*&\n \273\362\000z\206#\324\325\367\005\230\345c\226\006C4-\251e\347\341\360\031\030\325-\200d\377\242i\020\000\030\035\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\002\037\332k7i\265\377\377\030\224Mq,(?\211r}Q\005m\345\311\303!%2\273F\032\315\250>\025\251G\207\257\306\314\234g\034\265A\026x+6\314\307\"~\244\354\251[\201\373\255\3524>\371\005\026\205\310G;\315\302\216\357~{\311\250\360y\375\236vx(\212\021\222v\356jfs\351\3750\267\233r\344\300z\001\362_E\202\033R\371\337\255\222.\017w\030\332\322\244\235q J\310)\250\342J~=\023@4oZ\363a\020_[\266\266\246\243\262\005\017\210\026\371\237\242;n\204\330\254M.\320\366\r\357\227\336\376\033\264\023\210\341\3477\323\332\215\271\356\204\036(#<%\273g^A V\250\206G\"\365tl\375?E\367\262-\264\304\365c\006\300\274\374U\3115\367\026\006>\205\177\253\207\255\356\307\005:_\021\214\317\265\033>\301(d\"F\002\303\364\245\364\375%\264J\273e\244\306\322\'Q"
+  expected_result: REVOCATION_CHECK_FAILED
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Valid CRL with a serial number range that begins with the device cert."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  crl_bundle: "\n\247\n\n4\020\340\274\220\273\005\030\342\274\220\273\005*&\n \005{\224\250\354\377X\232#T\222Q\366\361\214\343\026\344vR\306\252f\313\261\314\334\\\3731\032\371\020\036\030(\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\002I\232\357\231~9\025\'\335\177\3472\017\271u\at\250\377:b,\005\223\372\233\246\202AV*\375\323\2744\332\316k;\034\276\204r\361\371\327\277\351K\356\036\n~\270\354\031\346\005\301\376\v\277\a\'\253zCF6\b\301y\244\211\360\376\245ch\260LP=(@;rh\025yf\300\334\325C\006>\245\374\a\217\211[\017\267\234\2167\024\024\210\265:\024\312^<\337\273N\352\215,\021??\236\033%\023\303-\335`\271\022\321\t\004\2216\236&\347y\266\262\306Ss\256#\213=PMpN\221\210$K\207O46\227\016|\004\306T\326\303\233\022\226r\251\270\332\327\341V^\210YOF\236\216}\233\3601x\n\204a\375\310;\306\303\246Dk\243BW\261\242\211\203\234|\214\t\036UT\030\200A\367\360\317\361r\\^\321\325=>\221\'eDN\242@\025\350ke#\376\"\240\320\332\3206\223\227"
+  expected_result: REVOCATION_CHECK_FAILED
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Valid CRL with a serial number range. Device cert is in the middle of the range."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  crl_bundle: "\n\247\n\n4\020\340\274\220\273\005\030\342\274\220\273\005*&\n \005{\224\250\354\377X\232#T\222Q\366\361\214\343\026\344vR\306\252f\313\261\314\334\\\3731\032\371\020\024\030(\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\0021%~\237\004\327\363\261\t\023\205\367&\233\260\354@s\026\\?N\004\363o\233\035\364\"\271R4\353\026\272\344\226\\\241\005{`<\226x\332z\311\260f\315\361\227)H:\2646$\264iP\242\212\335z|\271\241\242\322\024\020 \324e\217T[\345{0\302R\277r\232\216\t\374\302\003\207\323\375@\037m\277w\257\305\324u\n{\233Y\302\004D\305f9E\235n\234\302\003*\016\274h\237Y{lg\253\247F\345\337P\bJ\267\244\017\f\332\305\352\333@\375\270\313A\233w\020\370\020\333\212\252\005\350t\260H\355\005Y1\006\303N\020\224/\231\326>\254\240Ff\275\034\306\351\361\035\037-\020U\344pa\234\213\350\016\364;\245\263\266\310\fD\275\005\370\215d\227\031qx\263e\216\350\232\302\000i~8Y\365\303\234\253\350\264\374\333\336G\317\327V\000\000\352\331\352\301I\361%\250[\313\202\177\346\221\221{"
+  expected_result: REVOCATION_CHECK_FAILED
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Valid CRL with a serial number range that covers all 64-bit unsigned integers."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  crl_bundle: "\n\260\n\n=\020\340\274\220\273\005\030\342\274\220\273\005*/\n \005{\224\250\354\377X\232#T\222Q\366\361\214\343\026\344vR\306\252f\313\261\314\334\\\3731\032\371\020\000\030\377\377\377\377\377\377\377\377\377\001\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\002P\205\354\347J\025.a\347@{\355\344D\f\203\255s7\254\226\322Hr}\232\005\354(\366\232B\v@\224\325\026 F\342B\246{\315\312;\310\241\037 ;dVHI\322\233\353\326|\017\330IH\210\232\030@\017T\332,o\261\221o}FW\376Jq\224\316F%\335\001:\242\357<\345Zt\230\322J\n\022z\333\303q\255\341=\246\271\221\303\362\214\250\002W\230\233hl\346|\030o=0\330wT\341=}\243SIc\220I\321\340\322\227\201\372\365e\363\264\276h6\"\330\275j\177N\342\217G4LVJ:\203\305<)\002\'\343\026\363\2623\207)\375F\211:\312\225\031\344\033\241~,\342\200\360y\3033}x\004V\300j/\030u\256\256\245\312A\335\241\261sEZH\177\346\277j.ki\253\375\217_\217\342\307\2147*\201\351\232h\376r\037\204u\003c+\211\334\327h\021\361\226\023\224\315"
+  expected_result: REVOCATION_CHECK_FAILED
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Valid CRL with two non-overlapping serial number ranges. First range misses the device cert. Second range covers the device cert."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  crl_bundle: "\n\320\n\n]\020\340\274\220\273\005\030\342\274\220\273\005*&\n \005{\224\250\354\377X\232#T\222Q\366\361\214\343\026\344vR\306\252f\313\261\314\334\\\3731\032\371\020\035\030\037*\'\n \005{\224\250\354\377X\232#T\222Q\366\361\214\343\026\344vR\306\252f\313\261\314\334\\\3731\032\371\020\037\030\202\001\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\0025Jh)=GB\350Oh\251\377\345&\247\207\213\f\274w\257\217t\311\355\230\032\220\002L\264lw\354\ro\2548\0250n\'LS0\273\b\240T14\367\034\250g\305!Y\357\026\337\324\346)\210l\231\232\331\331\214\200F\004\271UF\330\ti\032\251\027\f\207E\234\265\365\372\312\255DB[\341\366K\314\220\031/\244\3612_R=:\201\220\376&\303\335.%\322QB.D\232\341\362f`\333\345\266\343\210\363uy\f\373[x\214\032\372\r\250\332x\031\273Y|\272xF\230\221T\347~\'\022\016@\335\346\374b;\232\005\337\r\235\240\245K\337i7\276\313?^)\220\364\327\032\315&\320\252$\247;\037yI\236\221o\354\032vw\327\277M;?\255\256\365\315\264K\211\326|\213\2767\320\367\023vY\332\361 \255h~\233g\275m}z\361ec\267\035qm\036\274KQ\353c(\354\000\'\b"
+  expected_result: REVOCATION_CHECK_FAILED
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Valid CRL with two serial number ranges. First range covers the device cert. Second range misses the device cert."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  crl_bundle: "\n\317\n\n\\\020\340\274\220\273\005\030\342\274\220\273\005*&\n \005{\224\250\354\377X\232#T\222Q\366\361\214\343\026\344vR\306\252f\313\261\314\334\\\3731\032\371\020\035\030\037*&\n \005{\224\250\354\377X\232#T\222Q\366\361\214\343\026\344vR\306\252f\313\261\314\334\\\3731\032\371\020\037\030 \022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\002\032\223\235e\233iG0\356\344\024U}/\245\034\034\367\nm\266@\300\353\033g\312\345\316%O\306\313b*\370\356\205\351\243\r\311\226\037\247t\016M\250Z\222a\260\223\315s\267Qg\377\236\343A\177\337\230\334o\351$\331\352O\3726\270\306j\204\347y`\363_2\212z\021_\t;\317U\321.\363\334\025kug*\206%VW\357\217f\3311\323\333\326G\002\264\212\236\215\343}\331\001\267\274n\004\261\037\341b\357@\225\035\246g/\301O\217*\336\005\0240~b\321G\276\262Py\235\026[~\300\\\\\355\220\033\214-\t\271\333\207\025\317\3136r\364\374\360\257\232iH\021$\b\032[r\206U\335A\250\223@\276\265\207&\347\357\220\325\357\022\371\tj\023\312\337\034G\035\211\341\364\373\224k\037sK\r\200t\3752\324\273\362\367\230\350\274\234#\256\200\016\341c)\274bz6\3042^\306\032\031Ra"
+  expected_result: REVOCATION_CHECK_FAILED
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Valid CRL with two overlapping serial number ranges that start with the same serial number. Only the second range covers the device cert."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  crl_bundle: "\n\317\n\n\\\020\340\274\220\273\005\030\342\274\220\273\005*&\n \005{\224\250\354\377X\232#T\222Q\366\361\214\343\026\344vR\306\252f\313\261\314\334\\\3731\032\371\020\034\030\035*&\n \005{\224\250\354\377X\232#T\222Q\366\361\214\343\026\344vR\306\252f\313\261\314\334\\\3731\032\371\020\034\030\036\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\002\027d\374\bl\327\t\254\214\343\257\376\377\331t\221\372\341\202&dQ\030\v\355\205\377\250J\r\017\345\356\360\202\257\222\r,\0270\244~\365\320\021\022q\0040\226\262\236\177\021\234U(%\351\372\326#\375\225~7\256Tt\005\325\0371\353\264 \244x\267\0358@\033)U\300\210rq\002Z\356\231\230\211*?\253\376\246JNq2\006\311\323n`@S\246\374#\256\026E\b\r~Kvz\2038\231.:\260@\255{\354G\337\2537\215{\306\346\002\261\003\3135\2642\213\375\302\336;w/\023\2579N\207p\026\273\275\213N\230=\375U\v\024:g:<?\037VbC\326/\320\t\312_R\346\225eA0\256\036a\240B\275@S\026\016\270\360\243XO0\270\222\223Gm\273\221\360/\362\245\230\370C\256!\023\367\242\324\360\210\205b\241:4\332\271\231\263\006!\366\025z\031\372\022\340\236{\322\366\325_"
+  expected_result: REVOCATION_CHECK_FAILED
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Valid CRL with two overlapping serial number ranges that start with the same serial number. Only the first range covers the device cert."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  crl_bundle: "\n\317\n\n\\\020\340\274\220\273\005\030\342\274\220\273\005*&\n \005{\224\250\354\377X\232#T\222Q\366\361\214\343\026\344vR\306\252f\313\261\314\334\\\3731\032\371\020\034\030\036*&\n \005{\224\250\354\377X\232#T\222Q\366\361\214\343\026\344vR\306\252f\313\261\314\334\\\3731\032\371\020\034\030\035\022\353\a0\202\003\3470\202\002\317\240\003\002\001\002\002\002\000\2220\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160601170615Z\027\r360527170615Z0\201\2071\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1\'0%\006\003U\004\003\f\036Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\274=\315\031\256Nv\203T.\331\225\362\365~\347\"\326\321\'\234A\20669\025qUx4\311)\263Q\370G\337\340T\001\221r\266o_\240l\330\016\301\017\225%#\262\002\371E\360\250W\361}\315\222\267O(\034J*\356\017\310I\202\275h\343tA|!\373\316\017\017\216\223\225\372\327P\253\262\317\217\324X\256m\231\202x\262\363\255\270,\224\217f[\324t\016\220(\377\2062(8\314r\367\364\311\0172a\244\325G}W6\351\264\"8\250\226\017\035\177-h;E<\315.>\321c\020\032m\344\'\273\306/2\225\205\352\337)7F\212y`\027]{\253\313\250\333\356\336,\216E\244\247\353\353y\3756Z\302l\322_Dad\034\020\243\330G\202\034i!\363\242\005(\247\"\am\244\247J\223\213\3521h^\224\202\271\264\261x;\'\t_\235{xB\311B\251\232\002\241&\277\363\245\200\2108-\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\017\272\020\347\307\205M\340wE_I\241\301\027\260n\200\247\n0\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\231\001\372\241\025\202\262\350\300\251\315\356)\262\277\272W\305\235\234\372+\366\300{\361x\241\227\376\321\371\327\336\252J\344\"\203\037\204\305i\321\344\357\a\273\201\303\006\233\330\266\025vk\t=\257\'\a\026\356\324[P\t1\347\241\237\n\371\261\327\234\324\333\215\224\037\b\203Ku\200\314\017\315\301\\\221\236W\364\254\260G\273\233\022\341\273\343\332},\315\203f\223]\375\375@}<e\354\352\311\223\243\177\204x\207\217\367R\227J\020\f\230Y\367\026\1772\034\214\034\n\270\312\263\256v\207\335I\252*\266\337\273\2605\256\202y\213p\r\341D\036E0^g\375\252\bDp\227\356\2216\255\304\031\345\235g\331\224/(\325\223\206\003<\276wA\335\033l#\354\210\r\004\355\346kP\'k\227\246J\345\031\004\330\214/\017\247m\203C\001\240\327\255\275\276\373\"\322Ap\205\340\266\330\247\244\241\272\035\a\264\372\377\202V\252\317\032\200\002\f\\\373F\205\022l\362\271gcx\255\302\277\341}\257Z\r~E,\371g3\\[\271\307\226\314K\3123\311\345\034\260ky\310Bi\270\264\177r\212\n\270\250.3\274!\262\305\351\344\367\0219\003\307\235[-dh\341\343U\367Ci\363\n\316\323kZ6\276\324\201\261\342\325\373\244\340sL=*!B\026L\317lND\241\006\003Z\240\275\006\032\f\324\215\211\277\217\247c\301\202\274\330\313x\371\301\300\267\251\244\233\353Rr\223\002F:\261N\031\215\214\243O\213|L\335\375\257\033\3507>\303\324\324%\232`\350\266/\332!\336\243]]\277/l\222\202\3528K\027\'\354\270\325\223ND/3V\366\330:\"\270H\220;\232\034\264\000\017\277_L\232ziY#fI\035\314CY\016\220r\367D\235 \203e|\224\'\177:\366YXr\351\202\024\026E\204\276\264\017\021\376\005\220\244&\312\361\320\310k"
+  expected_result: REVOCATION_CHECK_FAILED
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "Valid cert/path, no CRL provided, but revocation check is required."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  expected_result: REVOCATION_CHECK_FAILED_WITHOUT_CRL
+  cert_verification_time_seconds: 1466179169
+}
+tests {
+  description: "CRL is valid at the time of initial verification, but when device cert revocation is checked, the CRL signer cert has expired and the CRL is no longer valid."
+  der_cert_path: "0\202\003\3570\202\002\327\240\003\002\001\002\002\001\0360\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\036\027\r150122000235Z\027\r350117000235Z0\201\2031\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1#0!\006\003U\004\003\f\032Cast Test Untrusted Device0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\273h\313\224\232\312\213A@\212\267Pm\344\311\321\325\307\201\366\306D~\314\263\336\274wh\227\210\365\316@\314\365\217\322\321D;\245\213T\323\005\002\263\334\004\356\261\0271,m\277\274#\246\362U}\301\363\224~e\362\265_\374\303\221\373\221\362\320O\2103\032V\371\336\362\323\217\251X\036\037\342\016\231\254;l2\223\2117h\303z\346(\022\216\317:\242\020d\325\370k1\231=\210V\004\034\223~^\343\234\355\246\343<\023V\274\f92\377M>*n\271nMa5-\350\254\355\275~\350f!|\214)\027\261\267\3226\v\273\241\250\221s2vu\314\207m\250\271Sx\v\232\b\001\f\t\2634\254B\034\354\322\273P\t\234\267cA\252e\251G\f\210\275\367\344\224\306>Gi\255\210\231\233\216o\324$\031Y\n\"\251}\276\371&\321uh\a\340\177\212\346r\371\324\\\263f!\250^,Q\027v\206\253\002\003\001\000\001\243o0m0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024tK\b\223^\236\300\031{\271\016\027\203\207\020\270\227\264E\3530\037\006\003U\035#\004\0300\026\200\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\v\006\003U\035\017\004\004\003\002\a\2000\023\006\003U\035%\004\f0\n\006\b+\006\001\005\005\a\003\0020\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000lz\202\370\032\354\350\355E?\022\324\325\217,\204\033\231*\323\345l\342\262\365\313>\215\021\202\320?\b\365\373\250!J\004\311\275^\353;\212Ma?>\206\377\312\023.I\261\037\327\voX\236\314\002\226\225\001\201\254\021\031\372\245\016\350\217\026\375`o\262\3054\372\247OO0dc\232\332\357\201s\374\320\373@\211^\260\230\313\r\262\347\254V[\357w\"\333P\371\325\223\335\251\026~\301N$ \376|\325\243.c\206\030\361\342a\226\330\275\351\257\357\250\232\217<\211\206~`\316\242|\304\344\031\223\220nd\222G\275\264gZ\212\264\247\310\353i\346:\313\254~\207\247W\027\314\002\305\240\205\274Bn&\270\355\225\357A\361u\225\362\0209\200y\213.EuY-6?\004\334\021\r\260\332\240\n\" M\276\265\240\252\031}{x\021\232\236\340\002\\ \v\207\332\221K\346\262\247\270\320\243\a\026=\277\2671\340"
+  der_cert_path: "0\202\003\3410\202\002\311\240\003\002\001\002\002\001\0350\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2041\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1$0\"\006\003U\004\003\f\033Cast Test Untrusted Root CA0\036\027\r150122000232Z\027\r350117000232Z0\201\2001\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1 0\036\006\003U\004\003\f\027Cast Test Untrusted ICA0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\231\375334\366\b\375\304\035w\352\320\223\345.\026[\366\364o\217\257\312\230\231\fR\234\267K\002\234\232c\326\261j\212\005\245\303\236\"\235\323\307\036_F\0344\237\bSM\024\3213\214p\032\230?tJ\263t\345\310\232\316\216\r\2631\320\000!\033z\003\267TE\205\203\227\300\275\266\267p|\356\334hu\265P\215\351d0\0362\256w\340\347\213Xc\323`B\207~\305$\341\207\037\177\305\341\r\204%\317h\214e\331p+\325\a]C\341\244\027e\341\241\025a{\365.\360\211\330\347\031bk&\020\262\250e\235[R\246\377\001C\206uz\024\037\202\224\020*-\242H.\361~\372\236\323<y\001\3610\360\276\330\304\033[\3332\343\371\373\354p\217\206\rgq\247J\364d}\002I\332a\235\261\375\317\326$x\312\355 X\210\367?\006\036t\t|\\\227\022\330\222xE\022\366\001\210jp\370|\034-\002\003\001\000\001\243`0^0\017\006\003U\035\023\004\b0\006\001\001\377\002\001\0000\035\006\003U\035\016\004\026\004\024k~\345\312\262\315\345\361q\024\206\342\345-f\250Y\250T\2100\037\006\003U\035#\004\0300\026\200\024\314\304\316\216\324s\"M\017\274\214\373\365U\361a\2458\220\3110\v\006\003U\035\017\004\004\003\002\001\0060\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000\200\231\322\252\222\361o@\224\034\361\324~Qoa\341\360\247g\344\253vL\020\211A\373(\030J\327,M;\346\216q\301\210\336\212\373\305}Z\370\224\321D\326\325J>\004\320k\003\364~V\364\v\201]\347ZV\373$Tw\020\246\266~{\237\226\362i\254\300\370\356C\225\207\270\3344@\2713%\320\207\255\215\327\270\327\310\374\032\375\0024C\200 \372\213\2172\334\376\206 \2367z\020\240}{<\323bz\031\003\211\'\365<\371\016\246\022\000\\U&\353D\322(2\3747n\312\337\321\265y1\213\t\021+\f\022M\235+=Q~<\v\362\247`\333dY\244\364\377\233Vw\247\227\332U\313\227\273\330L\231\215d\302\210\b\336W\246\203\210[^U\205\251-\3300\200j\200\350/\207\346\346\260\331\373#oJ\357\3315\360\270\241\367\316\261\364\366\251\223\357U\021A\231\241e\313}\v\303\343\351F!\330\370"
+  crl_bundle: "\n\212\n\n\f\020\262\340\254\273\005\030\266\340\254\273\005\022\366\a0\202\003\3620\202\002\332\240\003\002\001\002\002\002\000\2270\r\006\t*\206H\206\367\r\001\001\v\005\0000\201\2101\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1(0&\006\003U\004\003\f\037Cast CRL Test Untrusted Root CA0\036\027\r160622004124Z\027\r160623004124Z0\201\2221\v0\t\006\003U\004\006\023\002US1\0230\021\006\003U\004\b\f\nCalifornia1\0260\024\006\003U\004\a\f\rMountain View1\0230\021\006\003U\004\n\f\nGoogle Inc1\r0\v\006\003U\004\v\f\004Cast1200\006\003U\004\003\f)Short Term Cast CRL Test Untrusted Signer0\202\001\"0\r\006\t*\206H\206\367\r\001\001\001\005\000\003\202\001\017\0000\202\001\n\002\202\001\001\000\250wB!d\3772~yr\367\f\207\217\005\336\266\250\020\214\370\241\240V &\317b\341%I=\344\341\363v{\237\300\362\200xj\352Q\005\366\337e\023Z\276\372ldm\205]XV\354\252RR\301p%\023\271\201\335\000\372\262\372}\372\331,/\017\314\212\177g\336\035\302\021\323\201c!\372\242\3273\344\370\206\3340\275\234\201_\005>\245\303S.\311\300\326\370\355\232#\037s;\353[\216\'\324\304\257y\325^\343\027}a\322_O\035|\177\363\303a;G\336\364wGRV_\r\361\334M\001\270\036)d\032Qo5\217\bp;=\375\327\370\327\aR\371(\373\021Jic\327\361\327z\275\362#\376\271]\304\225Z\372\247\277\374nq:7\2225_\006oR\343HK\354\253\026<\002\251\254@\374K\330\036h\217\242,;\325\337;\v\361\315\313D\351/\365\016\005\303U\247\"\035\265?M\351\267\217\002\003\001\000\001\243Z0X0\t\006\003U\035\023\004\0020\0000\035\006\003U\035\016\004\026\004\024\222\016\315\233\347\204\306\020\217{<\221\234Ttty\367.70\037\006\003U\035#\004\0300\026\200\024VG\243\022\246\333\267\363\361\344hb\313\001E\375+\002sh0\v\006\003U\035\017\004\004\003\002\a\2000\r\006\t*\206H\206\367\r\001\001\v\005\000\003\202\001\001\000h\335\370w\213\037>\aZ\246\354U:.09\223Z\202>F\367z\320+\272\257_I\302\325\022\260m 7\353\003p\247w\360\364\265eB\t\321\312T\351\324\216\215,w\356n6\'l=S\250\022ri\344N\247\353\353\a\354\314\342i\217\244\245\1770\202\025\026\n5W\237P\372\346-\331\375\372\321\373\037\216\372\226\272\004\326cx\253k\353\247H|4S\343\v\ra\247\247`\356\304\324\337U\374\342INVH\251A.\234\231\016\212\006D\177\b\321\312E\260\006\346O\304\236\3106\026Yk@.\2279\vWA\v\242\025\365\031\234\233\355\301\350\334\352\360V\032\035\233\024s\030g\204v\342}\"\220\311\256\320\257\226\362Q\251I>\360bJA\223\362\034\324\313\256\270\r\303\273PR\265\271\365\234\336\300Z\373\250\267\345\276\b\rU}[u\216\243j\236fU\375[\333\032\243\214\022K\t\360\033\243\365\235\032\200\0023\275_\342t\0371}\332\a\206-\255\2535\242D\313\004\352?2\177\276d}\365\207|\241c#bq\230\354]\315\300 \356u\266\376k\320\236\370\v-\257\236\370^\3423\305\316 \342\361\336`1\201\241\362\270!s\304)U\272\206\367\304\216A8X\030*C\212\n\312\265i\325\362\367\221\272\036!*\t\255,Y\272C\202k\331\211\022\2760\306\355\020\332\215Q\271\217\231\303\303\006\236\354\244\035*\341\274{\036\357B+\330r\221\315\312\252\027\376\272\373`\221#U\232j\033\253\331Iz\252\315\254\\A\240\376\'\221g\t\001\301\243\211;\313\21227\0254Q7O\031v}\242\037h\201 O\246\272\t\226\356;c\322\035\327_\020\250\247\325j[\315\177\vK\365K/bqA\017\341\363\313\204-r\nq\342\230\314\"\022C\332\313\217\324\030S3\222\267\224\341\3631I\023\200\265\365\363O\323\227dw\375"
+  expected_result: REVOCATION_CHECK_FAILED
+  cert_verification_time_seconds: 1466642485
+  crl_verification_time_seconds: 1466642483
+}
diff --git a/content/browser/background_sync/background_sync_browsertest.cc b/content/browser/background_sync/background_sync_browsertest.cc
index db7d12f..91450a6c 100644
--- a/content/browser/background_sync/background_sync_browsertest.cc
+++ b/content/browser/background_sync/background_sync_browsertest.cc
@@ -90,7 +90,7 @@
     const std::string& tag,
     const base::Callback<void(bool)>& callback,
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   ASSERT_EQ(SERVICE_WORKER_OK, status);
   int64_t service_worker_id = registration->id();
   BackgroundSyncManager* sync_manager = sync_context->background_sync_manager();
diff --git a/content/browser/background_sync/background_sync_manager.cc b/content/browser/background_sync/background_sync_manager.cc
index 859c530..390983c 100644
--- a/content/browser/background_sync/background_sync_manager.cc
+++ b/content/browser/background_sync/background_sync_manager.cc
@@ -974,8 +974,7 @@
     const base::Closure& event_fired_callback,
     const base::Closure& event_completed_callback,
     ServiceWorkerStatusCode service_worker_status,
-    const scoped_refptr<ServiceWorkerRegistration>&
-        service_worker_registration) {
+    scoped_refptr<ServiceWorkerRegistration> service_worker_registration) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (service_worker_status != SERVICE_WORKER_OK) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
diff --git a/content/browser/background_sync/background_sync_manager.h b/content/browser/background_sync/background_sync_manager.h
index 52391a2..f31a83a 100644
--- a/content/browser/background_sync/background_sync_manager.h
+++ b/content/browser/background_sync/background_sync_manager.h
@@ -249,8 +249,7 @@
       const base::Closure& event_fired_callback,
       const base::Closure& event_completed_callback,
       ServiceWorkerStatusCode service_worker_status,
-      const scoped_refptr<ServiceWorkerRegistration>&
-          service_worker_registration);
+      scoped_refptr<ServiceWorkerRegistration> service_worker_registration);
   void FireReadyEventsAllEventsFiring(const base::Closure& callback);
 
   // Called when a sync event has completed.
diff --git a/content/browser/background_sync/background_sync_manager_unittest.cc b/content/browser/background_sync/background_sync_manager_unittest.cc
index f4acfb7..a5c8b14 100644
--- a/content/browser/background_sync/background_sync_manager_unittest.cc
+++ b/content/browser/background_sync/background_sync_manager_unittest.cc
@@ -66,9 +66,9 @@
 void FindServiceWorkerRegistrationCallback(
     scoped_refptr<ServiceWorkerRegistration>* out_registration,
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   EXPECT_EQ(SERVICE_WORKER_OK, status) << ServiceWorkerStatusToString(status);
-  *out_registration = registration;
+  *out_registration = std::move(registration);
 }
 
 void UnregisterServiceWorkerCallback(bool* called,
diff --git a/content/browser/background_sync/background_sync_service_impl_unittest.cc b/content/browser/background_sync/background_sync_service_impl_unittest.cc
index 64db7980..a7f1e46 100644
--- a/content/browser/background_sync/background_sync_service_impl_unittest.cc
+++ b/content/browser/background_sync/background_sync_service_impl_unittest.cc
@@ -51,9 +51,9 @@
 void FindServiceWorkerRegistrationCallback(
     scoped_refptr<ServiceWorkerRegistration>* out_registration,
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   EXPECT_EQ(SERVICE_WORKER_OK, status) << ServiceWorkerStatusToString(status);
-  *out_registration = registration;
+  *out_registration = std::move(registration);
 }
 
 // Callbacks from BackgroundSyncServiceImpl methods
diff --git a/content/browser/devtools/protocol/service_worker_handler.cc b/content/browser/devtools/protocol/service_worker_handler.cc
index b9f528fd..127b0c0 100644
--- a/content/browser/devtools/protocol/service_worker_handler.cc
+++ b/content/browser/devtools/protocol/service_worker_handler.cc
@@ -269,7 +269,7 @@
     const std::string& tag,
     bool last_chance,
     ServiceWorkerStatusCode status,
-    const scoped_refptr<content::ServiceWorkerRegistration>& registration) {
+    scoped_refptr<content::ServiceWorkerRegistration> registration) {
   if (status != SERVICE_WORKER_OK || !registration->active_version())
     return;
   BackgroundSyncManager* background_sync_manager =
@@ -279,7 +279,7 @@
   // Keep the registration while dispatching the sync event.
   background_sync_manager->EmulateDispatchSyncEvent(
       tag, std::move(version), last_chance,
-      base::Bind(&StatusNoOpKeepingRegistration, registration));
+      base::Bind(&StatusNoOpKeepingRegistration, std::move(registration)));
 }
 
 void DispatchSyncEventOnIO(scoped_refptr<ServiceWorkerContextWrapper> context,
diff --git a/content/browser/frame_host/navigation_handle_impl.cc b/content/browser/frame_host/navigation_handle_impl.cc
index 2e67b73..47dc9a2 100644
--- a/content/browser/frame_host/navigation_handle_impl.cc
+++ b/content/browser/frame_host/navigation_handle_impl.cc
@@ -72,7 +72,8 @@
       frame_tree_node_(frame_tree_node),
       next_index_(0),
       navigation_start_(navigation_start),
-      pending_nav_entry_id_(pending_nav_entry_id) {
+      pending_nav_entry_id_(pending_nav_entry_id),
+      request_context_type_(REQUEST_CONTEXT_TYPE_UNSPECIFIED) {
   DCHECK(!navigation_start.is_null());
   GetDelegate()->DidStartNavigation(this);
 
@@ -102,6 +103,11 @@
   return frame_tree_node_->navigator()->GetDelegate();
 }
 
+RequestContextType NavigationHandleImpl::GetRequestContextType() const {
+  DCHECK_GE(state_, WILL_SEND_REQUEST);
+  return request_context_type_;
+}
+
 const GURL& NavigationHandleImpl::GetURL() {
   return url_;
 }
@@ -270,6 +276,7 @@
 
   WillStartRequest(method, resource_request_body, sanitized_referrer,
                    has_user_gesture, transition, is_external_protocol,
+                   REQUEST_CONTEXT_TYPE_LOCATION,
                    base::Bind(&UpdateThrottleCheckResult, &result));
 
   // Reset the callback to ensure it will not be called later.
@@ -318,6 +325,7 @@
     bool has_user_gesture,
     ui::PageTransition transition,
     bool is_external_protocol,
+    RequestContextType request_context_type,
     const ThrottleChecksFinishedCallback& callback) {
   // |method != "POST"| should imply absence of |resource_request_body|.
   if (method != "POST" && resource_request_body) {
@@ -333,7 +341,7 @@
   has_user_gesture_ = has_user_gesture;
   transition_ = transition;
   is_external_protocol_ = is_external_protocol;
-
+  request_context_type_ = request_context_type;
   state_ = WILL_SEND_REQUEST;
   complete_callback_ = callback;
 
diff --git a/content/browser/frame_host/navigation_handle_impl.h b/content/browser/frame_host/navigation_handle_impl.h
index 985f31e..264bffa 100644
--- a/content/browser/frame_host/navigation_handle_impl.h
+++ b/content/browser/frame_host/navigation_handle_impl.h
@@ -18,6 +18,7 @@
 #include "content/common/content_export.h"
 #include "content/public/browser/navigation_data.h"
 #include "content/public/browser/navigation_throttle.h"
+#include "content/public/common/request_context_type.h"
 #include "url/gurl.h"
 
 struct FrameHostMsg_DidCommitProvisionalLoad_Params;
@@ -26,7 +27,6 @@
 
 class NavigatorDelegate;
 class ResourceRequestBodyImpl;
-struct NavigationRequestInfo;
 
 // This class keeps track of a single navigation. It is created upon receipt of
 // a DidStartProvisionalLoad IPC in a RenderFrameHost. The RenderFrameHost owns
@@ -121,6 +121,8 @@
 
   NavigatorDelegate* GetDelegate() const;
 
+  RequestContextType GetRequestContextType() const;
+
   // Returns the response headers for the request or nullptr if there are none.
   // This should only be accessed after a redirect was encountered or after the
   // navigation is ready to commit. The headers returned should not be modified,
@@ -179,6 +181,7 @@
       bool has_user_gesture,
       ui::PageTransition transition,
       bool is_external_protocol,
+      RequestContextType request_context_type,
       const ThrottleChecksFinishedCallback& callback);
 
   // Called when the URLRequest will be redirected in the network stack.
@@ -312,6 +315,9 @@
   // The unique id of the corresponding NavigationEntry.
   int pending_nav_entry_id_;
 
+  // The fetch request context type.
+  RequestContextType request_context_type_;
+
   // This callback will be run when all throttle checks have been performed.
   ThrottleChecksFinishedCallback complete_callback_;
 
diff --git a/content/browser/frame_host/navigation_handle_impl_browsertest.cc b/content/browser/frame_host/navigation_handle_impl_browsertest.cc
index 5f5d0c8..0aacdf0 100644
--- a/content/browser/frame_host/navigation_handle_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_handle_impl_browsertest.cc
@@ -6,6 +6,7 @@
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "content/public/common/request_context_type.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
@@ -146,20 +147,38 @@
 
   void Resume() { navigation_handle()->Resume(); }
 
+  RequestContextType request_context_type() { return request_context_type_; }
+
  private:
   // NavigationThrottle implementation.
   NavigationThrottle::ThrottleCheckResult WillStartRequest() override {
+    NavigationHandleImpl* navigation_handle_impl =
+        static_cast<NavigationHandleImpl*>(navigation_handle());
+    CHECK_NE(REQUEST_CONTEXT_TYPE_UNSPECIFIED,
+             navigation_handle_impl->GetRequestContextType());
+    request_context_type_ = navigation_handle_impl->GetRequestContextType();
+
     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, did_call_will_start_);
     return will_start_result_;
   }
 
   NavigationThrottle::ThrottleCheckResult WillRedirectRequest() override {
+    NavigationHandleImpl* navigation_handle_impl =
+        static_cast<NavigationHandleImpl*>(navigation_handle());
+    CHECK_EQ(request_context_type_,
+             navigation_handle_impl->GetRequestContextType());
+
     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
                             did_call_will_redirect_);
     return will_redirect_result_;
   }
 
   NavigationThrottle::ThrottleCheckResult WillProcessResponse() override {
+    NavigationHandleImpl* navigation_handle_impl =
+        static_cast<NavigationHandleImpl*>(navigation_handle());
+    CHECK_EQ(request_context_type_,
+             navigation_handle_impl->GetRequestContextType());
+
     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
                             did_call_will_process_);
     return will_process_result_;
@@ -171,6 +190,7 @@
   base::Closure did_call_will_start_;
   base::Closure did_call_will_redirect_;
   base::Closure did_call_will_process_;
+  RequestContextType request_context_type_;
 };
 
 // Install a TestNavigationThrottle on all following requests and allows waiting
@@ -654,6 +674,133 @@
             GURL(embedded_test_server()->GetURL("bar.com", "/title2.html")));
 }
 
+// Checks that the RequestContextType value is properly set.
+IN_PROC_BROWSER_TEST_F(NavigationHandleImplBrowserTest,
+                       VerifyRequestContextTypeForFrameTree) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
+  GURL b_url(embedded_test_server()->GetURL(
+      "b.com", "/cross_site_iframe_factory.html?b(c())"));
+  GURL c_url(embedded_test_server()->GetURL(
+      "c.com", "/cross_site_iframe_factory.html?c()"));
+
+  TestNavigationThrottleInstaller installer(
+      shell()->web_contents(), NavigationThrottle::PROCEED,
+      NavigationThrottle::PROCEED, NavigationThrottle::PROCEED);
+  TestNavigationManager main_manager(shell()->web_contents(), main_url);
+  TestNavigationManager b_manager(shell()->web_contents(), b_url);
+  TestNavigationManager c_manager(shell()->web_contents(), c_url);
+  NavigationStartUrlRecorder url_recorder(shell()->web_contents());
+  TestNavigationThrottle* previous_throttle = nullptr;
+
+  // Starts and verifies the main frame navigation.
+  shell()->LoadURL(main_url);
+  main_manager.WaitForWillStartRequest();
+  // The throttle should not be null.
+  EXPECT_NE(previous_throttle, installer.navigation_throttle());
+  // Checks the only URL recorded so far is the one expected for the main frame.
+  EXPECT_EQ(main_url, url_recorder.urls().back());
+  EXPECT_EQ(1ul, url_recorder.urls().size());
+  // Checks the main frame RequestContextType.
+  EXPECT_EQ(REQUEST_CONTEXT_TYPE_LOCATION,
+            installer.navigation_throttle()->request_context_type());
+  // For each navigations the throttle should be a different instance.
+  previous_throttle = installer.navigation_throttle();
+
+  // Ditto for frame b navigation.
+  main_manager.WaitForNavigationFinished();
+  b_manager.WaitForWillStartRequest();
+  EXPECT_NE(previous_throttle, installer.navigation_throttle());
+  EXPECT_EQ(b_url, url_recorder.urls().back());
+  EXPECT_EQ(2ul, url_recorder.urls().size());
+  EXPECT_EQ(REQUEST_CONTEXT_TYPE_LOCATION,
+            installer.navigation_throttle()->request_context_type());
+  previous_throttle = installer.navigation_throttle();
+
+  // Ditto for frame c navigation.
+  b_manager.WaitForNavigationFinished();
+  c_manager.WaitForWillStartRequest();
+  EXPECT_NE(previous_throttle, installer.navigation_throttle());
+  EXPECT_EQ(c_url, url_recorder.urls().back());
+  EXPECT_EQ(3ul, url_recorder.urls().size());
+  EXPECT_EQ(REQUEST_CONTEXT_TYPE_LOCATION,
+            installer.navigation_throttle()->request_context_type());
+
+  // Lets the final navigation finish so that we conclude running the
+  // RequestContextType checks that happen in TestNavigationThrottle.
+  c_manager.WaitForNavigationFinished();
+  // Confirms the last navigation did finish.
+  EXPECT_FALSE(installer.navigation_throttle());
+}
+
+// Checks that the RequestContextType value is properly set for an hyper-link.
+IN_PROC_BROWSER_TEST_F(NavigationHandleImplBrowserTest,
+                       VerifyHyperlinkRequestContextType) {
+  GURL link_url(embedded_test_server()->GetURL("/title2.html"));
+  GURL document_url(embedded_test_server()->GetURL("/simple_links.html"));
+
+  TestNavigationThrottleInstaller installer(
+      shell()->web_contents(), NavigationThrottle::PROCEED,
+      NavigationThrottle::PROCEED, NavigationThrottle::PROCEED);
+  TestNavigationManager link_manager(shell()->web_contents(), link_url);
+  NavigationStartUrlRecorder url_recorder(shell()->web_contents());
+
+  // Navigate to a page with a link.
+  EXPECT_TRUE(NavigateToURL(shell(), document_url));
+  EXPECT_EQ(document_url, url_recorder.urls().back());
+  EXPECT_EQ(1ul, url_recorder.urls().size());
+
+  // Starts the navigation from a link click and then check it.
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  bool success = false;
+  EXPECT_TRUE(ExecuteScriptAndExtractBool(
+      shell(), "window.domAutomationController.send(clickSameSiteLink());",
+      &success));
+  EXPECT_TRUE(success);
+  link_manager.WaitForWillStartRequest();
+  EXPECT_EQ(link_url, url_recorder.urls().back());
+  EXPECT_EQ(2ul, url_recorder.urls().size());
+  EXPECT_EQ(REQUEST_CONTEXT_TYPE_HYPERLINK,
+            installer.navigation_throttle()->request_context_type());
+
+  // Finishes the last navigation.
+  link_manager.WaitForNavigationFinished();
+  EXPECT_FALSE(installer.navigation_throttle());
+}
+
+// Checks that the RequestContextType value is properly set for an form (POST).
+IN_PROC_BROWSER_TEST_F(NavigationHandleImplBrowserTest,
+                       VerifyFormRequestContextType) {
+  GURL document_url(
+      embedded_test_server()->GetURL("/session_history/form.html"));
+  GURL post_url(embedded_test_server()->GetURL("/echotitle"));
+
+  TestNavigationThrottleInstaller installer(
+      shell()->web_contents(), NavigationThrottle::PROCEED,
+      NavigationThrottle::PROCEED, NavigationThrottle::PROCEED);
+  TestNavigationManager post_manager(shell()->web_contents(), post_url);
+  NavigationStartUrlRecorder url_recorder(shell()->web_contents());
+
+  // Navigate to a page with a form.
+  EXPECT_TRUE(NavigateToURL(shell(), document_url));
+  EXPECT_EQ(document_url, url_recorder.urls().back());
+  EXPECT_EQ(1ul, url_recorder.urls().size());
+
+  // Executes the form POST navigation and then check it.
+  EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  GURL submit_url("javascript:submitForm('isubmit')");
+  shell()->LoadURL(submit_url);
+  post_manager.WaitForWillStartRequest();
+  EXPECT_EQ(post_url, url_recorder.urls().back());
+  EXPECT_EQ(2ul, url_recorder.urls().size());
+  EXPECT_EQ(REQUEST_CONTEXT_TYPE_FORM,
+            installer.navigation_throttle()->request_context_type());
+
+  // Finishes the last navigation.
+  post_manager.WaitForNavigationFinished();
+  EXPECT_FALSE(installer.navigation_throttle());
+}
+
 // Specialized test that verifies the NavigationHandle gets the HTTPS upgraded
 // URL from the very beginning of the navigation.
 class NavigationHandleImplHttpsUpgradeBrowserTest
diff --git a/content/browser/frame_host/navigation_handle_impl_unittest.cc b/content/browser/frame_host/navigation_handle_impl_unittest.cc
index f77bbdf..29dc039f 100644
--- a/content/browser/frame_host/navigation_handle_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_handle_impl_unittest.cc
@@ -5,6 +5,7 @@
 #include "base/macros.h"
 #include "content/browser/frame_host/navigation_handle_impl.h"
 #include "content/public/browser/navigation_throttle.h"
+#include "content/public/common/request_context_type.h"
 #include "content/test/test_render_frame_host.h"
 
 namespace content {
@@ -67,6 +68,8 @@
     test_handle_ = NavigationHandleImpl::Create(
         GURL(), main_test_rfh()->frame_tree_node(), true, false, false,
         base::TimeTicks::Now(), 0);
+    EXPECT_EQ(REQUEST_CONTEXT_TYPE_UNSPECIFIED,
+              test_handle_->request_context_type_);
   }
 
   void TearDown() override {
@@ -103,6 +106,7 @@
     // the NavigationHandleImplTest.
     test_handle_->WillStartRequest(
         "GET", nullptr, Referrer(), false, ui::PAGE_TRANSITION_LINK, false,
+        REQUEST_CONTEXT_TYPE_LOCATION,
         base::Bind(&NavigationHandleImplTest::UpdateThrottleCheckResult,
                    base::Unretained(this)));
   }
@@ -178,6 +182,24 @@
   NavigationThrottle::ThrottleCheckResult callback_result_;
 };
 
+// Checks that the request_context_type is properly set.
+// Note: can be extended to cover more internal members.
+TEST_F(NavigationHandleImplTest, SimpleDataChecks) {
+  SimulateWillStartRequest();
+  EXPECT_EQ(REQUEST_CONTEXT_TYPE_LOCATION,
+            test_handle()->GetRequestContextType());
+
+  test_handle()->Resume();
+  SimulateWillRedirectRequest();
+  EXPECT_EQ(REQUEST_CONTEXT_TYPE_LOCATION,
+            test_handle()->GetRequestContextType());
+
+  test_handle()->Resume();
+  SimulateWillProcessResponse();
+  EXPECT_EQ(REQUEST_CONTEXT_TYPE_LOCATION,
+            test_handle()->GetRequestContextType());
+}
+
 // Checks that a deferred navigation can be properly resumed.
 TEST_F(NavigationHandleImplTest, ResumeDeferred) {
   TestNavigationThrottle* test_throttle =
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index b994fa9..797d4e2d 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -24,6 +24,7 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/stream_handle.h"
 #include "content/public/common/content_client.h"
+#include "content/public/common/request_context_type.h"
 #include "content/public/common/resource_response.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_request_headers.h"
@@ -215,6 +216,7 @@
         Referrer::SanitizeForRequest(common_params_.url,
                                      common_params_.referrer),
         begin_params_.has_user_gesture, common_params_.transition, false,
+        begin_params_.request_context_type,
         base::Bind(&NavigationRequest::OnStartChecksComplete,
                    base::Unretained(this)));
     return;
diff --git a/content/browser/loader/DEPS b/content/browser/loader/DEPS
index 1f7b4e9..40e244e0 100644
--- a/content/browser/loader/DEPS
+++ b/content/browser/loader/DEPS
@@ -160,6 +160,7 @@
     "+content/public/common/content_features.h",
     "+content/public/common/content_switches.h",
     "+content/public/common/process_type.h",
+    "+content/public/common/request_context_type.h",
     "+content/public/common/resource_type.h",
 
     # TODO: To be replaced by mojo.
diff --git a/content/browser/loader/navigation_resource_throttle.cc b/content/browser/loader/navigation_resource_throttle.cc
index 2ad781f..6797605 100644
--- a/content/browser/loader/navigation_resource_throttle.cc
+++ b/content/browser/loader/navigation_resource_throttle.cc
@@ -51,7 +51,8 @@
     const Referrer& sanitized_referrer,
     bool has_user_gesture,
     ui::PageTransition transition,
-    bool is_external_protocol) {
+    bool is_external_protocol,
+    RequestContextType request_context_type) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   RenderFrameHostImpl* render_frame_host =
       RenderFrameHostImpl::FromID(render_process_id, render_frame_host_id);
@@ -69,7 +70,7 @@
 
   navigation_handle->WillStartRequest(
       method, resource_request_body, sanitized_referrer, has_user_gesture,
-      transition, is_external_protocol,
+      transition, is_external_protocol, request_context_type,
       base::Bind(&SendCheckResultToIOThread, callback));
 }
 
@@ -138,9 +139,11 @@
 
 NavigationResourceThrottle::NavigationResourceThrottle(
     net::URLRequest* request,
-    ResourceDispatcherHostDelegate* resource_dispatcher_host_delegate)
+    ResourceDispatcherHostDelegate* resource_dispatcher_host_delegate,
+    RequestContextType request_context_type)
     : request_(request),
       resource_dispatcher_host_delegate_(resource_dispatcher_host_delegate),
+      request_context_type_(request_context_type),
       weak_ptr_factory_(this) {}
 
 NavigationResourceThrottle::~NavigationResourceThrottle() {}
@@ -171,7 +174,7 @@
                      request_->url(), Referrer(GURL(request_->referrer()),
                                                info->GetReferrerPolicy())),
                  info->HasUserGesture(), info->GetPageTransition(),
-                 is_external_protocol));
+                 is_external_protocol, request_context_type_));
   *defer = true;
 }
 
diff --git a/content/browser/loader/navigation_resource_throttle.h b/content/browser/loader/navigation_resource_throttle.h
index 45e9830..6de64890 100644
--- a/content/browser/loader/navigation_resource_throttle.h
+++ b/content/browser/loader/navigation_resource_throttle.h
@@ -9,6 +9,7 @@
 #include "base/memory/weak_ptr.h"
 #include "content/public/browser/navigation_throttle.h"
 #include "content/public/browser/resource_throttle.h"
+#include "content/public/common/request_context_type.h"
 
 namespace net {
 class URLRequest;
@@ -25,7 +26,8 @@
  public:
   NavigationResourceThrottle(
       net::URLRequest* request,
-      ResourceDispatcherHostDelegate* resource_dispatcher_host_delegate);
+      ResourceDispatcherHostDelegate* resource_dispatcher_host_delegate,
+      RequestContextType request_context_type);
   ~NavigationResourceThrottle() override;
 
   // ResourceThrottle overrides:
@@ -40,6 +42,7 @@
 
   net::URLRequest* request_;
   ResourceDispatcherHostDelegate* resource_dispatcher_host_delegate_;
+  RequestContextType request_context_type_;
   base::WeakPtrFactory<NavigationResourceThrottle> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(NavigationResourceThrottle);
diff --git a/content/browser/loader/navigation_url_loader_impl_core.cc b/content/browser/loader/navigation_url_loader_impl_core.cc
index 9ff2ada..935cf08 100644
--- a/content/browser/loader/navigation_url_loader_impl_core.cc
+++ b/content/browser/loader/navigation_url_loader_impl_core.cc
@@ -147,8 +147,8 @@
 }
 
 void NavigationURLLoaderImplCore::OnServiceWorkerChecksPerformed(
-      ServiceWorkerStatusCode status,
-      const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    ServiceWorkerStatusCode status,
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // If the navigation has a ServiceWorker, bail out immediately.
   // TODO(clamy): only bail out when the ServiceWorker has a Fetch event
diff --git a/content/browser/loader/navigation_url_loader_impl_core.h b/content/browser/loader/navigation_url_loader_impl_core.h
index 1a7a152c..495a0ef8 100644
--- a/content/browser/loader/navigation_url_loader_impl_core.h
+++ b/content/browser/loader/navigation_url_loader_impl_core.h
@@ -74,7 +74,7 @@
   // registered for it.
   void OnServiceWorkerChecksPerformed(
       ServiceWorkerStatusCode status,
-      const scoped_refptr<ServiceWorkerRegistration>& registration);
+      scoped_refptr<ServiceWorkerRegistration> registration);
 
   base::WeakPtr<NavigationURLLoaderImpl> loader_;
   NavigationResourceHandler* resource_handler_;
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index fb12a9b9..d2d0bc1 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -1633,9 +1633,10 @@
       handler.reset(new CrossSiteResourceHandler(std::move(handler), request));
   }
 
-  return AddStandardHandlers(request, request_data.resource_type,
-                             resource_context, filter_->appcache_service(),
-                             child_id, route_id, std::move(handler));
+  return AddStandardHandlers(
+      request, request_data.resource_type, resource_context,
+      request_data.fetch_request_context_type, filter_->appcache_service(),
+      child_id, route_id, std::move(handler));
 }
 
 std::unique_ptr<ResourceHandler>
@@ -1643,6 +1644,7 @@
     net::URLRequest* request,
     ResourceType resource_type,
     ResourceContext* resource_context,
+    RequestContextType fetch_request_context_type,
     AppCacheService* appcache_service,
     int child_id,
     int route_id,
@@ -1669,8 +1671,10 @@
   // Add a NavigationResourceThrottle for navigations.
   // PlzNavigate: the throttle is unnecessary as communication with the UI
   // thread is handled by the NavigationURLloader.
-  if (!IsBrowserSideNavigationEnabled() && IsResourceTypeFrame(resource_type))
-    throttles.push_back(new NavigationResourceThrottle(request, delegate()));
+  if (!IsBrowserSideNavigationEnabled() && IsResourceTypeFrame(resource_type)) {
+    throttles.push_back(new NavigationResourceThrottle(
+        request, delegate(), fetch_request_context_type));
+  }
 
   if (delegate_) {
     delegate_->RequestBeginning(request,
@@ -2258,6 +2262,7 @@
   // currently it's a no-op.
   handler =
       AddStandardHandlers(new_request.get(), resource_type, resource_context,
+                          info.begin_params.request_context_type,
                           nullptr,  // appcache_service
                           -1,       // child_id
                           -1,       // route_id
diff --git a/content/browser/loader/resource_dispatcher_host_impl.h b/content/browser/loader/resource_dispatcher_host_impl.h
index a7242ff3..8682f47 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.h
+++ b/content/browser/loader/resource_dispatcher_host_impl.h
@@ -34,6 +34,7 @@
 #include "content/public/browser/global_request_id.h"
 #include "content/public/browser/resource_dispatcher_host.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "content/public/common/request_context_type.h"
 #include "content/public/common/resource_type.h"
 #include "ipc/ipc_message.h"
 #include "net/base/request_priority.h"
@@ -490,6 +491,7 @@
       net::URLRequest* request,
       ResourceType resource_type,
       ResourceContext* resource_context,
+      RequestContextType fetch_request_context_type,
       AppCacheService* appcache_service,
       int child_id,
       int route_id,
diff --git a/content/browser/media/webrtc/webrtc_internals.cc b/content/browser/media/webrtc/webrtc_internals.cc
index ae8a7ed..3be45a2 100644
--- a/content/browser/media/webrtc/webrtc_internals.cc
+++ b/content/browser/media/webrtc/webrtc_internals.cc
@@ -69,12 +69,14 @@
   return value_.get();
 }
 
-WebRTCInternals::WebRTCInternals() : WebRTCInternals(500) {}
+WebRTCInternals::WebRTCInternals() : WebRTCInternals(500, true) {}
 
-WebRTCInternals::WebRTCInternals(int aggregate_updates_ms)
+WebRTCInternals::WebRTCInternals(int aggregate_updates_ms,
+                                 bool should_block_power_saving)
     : audio_debug_recordings_(false),
       event_log_recordings_(false),
       selecting_event_log_(false),
+      should_block_power_saving_(should_block_power_saving),
       aggregate_updates_ms_(aggregate_updates_ms),
       weak_factory_(this) {
 // TODO(grunell): Shouldn't all the webrtc_internals* files be excluded from the
@@ -497,6 +499,8 @@
 
 void WebRTCInternals::CreateOrReleasePowerSaveBlocker() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (!should_block_power_saving_)
+    return;
 
   if (peer_connection_data_.empty() && power_save_blocker_) {
     DVLOG(1) << ("Releasing the block on application suspension since no "
diff --git a/content/browser/media/webrtc/webrtc_internals.h b/content/browser/media/webrtc/webrtc_internals.h
index 35a1d89..aec660ad 100644
--- a/content/browser/media/webrtc/webrtc_internals.h
+++ b/content/browser/media/webrtc/webrtc_internals.h
@@ -114,7 +114,7 @@
   // instance.
   // The default ctor sets |aggregate_updates_ms| to 500ms.
   WebRTCInternals();
-  explicit WebRTCInternals(int aggregate_updates_ms);
+  WebRTCInternals(int aggregate_updates_ms, bool should_block_power_saving);
   ~WebRTCInternals() override;
 
  private:
@@ -207,6 +207,7 @@
   // PowerSaveBlocker.  This prevents the application from being suspended while
   // remoting.
   std::unique_ptr<device::PowerSaveBlocker> power_save_blocker_;
+  const bool should_block_power_saving_;
 
   // Set of render process hosts that |this| is registered as an observer on.
   base::hash_set<int> render_process_id_set_;
diff --git a/content/browser/media/webrtc/webrtc_internals_message_handler.cc b/content/browser/media/webrtc/webrtc_internals_message_handler.cc
index ea74c5b..48e45c5 100644
--- a/content/browser/media/webrtc/webrtc_internals_message_handler.cc
+++ b/content/browser/media/webrtc/webrtc_internals_message_handler.cc
@@ -15,12 +15,17 @@
 
 namespace content {
 
-WebRTCInternalsMessageHandler::WebRTCInternalsMessageHandler() {
-  WebRTCInternals::GetInstance()->AddObserver(this);
+WebRTCInternalsMessageHandler::WebRTCInternalsMessageHandler()
+    : WebRTCInternalsMessageHandler(WebRTCInternals::GetInstance()) {}
+
+WebRTCInternalsMessageHandler::WebRTCInternalsMessageHandler(
+    WebRTCInternals* webrtc_internals)
+    : webrtc_internals_(webrtc_internals) {
+  webrtc_internals_->AddObserver(this);
 }
 
 WebRTCInternalsMessageHandler::~WebRTCInternalsMessageHandler() {
-  WebRTCInternals::GetInstance()->RemoveObserver(this);
+  webrtc_internals_->RemoveObserver(this);
 }
 
 void WebRTCInternalsMessageHandler::RegisterMessages() {
@@ -84,10 +89,9 @@
 void WebRTCInternalsMessageHandler::OnSetAudioDebugRecordingsEnabled(
     bool enable, const base::ListValue* /* unused_list */) {
   if (enable) {
-    WebRTCInternals::GetInstance()->EnableAudioDebugRecordings(
-        web_ui()->GetWebContents());
+    webrtc_internals_->EnableAudioDebugRecordings(web_ui()->GetWebContents());
   } else {
-    WebRTCInternals::GetInstance()->DisableAudioDebugRecordings();
+    webrtc_internals_->DisableAudioDebugRecordings();
   }
 }
 
@@ -95,18 +99,17 @@
     bool enable,
     const base::ListValue* /* unused_list */) {
   if (enable) {
-    WebRTCInternals::GetInstance()->EnableEventLogRecordings(
-        web_ui()->GetWebContents());
+    webrtc_internals_->EnableEventLogRecordings(web_ui()->GetWebContents());
   } else {
-    WebRTCInternals::GetInstance()->DisableEventLogRecordings();
+    webrtc_internals_->DisableEventLogRecordings();
   }
 }
 
 void WebRTCInternalsMessageHandler::OnDOMLoadDone(
     const base::ListValue* /* unused_list */) {
-  WebRTCInternals::GetInstance()->UpdateObserver(this);
+  webrtc_internals_->UpdateObserver(this);
 
-  if (WebRTCInternals::GetInstance()->IsAudioDebugRecordingsEnabled()) {
+  if (webrtc_internals_->IsAudioDebugRecordingsEnabled()) {
     RenderFrameHost* host = GetWebRTCInternalsHost();
     if (!host)
       return;
diff --git a/content/browser/media/webrtc/webrtc_internals_message_handler.h b/content/browser/media/webrtc/webrtc_internals_message_handler.h
index 82e242e..296ada1 100644
--- a/content/browser/media/webrtc/webrtc_internals_message_handler.h
+++ b/content/browser/media/webrtc/webrtc_internals_message_handler.h
@@ -17,11 +17,13 @@
 namespace content {
 
 class RenderFrameHost;
+class WebRTCInternals;
 
 // This class handles messages to and from WebRTCInternalsUI.
 // It delegates all its work to WebRTCInternalsProxy on the IO thread.
-class WebRTCInternalsMessageHandler : public WebUIMessageHandler,
-                                      public WebRTCInternalsUIObserver{
+class CONTENT_EXPORT WebRTCInternalsMessageHandler
+    : public WebUIMessageHandler,
+      public NON_EXPORTED_BASE(WebRTCInternalsUIObserver) {
  public:
   WebRTCInternalsMessageHandler();
   ~WebRTCInternalsMessageHandler() override;
@@ -32,6 +34,12 @@
   // WebRTCInternalsUIObserver override.
   void OnUpdate(const std::string& command, const base::Value* args) override;
 
+ protected:
+  // The WebRTCInternals to use. Always WebRTCInternals::GetInstance()
+  // except for testing.
+  explicit WebRTCInternalsMessageHandler(WebRTCInternals* webrtc_internals);
+  WebRTCInternals* const webrtc_internals_;
+
  private:
   // Returns a pointer to the RFH iff it is currently hosting the
   // webrtc-internals page.
diff --git a/content/browser/media/webrtc/webrtc_internals_message_handler_unittest.cc b/content/browser/media/webrtc/webrtc_internals_message_handler_unittest.cc
new file mode 100644
index 0000000..65b5e81
--- /dev/null
+++ b/content/browser/media/webrtc/webrtc_internals_message_handler_unittest.cc
@@ -0,0 +1,89 @@
+// Copyright (c) 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/media/webrtc/webrtc_internals_message_handler.h"
+
+#include <memory>
+#include <string>
+
+#include "base/run_loop.h"
+#include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/media/webrtc/webrtc_internals.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/url_constants.h"
+#include "content/public/test/test_web_ui.h"
+#include "content/test/test_web_contents.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+namespace {
+
+static const char kConstraints[] = "c";
+static const char kRtcConfiguration[] = "r";
+static const char kUrl[] = "u";
+
+class WebRTCInternalsMessageHandlerForTest
+    : public WebRTCInternalsMessageHandler {
+ public:
+  WebRTCInternalsMessageHandlerForTest(WebRTCInternals* webrtc_internals,
+                                       WebUI* web_ui)
+      : WebRTCInternalsMessageHandler(webrtc_internals) {
+    set_web_ui(web_ui);
+  }
+
+  ~WebRTCInternalsMessageHandlerForTest() override {}
+};
+
+class WebRTCInternalsForTest : public NON_EXPORTED_BASE(WebRTCInternals) {
+ public:
+  WebRTCInternalsForTest() : WebRTCInternals(0, false) {}
+  ~WebRTCInternalsForTest() override {}
+};
+
+}  // namespace
+
+class WebRtcInternalsMessageHandlerTest : public RenderViewHostTestHarness {
+ public:
+  WebRtcInternalsMessageHandlerTest() {}
+
+ protected:
+  void SetUp() override {
+    RenderViewHostTestHarness::SetUp();
+    web_ui_.reset(new TestWebUI());
+    web_ui_->set_web_contents(web_contents());
+  }
+
+  void TearDown() override {
+    web_ui_->set_web_contents(nullptr);
+    web_ui_.reset();
+    RenderViewHostTestHarness::TearDown();
+  }
+
+  std::unique_ptr<TestWebUI> web_ui_;
+};
+
+TEST_F(WebRtcInternalsMessageHandlerTest, DontRunJSBeforeNavigationCommitted) {
+  GURL webrtc_url(std::string("chrome://") + kChromeUIWebRTCInternalsHost);
+  GURL example_url("http://www.example.com/");
+
+  WebRTCInternalsForTest webrtc_internals;
+  WebRTCInternalsMessageHandlerForTest observer(&webrtc_internals,
+                                                web_ui_.get());
+
+  NavigateAndCommit(example_url);
+  webrtc_internals.OnAddPeerConnection(0, 1, 2, kUrl, kRtcConfiguration,
+                                       kConstraints);
+  base::RunLoop().RunUntilIdle();
+
+  static_cast<TestWebContents*>(web_contents())->StartNavigation(webrtc_url);
+  // We still shouldn't run JS, since navigation to webrtc-internals isn't
+  // finished.
+  webrtc_internals.OnRemovePeerConnection(1, 2);
+  base::RunLoop().RunUntilIdle();
+
+  webrtc_internals.RemoveObserver(&observer);
+}
+
+}  // namespace content
diff --git a/content/browser/media/webrtc/webrtc_internals_unittest.cc b/content/browser/media/webrtc/webrtc_internals_unittest.cc
index f3b048f..a652603c 100644
--- a/content/browser/media/webrtc/webrtc_internals_unittest.cc
+++ b/content/browser/media/webrtc/webrtc_internals_unittest.cc
@@ -52,7 +52,7 @@
 // updates (changes down from 500ms to 1ms).
 class WebRTCInternalsForTest : public NON_EXPORTED_BASE(WebRTCInternals) {
  public:
-  WebRTCInternalsForTest() : WebRTCInternals(1) {}
+  WebRTCInternalsForTest() : WebRTCInternals(1, true) {}
   ~WebRTCInternalsForTest() override {}
 };
 
diff --git a/content/browser/notifications/notification_event_dispatcher_impl.cc b/content/browser/notifications/notification_event_dispatcher_impl.cc
index 032d503..7c3c058 100644
--- a/content/browser/notifications/notification_event_dispatcher_impl.cc
+++ b/content/browser/notifications/notification_event_dispatcher_impl.cc
@@ -94,8 +94,7 @@
     const NotificationOperationCallback& dispatch_event_action,
     const NotificationDispatchCompleteCallback& dispatch_error_callback,
     ServiceWorkerStatusCode service_worker_status,
-    const scoped_refptr<ServiceWorkerRegistration>&
-        service_worker_registration) {
+    scoped_refptr<ServiceWorkerRegistration> service_worker_registration) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 #if defined(OS_ANDROID)
   // This LOG(INFO) deliberately exists to help track down the cause of
diff --git a/content/browser/notifications/notification_message_filter.cc b/content/browser/notifications/notification_message_filter.cc
index 766bb4f..e37d21b 100644
--- a/content/browser/notifications/notification_message_filter.cc
+++ b/content/browser/notifications/notification_message_filter.cc
@@ -222,7 +222,7 @@
     const NotificationResources& notification_resources,
     int64_t persistent_notification_id,
     content::ServiceWorkerStatusCode service_worker_status,
-    const scoped_refptr<content::ServiceWorkerRegistration>& registration) {
+    scoped_refptr<content::ServiceWorkerRegistration> registration) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   if (service_worker_status != SERVICE_WORKER_OK) {
diff --git a/content/browser/notifications/notification_message_filter.h b/content/browser/notifications/notification_message_filter.h
index 5ebce0e..bc28879 100644
--- a/content/browser/notifications/notification_message_filter.h
+++ b/content/browser/notifications/notification_message_filter.h
@@ -98,7 +98,7 @@
       const NotificationResources& notification_resources,
       int64_t persistent_notification_id,
       content::ServiceWorkerStatusCode service_worker_status,
-      const scoped_refptr<content::ServiceWorkerRegistration>& registration);
+      scoped_refptr<ServiceWorkerRegistration> registration);
 
   // Callback to be invoked when all notifications belonging to a Service Worker
   // registration have been read from the database. The |success| argument
diff --git a/content/browser/push_messaging/push_messaging_router.cc b/content/browser/push_messaging/push_messaging_router.cc
index 8e93fe7..01a8574 100644
--- a/content/browser/push_messaging/push_messaging_router.cc
+++ b/content/browser/push_messaging/push_messaging_router.cc
@@ -73,8 +73,7 @@
     const PushEventPayload& payload,
     const DeliverMessageCallback& deliver_message_callback,
     ServiceWorkerStatusCode service_worker_status,
-    const scoped_refptr<ServiceWorkerRegistration>&
-        service_worker_registration) {
+    scoped_refptr<ServiceWorkerRegistration> service_worker_registration) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // TODO(mvanouwerkerk): UMA logging.
   if (service_worker_status != SERVICE_WORKER_OK) {
diff --git a/content/browser/push_messaging/push_messaging_router.h b/content/browser/push_messaging/push_messaging_router.h
index 64adb8a..517863989 100644
--- a/content/browser/push_messaging/push_messaging_router.h
+++ b/content/browser/push_messaging/push_messaging_router.h
@@ -54,8 +54,7 @@
       const PushEventPayload& payload,
       const DeliverMessageCallback& deliver_message_callback,
       ServiceWorkerStatusCode service_worker_status,
-      const scoped_refptr<ServiceWorkerRegistration>&
-          service_worker_registration);
+      scoped_refptr<ServiceWorkerRegistration> service_worker_registration);
 
   // Delivers a push message with |data| to a specific |service_worker|. Must be
   // called on the IO thread, with the the worker running.
diff --git a/content/browser/service_worker/foreign_fetch_request_handler.cc b/content/browser/service_worker/foreign_fetch_request_handler.cc
index 9b21310..ac99dffe 100644
--- a/content/browser/service_worker/foreign_fetch_request_handler.cc
+++ b/content/browser/service_worker/foreign_fetch_request_handler.cc
@@ -174,7 +174,7 @@
 void ForeignFetchRequestHandler::DidFindRegistration(
     const base::WeakPtr<ServiceWorkerURLRequestJob>& job,
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   if (!job || job.get() != job_.get()) {
     // No more job to handle, or job changed somehow, so just return.
     return;
diff --git a/content/browser/service_worker/foreign_fetch_request_handler.h b/content/browser/service_worker/foreign_fetch_request_handler.h
index c9c3026..47d0fde 100644
--- a/content/browser/service_worker/foreign_fetch_request_handler.h
+++ b/content/browser/service_worker/foreign_fetch_request_handler.h
@@ -96,7 +96,7 @@
   void DidFindRegistration(
       const base::WeakPtr<ServiceWorkerURLRequestJob>& job,
       ServiceWorkerStatusCode status,
-      const scoped_refptr<ServiceWorkerRegistration>& registration);
+      scoped_refptr<ServiceWorkerRegistration> registration);
 
   // ServiceWorkerURLRequestJob::Delegate implementation:
   void OnPrepareToRestart() override;
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index 0be50994..e10b3e2 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -134,7 +134,7 @@
     const base::Closure& quit,
     ServiceWorkerStatusCode* out_status,
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   *out_status = status;
   if (!quit.is_null())
     BrowserThread::PostTask(run_quit_thread, FROM_HERE, quit);
@@ -1426,7 +1426,7 @@
       ServiceWorkerStatusCode* out_status,
       const base::Closure& continuation,
       ServiceWorkerStatusCode status,
-      const scoped_refptr<ServiceWorkerRegistration>& registration) {
+      scoped_refptr<ServiceWorkerRegistration> registration) {
     *out_status = status;
     if (!registration.get())
       EXPECT_NE(SERVICE_WORKER_OK, status);
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc
index 2db34643..eff6187 100644
--- a/content/browser/service_worker/service_worker_context_core.cc
+++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -823,7 +823,7 @@
     const GURL& other_url,
     const ServiceWorkerContext::CheckHasServiceWorkerCallback callback,
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   if (status != SERVICE_WORKER_OK) {
     callback.Run(false);
     return;
@@ -843,7 +843,7 @@
     registration->RegisterRegistrationFinishedCallback(
         base::Bind(&ServiceWorkerContextCore::
                        OnRegistrationFinishedForCheckHasServiceWorker,
-                   AsWeakPtr(), callback, registration));
+                   AsWeakPtr(), callback, std::move(registration)));
     return;
   }
 
@@ -852,7 +852,7 @@
 
 void ServiceWorkerContextCore::OnRegistrationFinishedForCheckHasServiceWorker(
     const ServiceWorkerContext::CheckHasServiceWorkerCallback callback,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   callback.Run(registration->active_version() ||
                registration->waiting_version());
 }
diff --git a/content/browser/service_worker/service_worker_context_core.h b/content/browser/service_worker/service_worker_context_core.h
index 71dcfe8d..472ebe2 100644
--- a/content/browser/service_worker/service_worker_context_core.h
+++ b/content/browser/service_worker/service_worker_context_core.h
@@ -333,10 +333,10 @@
       const GURL& other_url,
       const ServiceWorkerContext::CheckHasServiceWorkerCallback callback,
       ServiceWorkerStatusCode status,
-      const scoped_refptr<ServiceWorkerRegistration>& registration);
+      scoped_refptr<ServiceWorkerRegistration> registration);
   void OnRegistrationFinishedForCheckHasServiceWorker(
       const ServiceWorkerContext::CheckHasServiceWorkerCallback callback,
-      const scoped_refptr<ServiceWorkerRegistration>& registration);
+      scoped_refptr<ServiceWorkerRegistration> registration);
 
   // It's safe to store a raw pointer instead of a scoped_refptr to |wrapper_|
   // because the Wrapper::Shutdown call that hops threads to destroy |this| uses
diff --git a/content/browser/service_worker/service_worker_context_unittest.cc b/content/browser/service_worker/service_worker_context_unittest.cc
index 05409c83..fdfee7b 100644
--- a/content/browser/service_worker/service_worker_context_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_unittest.cc
@@ -59,7 +59,7 @@
     bool expect_waiting,
     bool expect_active,
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   ASSERT_EQ(expect_status, status);
   if (status != SERVICE_WORKER_OK) {
     EXPECT_FALSE(registration.get());
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index 0c32d716..bb5d2de 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -55,7 +55,7 @@
 void StartActiveWorkerOnIO(
     const ServiceWorkerContextWrapper::StatusCallback& callback,
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (status == SERVICE_WORKER_OK) {
     // Pass the reference of |registration| to WorkerStarted callback to prevent
@@ -72,7 +72,7 @@
 
 void SkipWaitingWorkerOnIO(
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (status != SERVICE_WORKER_OK || !registration->waiting_version())
     return;
@@ -400,7 +400,7 @@
 
 void ServiceWorkerContextWrapper::DidFindRegistrationForUpdate(
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   if (status != SERVICE_WORKER_OK)
@@ -564,7 +564,7 @@
 void ServiceWorkerContextWrapper::DidFindRegistrationForFindReady(
     const FindRegistrationCallback& callback,
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (status != SERVICE_WORKER_OK) {
     callback.Run(status, nullptr);
@@ -587,17 +587,17 @@
     // Wait until the version is activated.
     active_version->RegisterStatusChangeCallback(base::Bind(
         &ServiceWorkerContextWrapper::OnStatusChangedForFindReadyRegistration,
-        this, callback, registration));
+        this, callback, std::move(registration)));
     return;
   }
 
   DCHECK_EQ(ServiceWorkerVersion::ACTIVATED, active_version->status());
-  callback.Run(SERVICE_WORKER_OK, registration);
+  callback.Run(SERVICE_WORKER_OK, std::move(registration));
 }
 
 void ServiceWorkerContextWrapper::OnStatusChangedForFindReadyRegistration(
     const FindRegistrationCallback& callback,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   scoped_refptr<ServiceWorkerVersion> active_version =
       registration->active_version();
diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h
index 02858028..ab4c900b 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.h
+++ b/content/browser/service_worker/service_worker_context_wrapper.h
@@ -212,10 +212,10 @@
   void DidFindRegistrationForFindReady(
       const FindRegistrationCallback& callback,
       ServiceWorkerStatusCode status,
-      const scoped_refptr<ServiceWorkerRegistration>& registration);
+      scoped_refptr<ServiceWorkerRegistration> registration);
   void OnStatusChangedForFindReadyRegistration(
       const FindRegistrationCallback& callback,
-      const scoped_refptr<ServiceWorkerRegistration>& registration);
+      scoped_refptr<ServiceWorkerRegistration> registration);
 
   void DidDeleteAndStartOver(ServiceWorkerStatusCode status);
 
@@ -229,7 +229,7 @@
 
   void DidFindRegistrationForUpdate(
       ServiceWorkerStatusCode status,
-      const scoped_refptr<content::ServiceWorkerRegistration>& registration);
+      scoped_refptr<content::ServiceWorkerRegistration> registration);
 
   // The core context is only for use on the IO thread.
   // Can be null before/during init, during/after shutdown, and after
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.cc b/content/browser/service_worker/service_worker_controllee_request_handler.cc
index 2aedf74..6312b57 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.cc
@@ -176,10 +176,10 @@
                                 weak_factory_.GetWeakPtr()));
 }
 
-void
-ServiceWorkerControlleeRequestHandler::DidLookupRegistrationForMainResource(
-    ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+void ServiceWorkerControlleeRequestHandler::
+    DidLookupRegistrationForMainResource(
+        ServiceWorkerStatusCode status,
+        scoped_refptr<ServiceWorkerRegistration> registration) {
   // The job may have been canceled and then destroyed before this was invoked.
   if (!job_)
     return;
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.h b/content/browser/service_worker/service_worker_controllee_request_handler.h
index 8da2a56a..a6e48b2 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.h
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.h
@@ -66,7 +66,7 @@
   void PrepareForMainResource(const net::URLRequest* request);
   void DidLookupRegistrationForMainResource(
       ServiceWorkerStatusCode status,
-      const scoped_refptr<ServiceWorkerRegistration>& registration);
+      scoped_refptr<ServiceWorkerRegistration> registration);
   void OnVersionStatusChanged(
       ServiceWorkerRegistration* registration,
       ServiceWorkerVersion* version);
diff --git a/content/browser/service_worker/service_worker_dispatcher_host.cc b/content/browser/service_worker/service_worker_dispatcher_host.cc
index 10b633b9..577d660a 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host.cc
+++ b/content/browser/service_worker/service_worker_dispatcher_host.cc
@@ -1285,7 +1285,7 @@
     int provider_id,
     int request_id,
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   TRACE_EVENT_ASYNC_END2(
       "ServiceWorker", "ServiceWorkerDispatcherHost::GetRegistration",
       request_id, "Status", status, "Registration ID",
diff --git a/content/browser/service_worker/service_worker_dispatcher_host.h b/content/browser/service_worker/service_worker_dispatcher_host.h
index 9e1c587c..0f4bf5eec8ef 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host.h
+++ b/content/browser/service_worker/service_worker_dispatcher_host.h
@@ -213,7 +213,7 @@
       int provider_id,
       int request_id,
       ServiceWorkerStatusCode status,
-      const scoped_refptr<ServiceWorkerRegistration>& registration);
+      scoped_refptr<ServiceWorkerRegistration> registration);
   void GetRegistrationsComplete(
       int thread_id,
       int provider_id,
diff --git a/content/browser/service_worker/service_worker_job_unittest.cc b/content/browser/service_worker/service_worker_job_unittest.cc
index d06955f..14729cb 100644
--- a/content/browser/service_worker/service_worker_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_job_unittest.cc
@@ -58,10 +58,10 @@
     bool* called,
     scoped_refptr<ServiceWorkerRegistration>* registration,
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& result) {
+    scoped_refptr<ServiceWorkerRegistration> result) {
   EXPECT_EQ(expected_status, status);
   *called = true;
-  *registration = result;
+  *registration = std::move(result);
 }
 
 // Creates a callback which both keeps track of if it's been called,
diff --git a/content/browser/service_worker/service_worker_read_from_cache_job_unittest.cc b/content/browser/service_worker/service_worker_read_from_cache_job_unittest.cc
index 63e28ce..cc84e66 100644
--- a/content/browser/service_worker/service_worker_read_from_cache_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_read_from_cache_job_unittest.cc
@@ -47,7 +47,7 @@
     ServiceWorkerStatusCode* status_out,
     const base::Closure& quit_closure,
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   *status_out = status;
   quit_closure.Run();
 }
diff --git a/content/browser/service_worker/service_worker_register_job.cc b/content/browser/service_worker/service_worker_register_job.cc
index fbfb635..b6125ad 100644
--- a/content/browser/service_worker/service_worker_register_job.cc
+++ b/content/browser/service_worker/service_worker_register_job.cc
@@ -152,10 +152,10 @@
 ServiceWorkerRegisterJob::Internal::~Internal() {}
 
 void ServiceWorkerRegisterJob::set_registration(
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   DCHECK(phase_ == START || phase_ == REGISTER) << phase_;
   DCHECK(!internal_.registration.get());
-  internal_.registration = registration;
+  internal_.registration = std::move(registration);
 }
 
 ServiceWorkerRegistration* ServiceWorkerRegisterJob::registration() {
@@ -209,7 +209,7 @@
 // Throughout this file, comments in quotes are excerpts from the spec.
 void ServiceWorkerRegisterJob::ContinueWithRegistration(
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
+    scoped_refptr<ServiceWorkerRegistration> existing_registration) {
   DCHECK_EQ(REGISTRATION_JOB, job_type_);
   if (status != SERVICE_WORKER_ERROR_NOT_FOUND && status != SERVICE_WORKER_OK) {
     Complete(status);
@@ -248,7 +248,7 @@
 
 void ServiceWorkerRegisterJob::ContinueWithUpdate(
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& existing_registration) {
+    scoped_refptr<ServiceWorkerRegistration> existing_registration) {
   DCHECK_EQ(UPDATE_JOB, job_type_);
   if (status != SERVICE_WORKER_OK) {
     Complete(status);
@@ -294,7 +294,7 @@
 }
 
 void ServiceWorkerRegisterJob::ContinueWithUninstallingRegistration(
-    const scoped_refptr<ServiceWorkerRegistration>& existing_registration,
+    scoped_refptr<ServiceWorkerRegistration> existing_registration,
     ServiceWorkerStatusCode status) {
   if (status != SERVICE_WORKER_OK) {
     Complete(status);
@@ -306,7 +306,7 @@
 }
 
 void ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl(
-    const scoped_refptr<ServiceWorkerRegistration>& existing_registration,
+    scoped_refptr<ServiceWorkerRegistration> existing_registration,
     ServiceWorkerStatusCode status) {
   if (status != SERVICE_WORKER_OK) {
     Complete(status);
diff --git a/content/browser/service_worker/service_worker_register_job.h b/content/browser/service_worker/service_worker_register_job.h
index 153203e..7ec65a8 100644
--- a/content/browser/service_worker/service_worker_register_job.h
+++ b/content/browser/service_worker/service_worker_register_job.h
@@ -96,8 +96,7 @@
     scoped_refptr<ServiceWorkerVersion> new_version;
   };
 
-  void set_registration(
-      const scoped_refptr<ServiceWorkerRegistration>& registration);
+  void set_registration(scoped_refptr<ServiceWorkerRegistration> registration);
   ServiceWorkerRegistration* registration();
   void set_new_version(ServiceWorkerVersion* version);
   ServiceWorkerVersion* new_version();
@@ -107,16 +106,16 @@
   void StartImpl();
   void ContinueWithRegistration(
       ServiceWorkerStatusCode status,
-      const scoped_refptr<ServiceWorkerRegistration>& registration);
+      scoped_refptr<ServiceWorkerRegistration> registration);
   void ContinueWithUpdate(
       ServiceWorkerStatusCode status,
-      const scoped_refptr<ServiceWorkerRegistration>& registration);
+      scoped_refptr<ServiceWorkerRegistration> registration);
   void RegisterAndContinue();
   void ContinueWithUninstallingRegistration(
-      const scoped_refptr<ServiceWorkerRegistration>& existing_registration,
+      scoped_refptr<ServiceWorkerRegistration> existing_registration,
       ServiceWorkerStatusCode status);
   void ContinueWithRegistrationForSameScriptUrl(
-      const scoped_refptr<ServiceWorkerRegistration>& existing_registration,
+      scoped_refptr<ServiceWorkerRegistration> existing_registration,
       ServiceWorkerStatusCode status);
   void UpdateAndContinue();
   void OnStartWorkerFinished(ServiceWorkerStatusCode status);
diff --git a/content/browser/service_worker/service_worker_storage.cc b/content/browser/service_worker/service_worker_storage.cc
index 7fe9008..8cf5ce3 100644
--- a/content/browser/service_worker/service_worker_storage.cc
+++ b/content/browser/service_worker/service_worker_storage.cc
@@ -39,7 +39,7 @@
 }
 
 void CompleteFindNow(
-    const scoped_refptr<ServiceWorkerRegistration>& registration,
+    scoped_refptr<ServiceWorkerRegistration> registration,
     ServiceWorkerStatusCode status,
     const ServiceWorkerStorage::FindRegistrationCallback& callback) {
   if (registration && registration->is_deleted()) {
@@ -47,16 +47,16 @@
     callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND, nullptr);
     return;
   }
-  callback.Run(status, registration);
+  callback.Run(status, std::move(registration));
 }
 
 void CompleteFindSoon(
     const tracked_objects::Location& from_here,
-    const scoped_refptr<ServiceWorkerRegistration>& registration,
+    scoped_refptr<ServiceWorkerRegistration> registration,
     ServiceWorkerStatusCode status,
     const ServiceWorkerStorage::FindRegistrationCallback& callback) {
-  RunSoon(from_here,
-          base::Bind(&CompleteFindNow, registration, status, callback));
+  RunSoon(from_here, base::Bind(&CompleteFindNow, std::move(registration),
+                                status, callback));
 }
 
 const base::FilePath::CharType kDatabaseName[] =
@@ -167,9 +167,7 @@
         TRACE_EVENT_SCOPE_THREAD,
         "URL", document_url.spec(),
         "Status", ServiceWorkerStatusToString(status));
-    CompleteFindNow(installing_registration,
-                    status,
-                    callback);
+    CompleteFindNow(std::move(installing_registration), status, callback);
     return;
   }
 
@@ -214,10 +212,11 @@
     // Look for something currently being installed.
     scoped_refptr<ServiceWorkerRegistration> installing_registration =
         FindInstallingRegistrationForPattern(scope);
-    CompleteFindSoon(FROM_HERE, installing_registration,
-                     installing_registration ? SERVICE_WORKER_OK
-                                             : SERVICE_WORKER_ERROR_NOT_FOUND,
-                     callback);
+    ServiceWorkerStatusCode installing_status =
+        installing_registration ? SERVICE_WORKER_OK
+                                : SERVICE_WORKER_ERROR_NOT_FOUND;
+    CompleteFindSoon(FROM_HERE, std::move(installing_registration),
+                     installing_status, callback);
     return;
   }
 
@@ -277,7 +276,7 @@
   scoped_refptr<ServiceWorkerRegistration> registration =
       context_->GetLiveRegistration(registration_id);
   if (registration) {
-    CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
+    CompleteFindNow(std::move(registration), SERVICE_WORKER_OK, callback);
     return;
   }
 
@@ -896,7 +895,7 @@
     ServiceWorkerStatusCode installing_status =
         installing_registration ? SERVICE_WORKER_OK
                                 : SERVICE_WORKER_ERROR_NOT_FOUND;
-    callback.Run(installing_status, installing_registration);
+    callback.Run(installing_status, std::move(installing_registration));
     TRACE_EVENT_ASYNC_END2(
         "ServiceWorker",
         "ServiceWorkerStorage::FindRegistrationForDocument",
@@ -933,9 +932,10 @@
   if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
     scoped_refptr<ServiceWorkerRegistration> installing_registration =
         FindInstallingRegistrationForPattern(scope);
-    callback.Run(installing_registration ? SERVICE_WORKER_OK
-                                         : SERVICE_WORKER_ERROR_NOT_FOUND,
-                 installing_registration);
+    ServiceWorkerStatusCode installing_status =
+        installing_registration ? SERVICE_WORKER_OK
+                                : SERVICE_WORKER_ERROR_NOT_FOUND;
+    callback.Run(installing_status, std::move(installing_registration));
     return;
   }
 
@@ -973,7 +973,7 @@
   DCHECK(!resources.empty());
   scoped_refptr<ServiceWorkerRegistration> registration =
       GetOrCreateRegistration(data, resources);
-  CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
+  CompleteFindNow(std::move(registration), SERVICE_WORKER_OK, callback);
 }
 
 void ServiceWorkerStorage::DidGetRegistrations(
@@ -1012,7 +1012,7 @@
     }
   }
 
-  callback.Run(SERVICE_WORKER_OK, registrations);
+  callback.Run(SERVICE_WORKER_OK, std::move(registrations));
 }
 
 void ServiceWorkerStorage::DidGetRegistrationsInfos(
diff --git a/content/browser/service_worker/service_worker_storage.h b/content/browser/service_worker/service_worker_storage.h
index 61ce44dc..1667b54a 100644
--- a/content/browser/service_worker/service_worker_storage.h
+++ b/content/browser/service_worker/service_worker_storage.h
@@ -57,9 +57,10 @@
  public:
   typedef std::vector<ServiceWorkerDatabase::ResourceRecord> ResourceList;
   typedef base::Callback<void(ServiceWorkerStatusCode status)> StatusCallback;
-  typedef base::Callback<void(ServiceWorkerStatusCode status,
-                              const scoped_refptr<ServiceWorkerRegistration>&
-                                  registration)> FindRegistrationCallback;
+  typedef base::Callback<void(
+      ServiceWorkerStatusCode status,
+      scoped_refptr<ServiceWorkerRegistration> registration)>
+      FindRegistrationCallback;
   typedef base::Callback<void(
       ServiceWorkerStatusCode status,
       const std::vector<scoped_refptr<ServiceWorkerRegistration>>&
diff --git a/content/browser/service_worker/service_worker_storage_unittest.cc b/content/browser/service_worker/service_worker_storage_unittest.cc
index 478ace7..fe64a67 100644
--- a/content/browser/service_worker/service_worker_storage_unittest.cc
+++ b/content/browser/service_worker/service_worker_storage_unittest.cc
@@ -62,15 +62,14 @@
   return base::Bind(&StatusCallback, was_called, result);
 }
 
-void FindCallback(
-    bool* was_called,
-    ServiceWorkerStatusCode* result,
-    scoped_refptr<ServiceWorkerRegistration>* found,
-    ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+void FindCallback(bool* was_called,
+                  ServiceWorkerStatusCode* result,
+                  scoped_refptr<ServiceWorkerRegistration>* found,
+                  ServiceWorkerStatusCode status,
+                  scoped_refptr<ServiceWorkerRegistration> registration) {
   *was_called = true;
   *result = status;
-  *found = registration;
+  *found = std::move(registration);
 }
 
 ServiceWorkerStorage::FindRegistrationCallback MakeFindCallback(
@@ -434,7 +433,7 @@
   }
 
   ServiceWorkerStatusCode UpdateToActiveState(
-      const scoped_refptr<ServiceWorkerRegistration>& registration) {
+      scoped_refptr<ServiceWorkerRegistration> registration) {
     bool was_called = false;
     ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_MAX_VALUE;
     storage()->UpdateToActiveState(registration.get(),
@@ -446,7 +445,7 @@
   }
 
   void UpdateLastUpdateCheckTime(
-      const scoped_refptr<ServiceWorkerRegistration>& registration) {
+      scoped_refptr<ServiceWorkerRegistration> registration) {
     storage()->UpdateLastUpdateCheckTime(registration.get());
     base::RunLoop().RunUntilIdle();
   }
diff --git a/content/browser/service_worker/service_worker_unregister_job.cc b/content/browser/service_worker/service_worker_unregister_job.cc
index c0fc9dc4..0ca2f7f0 100644
--- a/content/browser/service_worker/service_worker_unregister_job.cc
+++ b/content/browser/service_worker/service_worker_unregister_job.cc
@@ -57,7 +57,7 @@
 
 void ServiceWorkerUnregisterJob::OnRegistrationFound(
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   if (status == SERVICE_WORKER_ERROR_NOT_FOUND) {
     DCHECK(!registration.get());
     Complete(kInvalidServiceWorkerRegistrationId,
diff --git a/content/browser/service_worker/service_worker_unregister_job.h b/content/browser/service_worker/service_worker_unregister_job.h
index a614253..517bc27 100644
--- a/content/browser/service_worker/service_worker_unregister_job.h
+++ b/content/browser/service_worker/service_worker_unregister_job.h
@@ -51,7 +51,7 @@
  private:
   void OnRegistrationFound(
       ServiceWorkerStatusCode status,
-      const scoped_refptr<ServiceWorkerRegistration>& registration);
+      scoped_refptr<ServiceWorkerRegistration> registration);
   void Complete(int64_t registration_id, ServiceWorkerStatusCode status);
   void CompleteInternal(int64_t registration_id,
                         ServiceWorkerStatusCode status);
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index ed7ae82ea..c9f14e5 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -1284,7 +1284,7 @@
     bool is_browser_startup_complete,
     const StatusCallback& callback,
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   scoped_refptr<ServiceWorkerRegistration> protect = registration;
   if (status == SERVICE_WORKER_ERROR_NOT_FOUND) {
     // When the registration has already been deleted from the storage but its
@@ -1654,7 +1654,7 @@
 
 void ServiceWorkerVersion::FoundRegistrationForUpdate(
     ServiceWorkerStatusCode status,
-    const scoped_refptr<ServiceWorkerRegistration>& registration) {
+    scoped_refptr<ServiceWorkerRegistration> registration) {
   if (!context_)
     return;
 
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h
index 417b301e..db5392c1 100644
--- a/content/browser/service_worker/service_worker_version.h
+++ b/content/browser/service_worker/service_worker_version.h
@@ -591,7 +591,7 @@
       bool is_browser_startup_complete,
       const StatusCallback& callback,
       ServiceWorkerStatusCode status,
-      const scoped_refptr<ServiceWorkerRegistration>& registration);
+      scoped_refptr<ServiceWorkerRegistration> registration);
   void StartWorkerInternal();
 
   void DidSkipWaiting(int request_id);
@@ -639,7 +639,7 @@
 
   void FoundRegistrationForUpdate(
       ServiceWorkerStatusCode status,
-      const scoped_refptr<ServiceWorkerRegistration>& registration);
+      scoped_refptr<ServiceWorkerRegistration> registration);
 
   void OnStoppedInternal(EmbeddedWorkerStatus old_status);
 
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 9f899f0d..ea27333 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -782,6 +782,7 @@
     # WebRTC-specific sources. Put WebRTC plugin-related stuff further below.
     'content_unittests_webrtc_sources': [
       'browser/media/webrtc/webrtc_internals_unittest.cc',
+      'browser/media/webrtc/webrtc_internals_message_handler_unittest.cc',
       'browser/media/webrtc/webrtc_eventlog_host_unittest.cc',
       'browser/renderer_host/p2p/socket_host_tcp_server_unittest.cc',
       'browser/renderer_host/p2p/socket_host_tcp_unittest.cc',
diff --git a/extensions/browser/api/cast_channel/cast_auth_util.cc b/extensions/browser/api/cast_channel/cast_auth_util.cc
index 451058b..0e5c388 100644
--- a/extensions/browser/api/cast_channel/cast_auth_util.cc
+++ b/extensions/browser/api/cast_channel/cast_auth_util.cc
@@ -148,12 +148,13 @@
                     response.intermediate_certificate().end());
 
   // Use the current time when checking certificate validity.
-  base::Time::Exploded now;
-  base::Time::Now().UTCExplode(&now);
+  base::Time now = base::Time::Now();
 
+  // CRL should not be enforced until it is served.
   cast_crypto::CastDeviceCertPolicy device_policy;
-  if (!cast_crypto::VerifyDeviceCert(cert_chain, now, &verification_context,
-                                     &device_policy)) {
+  if (!cast_crypto::VerifyDeviceCert(
+          cert_chain, now, &verification_context, &device_policy, nullptr,
+          cast_certificate::CRLPolicy::CRL_OPTIONAL)) {
     // TODO(eroman): The error information was lost; this error is ambiguous.
     return AuthResult("Failed verifying cast device certificate",
                       AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA);
diff --git a/gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.cc b/gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.cc
index 500dd09..b24c50e5 100644
--- a/gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.cc
+++ b/gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.cc
@@ -1503,12 +1503,11 @@
         uint packedEdgesC = packedEdgesArray[(1 + _x) * 4 + (1 + _y)];
 
         uvec4 edges = UnpackEdge(packedEdgesC);
-        vec4 edgesFlt = vec4(edges);
-
-        float numberOfEdges = dot(edgesFlt, vec4(1, 1, 1, 1));
-        if (numberOfEdges <= 1.0)
+        uint numberOfEdges = edges.x + edges.y + edges.z + edges.w;
+        if (numberOfEdges <= 1u)
           continue;
 
+        vec4 edgesFlt = vec4(edges);
         float fromRight = edgesFlt.r;
         float fromAbove = edgesFlt.g;
         float fromLeft = edgesFlt.b;
@@ -1531,7 +1530,7 @@
         // the current pixel's colour in return.
         // However, in the case when this is an actual corner, the pixel's
         // colour will be partially overwritten by it's 2 neighbours.
-        if( numberOfEdges == 2.0 )
+        if (numberOfEdges == 2u)
         {
           // with value of 0.15, the pixel will retain approx 77% of its
           // colour and the remaining 23% will come from its 2 neighbours
@@ -1602,7 +1601,7 @@
         }
 
         // 2.) U-like shape (surrounded with edges from 3 sides)
-        if (numberOfEdges == 3.0) {
+        if (numberOfEdges == 3u) {
           // with value of 0.13, the pixel will retain approx 72% of its
           // colour and the remaining 28% will be picked from its 3
           // neighbours (which are unlikely to be blurred too but could be)
@@ -1610,7 +1609,7 @@
         }
 
         // 3.) Completely surrounded with edges from all 4 sides
-        if (numberOfEdges == 4.0) {
+        if (numberOfEdges == 4u) {
           // with value of 0.07, the pixel will retain 78% of its colour
           // and the remaining 22% will come from its 4 neighbours (which
           // are unlikely to be blurred)
@@ -1670,11 +1669,11 @@
 
     \n#ifdef DEBUG_OUTPUT_AAINFO\n
         imageStore(g_resultTextureSlot2, screenPosI.xy,
-                   PackBlurAAInfo(screenPosI.xy, uint(numberOfEdges)));
+                   PackBlurAAInfo(screenPosI.xy, numberOfEdges));
     \n#endif\n
         imageStore(g_resultTextureFlt4Slot1, screenPosI.xy, color);
 
-        if (numberOfEdges == 2.0) {
+        if (numberOfEdges == 2u) {
           uint packedEdgesL = packedEdgesArray[(0 + _x) * 4 + (1 + _y)];
           uint packedEdgesB = packedEdgesArray[(1 + _x) * 4 + (0 + _y)];
           uint packedEdgesR = packedEdgesArray[(2 + _x) * 4 + (1 + _y)];
diff --git a/ios/chrome/BUILD.gn b/ios/chrome/BUILD.gn
index 7cbf6d6a..73ab352 100644
--- a/ios/chrome/BUILD.gn
+++ b/ios/chrome/BUILD.gn
@@ -41,6 +41,7 @@
     "browser/geolocation/omnibox_geolocation_local_state_unittest.mm",
     "browser/install_time_util_unittest.mm",
     "browser/installation_notifier_unittest.mm",
+    "browser/itunes_links/itunes_links_observer_unittest.mm",
     "browser/metrics/ios_chrome_metrics_service_accessor_unittest.cc",
     "browser/metrics/ios_chrome_stability_metrics_provider_unittest.cc",
     "browser/metrics/mobile_session_shutdown_metrics_provider_unittest.mm",
diff --git a/ios/chrome/browser/BUILD.gn b/ios/chrome/browser/BUILD.gn
index d02ce56..c8b710c 100644
--- a/ios/chrome/browser/BUILD.gn
+++ b/ios/chrome/browser/BUILD.gn
@@ -225,6 +225,8 @@
     "ios_chrome_io_thread.mm",
     "ios_chrome_main_parts.h",
     "ios_chrome_main_parts.mm",
+    "itunes_links/itunes_links_observer.h",
+    "itunes_links/itunes_links_observer.mm",
     "memory/memory_debugger.h",
     "memory/memory_debugger.mm",
     "memory/memory_debugger_manager.h",
@@ -419,6 +421,7 @@
     "ssl/ios_ssl_blocking_page.mm",
     "ssl/ios_ssl_error_handler.h",
     "ssl/ios_ssl_error_handler.mm",
+    "storekit_launcher.h",
     "suggestions/image_fetcher_impl.h",
     "suggestions/image_fetcher_impl.mm",
     "suggestions/ios_image_decoder_impl.h",
diff --git a/ios/chrome/browser/itunes_links/OWNERS b/ios/chrome/browser/itunes_links/OWNERS
new file mode 100644
index 0000000..4d9d202
--- /dev/null
+++ b/ios/chrome/browser/itunes_links/OWNERS
@@ -0,0 +1,3 @@
+jif@google.com
+
+per-file *_kiftest.*=baxley@google.com
diff --git a/ios/chrome/browser/itunes_links/itunes_links_observer.h b/ios/chrome/browser/itunes_links/itunes_links_observer.h
new file mode 100644
index 0000000..4cf8ea0b
--- /dev/null
+++ b/ios/chrome/browser/itunes_links/itunes_links_observer.h
@@ -0,0 +1,36 @@
+// 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 IOS_CHROME_BROWSER_ITUNES_LINKS_ITUNES_LINKS_OBSERVER_H_
+#define IOS_CHROME_BROWSER_ITUNES_LINKS_ITUNES_LINKS_OBSERVER_H_
+
+#import <Foundation/Foundation.h>
+
+#import "ios/web/public/web_state/web_state_observer_bridge.h"
+
+namespace web {
+class WebState;
+};
+
+@protocol StoreKitLauncher;
+
+// Instances of this class observe navigation events. If a page concerning a
+// product on the iTunes App Store is loaded, this class will trigger the
+// opening of a SKStoreProductViewController presenting the information of the
+// said product.
+// The goal of this class is to workaround a bug where Apple serves the wrong
+// content for itunes.apple.com pages, see http://crbug.com/623016.
+@interface ITunesLinksObserver : NSObject<CRWWebStateObserver>
+- (instancetype)initWithWebState:(web::WebState*)webState
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+// Sets the object responsible for showing the App Store product page.
+// |storeKitLauncher| can be nil.
+- (void)setStoreKitLauncher:(id<StoreKitLauncher>)storeKitLauncher;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_ITUNES_LINKS_ITUNES_LINKS_OBSERVER_H_
diff --git a/ios/chrome/browser/itunes_links/itunes_links_observer.mm b/ios/chrome/browser/itunes_links/itunes_links_observer.mm
new file mode 100644
index 0000000..c881fd69
--- /dev/null
+++ b/ios/chrome/browser/itunes_links/itunes_links_observer.mm
@@ -0,0 +1,70 @@
+// 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.
+
+#import "ios/chrome/browser/itunes_links/itunes_links_observer.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#import "base/mac/scoped_nsobject.h"
+#include "base/strings/sys_string_conversions.h"
+#import "ios/chrome/browser/storekit_launcher.h"
+#import "ios/web/public/web_state/web_state_observer_bridge.h"
+#include "ios/web/public/web_state/web_state.h"
+#include "url/gurl.h"
+
+@interface ITunesLinksObserver ()
+
+// If |URL| points to a product on itunes.apple.com, returns the product ID.
+// Otherwise, returns nil.
+// Examples of URLs pointing to products on itunes.apple.com can be found in
+// itunes_links_observer_unittest.mm.
++ (NSString*)productIDFromURL:(const GURL&)URL;
+
+@end
+
+@implementation ITunesLinksObserver {
+  base::WeakNSProtocol<id<StoreKitLauncher>> _storeKitLauncher;
+  std::unique_ptr<web::WebStateObserverBridge> _webStateObserverBridge;
+}
+
+- (instancetype)initWithWebState:(web::WebState*)webState {
+  self = [super init];
+  if (self) {
+    _webStateObserverBridge.reset(
+        new web::WebStateObserverBridge(webState, self));
+  }
+  return self;
+}
+
+- (instancetype)init {
+  NOTREACHED();
+  return nil;
+}
+
++ (NSString*)productIDFromURL:(const GURL&)URL {
+  if (!URL.SchemeIsHTTPOrHTTPS() || !URL.DomainIs("itunes.apple.com"))
+    return nil;
+  std::string fileName = URL.ExtractFileName();
+  // The first 2 characters must be "id", followed by the app ID.
+  if (fileName.length() < 3 || fileName.substr(0, 2) != "id")
+    return nil;
+  std::string productID = fileName.substr(2);
+  return base::SysUTF8ToNSString(productID);
+}
+
+#pragma mark - CRWWebStateObserver
+
+- (void)webStateDidLoadPage:(web::WebState*)webState {
+  GURL URL = webState->GetLastCommittedURL();
+  NSString* productID = [ITunesLinksObserver productIDFromURL:URL];
+  if (productID)
+    [_storeKitLauncher openAppStore:productID];
+}
+
+- (void)setStoreKitLauncher:(id<StoreKitLauncher>)storeKitLauncher {
+  _storeKitLauncher.reset(storeKitLauncher);
+}
+
+@end
diff --git a/ios/chrome/browser/itunes_links/itunes_links_observer_unittest.mm b/ios/chrome/browser/itunes_links/itunes_links_observer_unittest.mm
new file mode 100644
index 0000000..a726370
--- /dev/null
+++ b/ios/chrome/browser/itunes_links/itunes_links_observer_unittest.mm
@@ -0,0 +1,73 @@
+// 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.
+
+#import <Foundation/Foundation.h>
+
+#import "ios/chrome/browser/itunes_links/itunes_links_observer.h"
+
+#import "base/mac/scoped_nsobject.h"
+#import "ios/web/public/test/test_web_state.h"
+#import "ios/chrome/browser/storekit_launcher.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+#import "third_party/ocmock/gtest_support.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#include "url/gurl.h"
+
+namespace {
+
+class ITunesLinksObserverTest : public PlatformTest {
+ protected:
+  void SetUp() override {
+    mocked_store_kit_launcher_.reset(
+        [[OCMockObject mockForProtocol:@protocol(StoreKitLauncher)] retain]);
+    link_observer_.reset(
+        [[ITunesLinksObserver alloc] initWithWebState:&web_state_]);
+    [link_observer_ setStoreKitLauncher:mocked_store_kit_launcher_];
+  }
+  void VerifyOpeningOfAppStore(NSString* expected_product_id,
+                               std::string const& url_string) {
+    if (expected_product_id)
+      [[mocked_store_kit_launcher_.get() expect]
+          openAppStore:expected_product_id];
+    web_state_.SetCurrentURL(GURL(url_string));
+    [link_observer_ webStateDidLoadPage:&web_state_];
+    EXPECT_OCMOCK_VERIFY(mocked_store_kit_launcher_.get());
+  }
+  web::TestWebState web_state_;
+  base::scoped_nsobject<id> mocked_store_kit_launcher_;
+  // |link_observer_| must be destroyed before web_state_.
+  base::scoped_nsobject<ITunesLinksObserver> link_observer_;
+};
+
+// Verifies that navigating to URLs not concerning a product hosted on the
+// iTunes AppStore does not trigger the opening of the AppStore.
+TEST_F(ITunesLinksObserverTest, NonMatchingUrls) {
+  VerifyOpeningOfAppStore(nil, "");
+  VerifyOpeningOfAppStore(nil, "foobar");
+  VerifyOpeningOfAppStore(nil, "foo://bar");
+  VerifyOpeningOfAppStore(nil, "http://foo");
+  VerifyOpeningOfAppStore(nil, "http://foo?bar#qux");
+  VerifyOpeningOfAppStore(nil, "http://foo.bar/qux");
+  VerifyOpeningOfAppStore(nil, "http://itunes.apple.com");
+  VerifyOpeningOfAppStore(nil, "http://itunes.apple.com/id");
+  VerifyOpeningOfAppStore(nil, "http://itunes.apple.com/12345");
+  VerifyOpeningOfAppStore(nil, "ftp://itunes.apple.com/id123");
+}
+
+// Verifies that navigating to URLs concerning a product hosted on iTunes
+// AppStore triggers the opening of the AppStore.
+TEST_F(ITunesLinksObserverTest, MatchingUrls) {
+  VerifyOpeningOfAppStore(@"123", "http://itunes.apple.com/id123");
+  VerifyOpeningOfAppStore(@"123", "https://itunes.apple.com/id123");
+  VerifyOpeningOfAppStore(@"123", "http://itunes.apple.com/bar/id123");
+  VerifyOpeningOfAppStore(@"123", "http://itunes.apple.com/bar/id123?qux");
+  VerifyOpeningOfAppStore(@"123", "http://itunes.apple.com/bar/id123?qux&baz");
+  VerifyOpeningOfAppStore(@"123",
+                          "http://itunes.apple.com/bar/id123?qux&baz#foo");
+  VerifyOpeningOfAppStore(@"123",
+                          "http://foo.itunes.apple.com/bar/id123?qux&baz#foo");
+}
+
+}  // namespace
diff --git a/ios/chrome/browser/storekit_launcher.h b/ios/chrome/browser/storekit_launcher.h
new file mode 100644
index 0000000..2e416727
--- /dev/null
+++ b/ios/chrome/browser/storekit_launcher.h
@@ -0,0 +1,17 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_STOREKIT_LAUNCHER_H_
+#define IOS_CHROME_BROWSER_STOREKIT_LAUNCHER_H_
+
+// Protocol to be implemented by a class that provides an access to the app
+// store with StoreKit.
+@protocol StoreKitLauncher
+
+// Opens StoreKit modal to present a product identified with |productID|.
+- (void)openAppStore:(NSString*)productID;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_STOREKIT_LAUNCHER_H_
diff --git a/ios/chrome/ios_chrome.gyp b/ios/chrome/ios_chrome.gyp
index decca74e1..6a0fed40 100644
--- a/ios/chrome/ios_chrome.gyp
+++ b/ios/chrome/ios_chrome.gyp
@@ -100,6 +100,7 @@
         '../../components/components.gyp:signin_core_browser',
         '../../components/components.gyp:signin_core_common',
         '../../components/components.gyp:signin_ios_browser',
+        '../../components/components.gyp:spellcheck_common',
         '../../components/components.gyp:ssl_config',
         '../../components/components.gyp:suggestions',
         '../../components/components.gyp:sync_driver',
@@ -359,6 +360,8 @@
         'browser/ios_chrome_io_thread.mm',
         'browser/ios_chrome_main_parts.h',
         'browser/ios_chrome_main_parts.mm',
+        'browser/itunes_links/itunes_links_observer.h',
+        'browser/itunes_links/itunes_links_observer.mm',
         'browser/memory/memory_debugger.h',
         'browser/memory/memory_debugger.mm',
         'browser/memory/memory_debugger_manager.h',
@@ -553,6 +556,7 @@
         'browser/ssl/ios_ssl_blocking_page.mm',
         'browser/ssl/ios_ssl_error_handler.h',
         'browser/ssl/ios_ssl_error_handler.mm',
+        'browser/storekit_launcher.h',
         'browser/suggestions/image_fetcher_impl.h',
         'browser/suggestions/image_fetcher_impl.mm',
         'browser/suggestions/ios_image_decoder_impl.h',
diff --git a/ios/chrome/ios_chrome_tests.gyp b/ios/chrome/ios_chrome_tests.gyp
index 0cf732df..6b10800 100644
--- a/ios/chrome/ios_chrome_tests.gyp
+++ b/ios/chrome/ios_chrome_tests.gyp
@@ -56,6 +56,7 @@
         'browser/geolocation/omnibox_geolocation_local_state_unittest.mm',
         'browser/install_time_util_unittest.mm',
         'browser/installation_notifier_unittest.mm',
+        'browser/itunes_links/itunes_links_observer_unittest.mm',
         'browser/metrics/ios_chrome_metrics_service_accessor_unittest.cc',
         'browser/metrics/ios_chrome_stability_metrics_provider_unittest.cc',
         'browser/metrics/mobile_session_shutdown_metrics_provider_unittest.mm',
diff --git a/ipc/ipc_channel_mojo_unittest.cc b/ipc/ipc_channel_mojo_unittest.cc
index 41950d29..de37658 100644
--- a/ipc/ipc_channel_mojo_unittest.cc
+++ b/ipc/ipc_channel_mojo_unittest.cc
@@ -226,10 +226,6 @@
  public:
   ListenerExpectingErrors() : has_error_(false) {}
 
-  void OnChannelConnected(int32_t peer_pid) override {
-    base::MessageLoop::current()->QuitWhenIdle();
-  }
-
   bool OnMessageReceived(const IPC::Message& message) override { return true; }
 
   void OnChannelError() override {
@@ -265,8 +261,7 @@
   Close();
 }
 
-// Disabled because flake. http://crbug.com/630831
-TEST_F(IPCChannelMojoTest, DISABLED_SendFailWithPendingMessages) {
+TEST_F(IPCChannelMojoTest, SendFailWithPendingMessages) {
   InitWithMojo("IPCChannelMojoErraticTestClient");
 
   // Set up IPC channel and start client.
diff --git a/ipc/ipc_mojo_bootstrap.cc b/ipc/ipc_mojo_bootstrap.cc
index a21720b..21e7ca3 100644
--- a/ipc/ipc_mojo_bootstrap.cc
+++ b/ipc/ipc_mojo_bootstrap.cc
@@ -11,6 +11,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/atomicops.h"
 #include "base/callback.h"
 #include "base/logging.h"
 #include "base/macros.h"
@@ -65,6 +66,8 @@
     DCHECK(thread_checker_.CalledOnValidThread());
     DCHECK(task_runner_->BelongsToCurrentThread());
     thread_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+    base::subtle::Release_Store(&is_thread_task_runner_set_, 1);
+
     connector_.reset(new mojo::Connector(
         std::move(handle), mojo::Connector::SINGLE_THREADED_SEND,
         task_runner_));
@@ -359,8 +362,11 @@
     // BelongsToCurrentThread() == false during shutdown. By the time shutdown
     // occurs, |thread_task_runner_| will be non-null and is guaranteed to run
     // tasks on the same thread as |task_runner_|.
-    return task_runner_->BelongsToCurrentThread() ||
-        (thread_task_runner_ && thread_task_runner_->BelongsToCurrentThread());
+    base::subtle::Atomic32 has_thread_task_runner =
+        base::subtle::Acquire_Load(&is_thread_task_runner_set_);
+    if (has_thread_task_runner)
+      return thread_task_runner_->BelongsToCurrentThread();
+    return task_runner_->BelongsToCurrentThread();
   }
 
   bool SendMessage(mojo::Message* message) {
@@ -611,6 +617,7 @@
   // MessageLoop destruction which may cause the user-provided |task_runner_| to
   // incorrectly report that BelongsToCurrentThread() == false during shutdown.
   scoped_refptr<base::SingleThreadTaskRunner> thread_task_runner_;
+  base::subtle::Atomic32 is_thread_task_runner_set_ = 0;
 
   scoped_refptr<base::SingleThreadTaskRunner> proxy_task_runner_;
   const bool set_interface_id_namespace_bit_;
diff --git a/media/gpu/v4l2_video_decode_accelerator.cc b/media/gpu/v4l2_video_decode_accelerator.cc
index 327fcf9..4f23a9eb 100644
--- a/media/gpu/v4l2_video_decode_accelerator.cc
+++ b/media/gpu/v4l2_video_decode_accelerator.cc
@@ -665,7 +665,6 @@
 
     switch (decoder_state_) {
       case kInitialized:
-      case kAfterReset:
         schedule_task = DecodeBufferInitial(data, decoded_size, &decoded_size);
         break;
       case kDecoding:
@@ -800,8 +799,7 @@
                                                      size_t* endpos) {
   DVLOGF(3) << "data=" << data << ", size=" << size;
   DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
-  DCHECK_NE(decoder_state_, kUninitialized);
-  DCHECK_NE(decoder_state_, kDecoding);
+  DCHECK_EQ(decoder_state_, kInitialized);
   // Initial decode.  We haven't been able to get output stream format info yet.
   // Get it, and start decoding.
 
@@ -834,7 +832,7 @@
   }
 
   // Run this initialization only on first startup.
-  if (decoder_state_ == kInitialized) {
+  if (output_buffer_map_.empty()) {
     DVLOGF(3) << "running initialization";
     // Success! Setup our parameters.
     if (!CreateBuffersForFormat(format, visible_size))
@@ -1336,7 +1334,7 @@
   TRACE_EVENT0("Video Decoder", "V4L2VDA::FlushTask");
 
   // Flush outstanding buffers.
-  if (decoder_state_ == kInitialized || decoder_state_ == kAfterReset) {
+  if (decoder_state_ == kInitialized) {
     // There's nothing in the pipe, so return done immediately.
     DVLOGF(3) << "returning flush";
     child_task_runner_->PostTask(FROM_HERE,
@@ -1488,13 +1486,7 @@
 
   // Jobs drained, we're finished resetting.
   DCHECK_EQ(decoder_state_, kResetting);
-  if (output_buffer_map_.empty()) {
-    // We must have gotten Reset() before we had a chance to request buffers
-    // from the client.
-    decoder_state_ = kInitialized;
-  } else {
-    decoder_state_ = kAfterReset;
-  }
+  decoder_state_ = kInitialized;
 
   decoder_partial_frame_pending_ = false;
   decoder_delay_bitstream_buffer_id_ = -1;
diff --git a/media/gpu/v4l2_video_decode_accelerator.h b/media/gpu/v4l2_video_decode_accelerator.h
index a756fb6f..f7e2003 100644
--- a/media/gpu/v4l2_video_decode_accelerator.h
+++ b/media/gpu/v4l2_video_decode_accelerator.h
@@ -133,7 +133,6 @@
     kInitialized,    // Initialize() returned true; ready to start decoding.
     kDecoding,       // DecodeBufferInitial() successful; decoding frames.
     kResetting,      // Presently resetting.
-    kAfterReset,     // After Reset(), ready to start decoding again.
     kChangingResolution,  // Performing resolution change, all remaining
                           // pre-change frames decoded and processed.
     kError,               // Error in kDecoding state.
@@ -249,8 +248,6 @@
   // the NotifyResetDone callback, then set the decoder state to kResetting so
   // that all intervening tasks will drain.
   void ResetTask();
-  // ResetDoneTask() will set the decoder state back to kAfterReset, so
-  // subsequent decoding can continue.
   void ResetDoneTask();
 
   // Device destruction task.
diff --git a/net/http/bidirectional_stream_unittest.cc b/net/http/bidirectional_stream_unittest.cc
index 28bbc30bf..3d0db380 100644
--- a/net/http/bidirectional_stream_unittest.cc
+++ b/net/http/bidirectional_stream_unittest.cc
@@ -433,7 +433,7 @@
   const char* const kExtraResponseHeaders[] = {"header-name", "header-value"};
 
   SpdySerializedFrame resp(
-      spdy_util_.ConstructSpdyGetSynReply(kExtraResponseHeaders, 1, 1));
+      spdy_util_.ConstructSpdyGetReply(kExtraResponseHeaders, 1, 1));
 
   SpdySerializedFrame body_frame(spdy_util_.ConstructSpdyDataFrame(1, false));
   // Last body frame has END_STREAM flag set.
@@ -515,7 +515,7 @@
       CreateMockWrite(req, 0), CreateMockWrite(data_frame, 3),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame response_body_frame1(
       spdy_util_.ConstructSpdyDataFrame(1, false));
   SpdySerializedFrame response_body_frame2(
@@ -660,7 +660,7 @@
       CreateMockWrite(data_frame2, 6), CreateMockWrite(data_frame3, 9),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame response_body_frame1(
       spdy_util_.ConstructSpdyDataFrame(1, false));
   SpdySerializedFrame response_body_frame2(
@@ -757,7 +757,7 @@
       CreateMockWrite(req, 0), CreateMockWrite(data_frame1, 1),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame response_body_frame1(
       spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {
@@ -858,7 +858,7 @@
 
   MockWrite writes[] = {CreateMockWrite(req, 0)};
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
 
   SpdySerializedFrame response_body_frame(
       spdy_util_.ConstructSpdyDataFrame(1, nullptr, 0, true));
@@ -918,7 +918,7 @@
   const char* const kExtraResponseHeaders[] = {"header-name", "header-value"};
 
   SpdySerializedFrame resp(
-      spdy_util_.ConstructSpdyGetSynReply(kExtraResponseHeaders, 1, 1));
+      spdy_util_.ConstructSpdyGetReply(kExtraResponseHeaders, 1, 1));
 
   SpdySerializedFrame body_frame(spdy_util_.ConstructSpdyDataFrame(1, false));
   // Last body frame has END_STREAM flag set.
@@ -999,7 +999,7 @@
   const char* const kExtraResponseHeaders[] = {"header-name", "header-value"};
 
   SpdySerializedFrame resp(
-      spdy_util_.ConstructSpdyGetSynReply(kExtraResponseHeaders, 1, 1));
+      spdy_util_.ConstructSpdyGetReply(kExtraResponseHeaders, 1, 1));
 
   SpdySerializedFrame body_frame(spdy_util_.ConstructSpdyDataFrame(1, false));
 
@@ -1077,7 +1077,7 @@
       CreateMockWrite(rst, 5),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame response_body_frame(
       spdy_util_.ConstructSpdyDataFrame(1, false));
 
@@ -1138,7 +1138,7 @@
       CreateMockWrite(req, 0), CreateMockWrite(rst, 4),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame response_body_frame(
       spdy_util_.ConstructSpdyDataFrame(1, false));
 
@@ -1197,7 +1197,7 @@
 
   const char* const kExtraHeaders[] = {"X-UpperCase", "yes"};
   SpdySerializedFrame resp(
-      spdy_util_.ConstructSpdyGetSynReply(kExtraHeaders, 1, 1));
+      spdy_util_.ConstructSpdyGetReply(kExtraHeaders, 1, 1));
 
   MockRead reads[] = {
       CreateMockRead(resp, 1), MockRead(ASYNC, 0, 3),
@@ -1267,7 +1267,7 @@
   const char* const kExtraResponseHeaders[] = {"header-name", "header-value"};
 
   SpdySerializedFrame resp(
-      spdy_util_.ConstructSpdyGetSynReply(kExtraResponseHeaders, 1, 1));
+      spdy_util_.ConstructSpdyGetReply(kExtraResponseHeaders, 1, 1));
 
   MockRead reads[] = {
       CreateMockRead(resp, 1), MockRead(ASYNC, 0, 3),
@@ -1319,7 +1319,7 @@
   const char* const kExtraResponseHeaders[] = {"header-name", "header-value"};
 
   SpdySerializedFrame resp(
-      spdy_util_.ConstructSpdyGetSynReply(kExtraResponseHeaders, 1, 1));
+      spdy_util_.ConstructSpdyGetReply(kExtraResponseHeaders, 1, 1));
 
   SpdySerializedFrame response_body_frame(
       spdy_util_.ConstructSpdyDataFrame(1, false));
@@ -1374,7 +1374,7 @@
   const char* const kExtraResponseHeaders[] = {"header-name", "header-value"};
 
   SpdySerializedFrame resp(
-      spdy_util_.ConstructSpdyGetSynReply(kExtraResponseHeaders, 1, 1));
+      spdy_util_.ConstructSpdyGetReply(kExtraResponseHeaders, 1, 1));
 
   SpdySerializedFrame response_body_frame(
       spdy_util_.ConstructSpdyDataFrame(1, false));
@@ -1436,7 +1436,7 @@
 
   const char* const kExtraHeaders[] = {"X-UpperCase", "yes"};
   SpdySerializedFrame resp(
-      spdy_util_.ConstructSpdyGetSynReply(kExtraHeaders, 1, 1));
+      spdy_util_.ConstructSpdyGetReply(kExtraHeaders, 1, 1));
 
   MockRead reads[] = {
       CreateMockRead(resp, 1), MockRead(ASYNC, 0, 3),
@@ -1488,7 +1488,7 @@
                                                alt_svc_header_value.c_str()};
 
   SpdySerializedFrame resp(
-      spdy_util_.ConstructSpdyGetSynReply(kExtraResponseHeaders, 1, 1));
+      spdy_util_.ConstructSpdyGetReply(kExtraResponseHeaders, 1, 1));
   SpdySerializedFrame body_frame(spdy_util_.ConstructSpdyDataFrame(1, true));
 
   MockRead reads[] = {
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index dc1317f..12dae41 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -1392,7 +1392,7 @@
   SpdySerializedFrame spdy_request(spdy_util_.ConstructSpdyGet(
       request.url.spec().c_str(), 1, DEFAULT_PRIORITY));
   SpdySerializedFrame spdy_response(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame spdy_data(
       spdy_util_.ConstructSpdyDataFrame(1, "hello", 5, true));
 
@@ -4365,7 +4365,7 @@
       spdy_util_.ConstructSpdyGet("http://www.example.org/", 1, LOWEST));
   MockWrite spdy_writes[] = {CreateMockWrite(req, 0)};
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame data(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead spdy_reads[] = {
       CreateMockRead(resp, 1), CreateMockRead(data, 2), MockRead(ASYNC, 0, 3),
@@ -4425,7 +4425,7 @@
       spdy_util_.ConstructSpdyGet("http://www.example.org/", 1, LOWEST));
   MockWrite spdy_writes[] = {CreateMockWrite(req, 0)};
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame data(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead spdy_reads[] = {
       CreateMockRead(resp, 1), CreateMockRead(data, 2), MockRead(ASYNC, 0, 3),
@@ -4511,13 +4511,12 @@
   const char* const kExtraAuthenticationHeaders[] = {
     "proxy-authenticate", "Basic realm=\"MyRealm1\""
   };
-  SpdySerializedFrame resp_authentication(spdy_util_.ConstructSpdySynReplyError(
+  SpdySerializedFrame resp_authentication(spdy_util_.ConstructSpdyReplyError(
       "407 Proxy Authentication Required", kExtraAuthenticationHeaders,
       arraysize(kExtraAuthenticationHeaders) / 2, 1));
   SpdySerializedFrame body_authentication(
       spdy_util_.ConstructSpdyDataFrame(1, true));
-  SpdySerializedFrame resp_data(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+  SpdySerializedFrame resp_data(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
   SpdySerializedFrame body_data(spdy_util_.ConstructSpdyDataFrame(3, true));
   MockRead spdy_reads[] = {
       CreateMockRead(resp_authentication, 1),
@@ -4599,8 +4598,7 @@
       "Connection: keep-alive\r\n\r\n";
   SpdySerializedFrame wrapped_get(
       spdy_util_.ConstructSpdyDataFrame(1, get, strlen(get), false));
-  SpdySerializedFrame conn_resp(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame conn_resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   const char resp[] = "HTTP/1.1 200 OK\r\n"
       "Content-Length: 10\r\n\r\n";
   SpdySerializedFrame wrapped_get_resp(
@@ -4681,10 +4679,9 @@
   SpdySerializedFrame get(
       spdy_util_wrapped.ConstructSpdyGet(kMyUrl, 1, LOWEST));
   SpdySerializedFrame wrapped_get(spdy_util_.ConstructWrappedSpdyFrame(get, 1));
-  SpdySerializedFrame conn_resp(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame conn_resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame get_resp(
-      spdy_util_wrapped.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_wrapped.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame wrapped_get_resp(
       spdy_util_.ConstructWrappedSpdyFrame(get_resp, 1));
   SpdySerializedFrame body(spdy_util_wrapped.ConstructSpdyDataFrame(1, true));
@@ -4772,7 +4769,7 @@
       CreateMockWrite(connect, 0), CreateMockWrite(get, 2),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdySynReplyError(1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyReplyError(1));
   SpdySerializedFrame data(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead spdy_reads[] = {
       CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, 0, 3),
@@ -4824,8 +4821,7 @@
   // CONNECT to www.example.org:443 via SPDY.
   SpdySerializedFrame connect1(spdy_util_.ConstructSpdyConnect(
       NULL, 0, 1, LOWEST, HostPortPair("www.example.org", 443)));
-  SpdySerializedFrame conn_resp1(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame conn_resp1(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
 
   // Fetch https://www.example.org/ via HTTP.
   const char get1[] =
@@ -4847,11 +4843,10 @@
   SpdyHeaderBlock connect2_block;
   connect2_block[spdy_util_.GetMethodKey()] = "CONNECT";
   connect2_block[spdy_util_.GetHostKey()] = "mail.example.org:443";
-  SpdySerializedFrame connect2(
-      spdy_util_.ConstructSpdySyn(3, std::move(connect2_block), LOWEST, false));
+  SpdySerializedFrame connect2(spdy_util_.ConstructSpdyHeaders(
+      3, std::move(connect2_block), LOWEST, false));
 
-  SpdySerializedFrame conn_resp2(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+  SpdySerializedFrame conn_resp2(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
 
   // Fetch https://mail.example.org/ via HTTP.
   const char get2[] =
@@ -4958,8 +4953,7 @@
   // CONNECT to www.example.org:443 via SPDY.
   SpdySerializedFrame connect1(spdy_util_.ConstructSpdyConnect(
       NULL, 0, 1, LOWEST, HostPortPair("www.example.org", 443)));
-  SpdySerializedFrame conn_resp1(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame conn_resp1(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
 
   // Fetch https://www.example.org/ via HTTP.
   const char get1[] =
@@ -5081,9 +5075,8 @@
   SpdyHeaderBlock headers(
       spdy_util_.ConstructGetHeaderBlockForProxy("http://www.example.org/"));
   SpdySerializedFrame get1(
-      spdy_util_.ConstructSpdySyn(1, std::move(headers), LOWEST, true));
-  SpdySerializedFrame get_resp1(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true));
+  SpdySerializedFrame get_resp1(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, "1", 1, true));
   spdy_util_.UpdateWithStreamDestruction(1);
 
@@ -5091,9 +5084,8 @@
   SpdyHeaderBlock headers2(
       spdy_util_.ConstructGetHeaderBlockForProxy("http://mail.example.org/"));
   SpdySerializedFrame get2(
-      spdy_util_.ConstructSpdySyn(3, std::move(headers2), LOWEST, true));
-  SpdySerializedFrame get_resp2(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+      spdy_util_.ConstructSpdyHeaders(3, std::move(headers2), LOWEST, true));
+  SpdySerializedFrame get_resp2(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
   SpdySerializedFrame body2(
       spdy_util_.ConstructSpdyDataFrame(3, "22", 2, true));
 
@@ -7672,7 +7664,7 @@
     "location",
     "http://login.example.com/",
   };
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdySynReplyError(
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyReplyError(
       "302 Redirect", kExtraHeaders, arraysize(kExtraHeaders) / 2, 1));
   MockRead data_reads[] = {
       CreateMockRead(resp, 1), MockRead(ASYNC, 0, 3),  // EOF
@@ -7772,7 +7764,7 @@
     "location",
     "http://login.example.com/",
   };
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdySynReplyError(
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyReplyError(
       "404 Not Found", kExtraHeaders, arraysize(kExtraHeaders) / 2, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(
       1, "The host does not exist", 23, true));
@@ -7854,11 +7846,10 @@
   const char* const kAuthChallenge[] = {
     "proxy-authenticate", "Basic realm=\"MyRealm1\"",
   };
-  SpdySerializedFrame conn_auth_resp(spdy_util_.ConstructSpdySynReplyError(
+  SpdySerializedFrame conn_auth_resp(spdy_util_.ConstructSpdyReplyError(
       kAuthStatus, kAuthChallenge, arraysize(kAuthChallenge) / 2, 1));
 
-  SpdySerializedFrame conn_resp(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+  SpdySerializedFrame conn_resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
   const char resp[] = "HTTP/1.1 200 OK\r\n"
       "Content-Length: 5\r\n\r\n";
 
@@ -7975,7 +7966,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
 
   SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true));
 
@@ -8089,7 +8080,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
 
   SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true));
 
@@ -8167,7 +8158,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
 
   SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush(
       nullptr, 0, 2, 1, "https://myproxy:70/foo.dat"));
@@ -8175,7 +8166,7 @@
   SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true));
 
   SpdySerializedFrame stream2_reply(
-      spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
 
   SpdySerializedFrame stream2_body(spdy_util_.ConstructSpdyDataFrame(1, true));
 
@@ -10741,7 +10732,7 @@
       spdy_util_.ConstructSpdyGet("https://www.example.org/", 1, LOWEST));
   MockWrite spdy_writes[] = {CreateMockWrite(req, 0)};
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame data(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead spdy_reads[] = {
       CreateMockRead(resp, 1), CreateMockRead(data, 2), MockRead(ASYNC, 0, 3),
@@ -10839,9 +10830,9 @@
   MockWrite spdy_writes[] = {
       CreateMockWrite(req1, 0), CreateMockWrite(req2, 1),
   };
-  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame data1(spdy_util_.ConstructSpdyDataFrame(1, true));
-  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
   SpdySerializedFrame data2(spdy_util_.ConstructSpdyDataFrame(3, true));
   MockRead spdy_reads[] = {
       CreateMockRead(resp1, 2), CreateMockRead(data1, 3),
@@ -11089,7 +11080,7 @@
 
   const char kCONNECTResponse[] = "HTTP/1.1 200 Connected\r\n\r\n";
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame data(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead spdy_reads[] = {
       MockRead(ASYNC, 1, kCONNECTResponse), CreateMockRead(resp, 3),
@@ -11179,7 +11170,7 @@
       spdy_util_.ConstructSpdyGet("https://www.example.org/", 1, LOWEST));
   MockWrite spdy_writes[] = {CreateMockWrite(req, 0)};
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame data(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead spdy_reads[] = {
       CreateMockRead(resp, 1), CreateMockRead(data, 2), MockRead(ASYNC, 0, 3),
@@ -12332,7 +12323,7 @@
       spdy_util_.ConstructSpdyGet("https://www.example.org", 1, LOWEST));
   MockWrite spdy_writes[] = {CreateMockWrite(req, 0)};
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame data(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead spdy_reads[] = {
       CreateMockRead(resp, 1), CreateMockRead(data, 2), MockRead(ASYNC, 0, 3),
@@ -12754,12 +12745,10 @@
   MockWrite spdy_writes[] = {
       CreateMockWrite(host1_req, 0), CreateMockWrite(host2_req, 3),
   };
-  SpdySerializedFrame host1_resp(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame host1_resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame host1_resp_body(
       spdy_util_.ConstructSpdyDataFrame(1, true));
-  SpdySerializedFrame host2_resp(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+  SpdySerializedFrame host2_resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
   SpdySerializedFrame host2_resp_body(
       spdy_util_.ConstructSpdyDataFrame(3, true));
   MockRead spdy_reads[] = {
@@ -12847,12 +12836,10 @@
   MockWrite spdy_writes[] = {
       CreateMockWrite(host1_req, 0), CreateMockWrite(host2_req, 3),
   };
-  SpdySerializedFrame host1_resp(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame host1_resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame host1_resp_body(
       spdy_util_.ConstructSpdyDataFrame(1, true));
-  SpdySerializedFrame host2_resp(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+  SpdySerializedFrame host2_resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
   SpdySerializedFrame host2_resp_body(
       spdy_util_.ConstructSpdyDataFrame(3, true));
   MockRead spdy_reads[] = {
@@ -12971,12 +12958,10 @@
   MockWrite spdy_writes[] = {
       CreateMockWrite(host1_req, 0), CreateMockWrite(host2_req, 3),
   };
-  SpdySerializedFrame host1_resp(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame host1_resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame host1_resp_body(
       spdy_util_.ConstructSpdyDataFrame(1, true));
-  SpdySerializedFrame host2_resp(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+  SpdySerializedFrame host2_resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
   SpdySerializedFrame host2_resp_body(
       spdy_util_.ConstructSpdyDataFrame(3, true));
   MockRead spdy_reads[] = {
@@ -13056,7 +13041,7 @@
       CreateMockWrite(req1, 0),
   };
 
-  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads1[] = {CreateMockRead(resp1, 1), CreateMockRead(body1, 2),
                        MockRead(SYNCHRONOUS, ERR_IO_PENDING, 3)};
@@ -13414,7 +13399,7 @@
   req2_block[spdy_util_.GetSchemeKey()] = "http";
   req2_block[spdy_util_.GetPathKey()] = "/";
   SpdySerializedFrame req2(
-      spdy_util_.ConstructSpdySyn(3, std::move(req2_block), MEDIUM, true));
+      spdy_util_.ConstructSpdyHeaders(3, std::move(req2_block), MEDIUM, true));
 
   MockWrite writes1[] = {
       CreateMockWrite(connect, 0), CreateMockWrite(wrapped_req1, 2),
@@ -13422,15 +13407,15 @@
   };
 
   SpdySerializedFrame conn_resp(
-      spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame resp1(
-      spdy_util_wrapped.ConstructSpdyGetSynReply(nullptr, 0, 1));
+      spdy_util_wrapped.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame body1(spdy_util_wrapped.ConstructSpdyDataFrame(1, true));
   SpdySerializedFrame wrapped_resp1(
       spdy_util_wrapped.ConstructWrappedSpdyFrame(resp1, 1));
   SpdySerializedFrame wrapped_body1(
       spdy_util_wrapped.ConstructWrappedSpdyFrame(body1, 1));
-  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
   SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
   MockRead reads1[] = {
       CreateMockRead(conn_resp, 1),
@@ -13525,13 +13510,13 @@
   SpdyHeaderBlock headers(
       spdy_util_.ConstructGetHeaderBlockForProxy("http://www.example.org/"));
   SpdySerializedFrame req1(
-      spdy_util_.ConstructSpdySyn(1, std::move(headers), LOWEST, true));
+      spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true));
 
   MockWrite writes1[] = {
       CreateMockWrite(req1, 0),
   };
 
-  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads1[] = {
       MockRead(ASYNC, ERR_IO_PENDING, 1), CreateMockRead(resp1, 2),
@@ -13554,8 +13539,7 @@
       CreateMockWrite(req2, 0),
   };
 
-  SpdySerializedFrame resp2(
-      spdy_util_secure.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp2(spdy_util_secure.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body2(spdy_util_secure.ConstructSpdyDataFrame(1, true));
   MockRead reads2[] = {CreateMockRead(resp2, 1), CreateMockRead(body2, 2),
                        MockRead(ASYNC, OK, 3)};
@@ -13647,7 +13631,7 @@
       CreateMockWrite(req2, 0),
   };
 
-  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads2[] = {
       CreateMockRead(resp2, 1), CreateMockRead(body2, 2),
@@ -13719,8 +13703,7 @@
   MockWrite spdy1_writes[] = {
       CreateMockWrite(host1_req, 0),
   };
-  SpdySerializedFrame host1_resp(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame host1_resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame host1_resp_body(
       spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead spdy1_reads[] = {
@@ -13741,8 +13724,7 @@
   MockWrite spdy2_writes[] = {
       CreateMockWrite(host2_req, 0),
   };
-  SpdySerializedFrame host2_resp(
-      spdy_util_2.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame host2_resp(spdy_util_2.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame host2_resp_body(
       spdy_util_2.ConstructSpdyDataFrame(1, true));
   MockRead spdy2_reads[] = {
@@ -15581,7 +15563,7 @@
   ssl.SetNextProto(kProtoHTTP2);
   session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {CreateMockRead(resp), CreateMockRead(body),
                       MockRead(ASYNC, ERR_IO_PENDING)};
diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc
index 93beffd..7f056ea 100644
--- a/net/http/http_proxy_client_socket_pool_unittest.cc
+++ b/net/http/http_proxy_client_socket_pool_unittest.cc
@@ -362,7 +362,7 @@
       spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, LOW,
                                       HostPortPair("www.google.com", 443)));
   MockWrite spdy_writes[] = {CreateMockWrite(req, 0, ASYNC)};
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   MockRead spdy_reads[] = {
       CreateMockRead(resp, 1, ASYNC),
       // Connection stays open.
@@ -402,7 +402,7 @@
       spdy_util_.ConstructSpdyConnect(kAuthHeaders, kAuthHeadersSize, 1, MEDIUM,
                                       HostPortPair("www.google.com", 443)));
   MockWrite spdy_writes[] = {CreateMockWrite(req, 0, ASYNC)};
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   MockRead spdy_reads[] = {CreateMockRead(resp, 1, ASYNC),
                            MockRead(ASYNC, 0, 2)};
 
@@ -591,7 +591,7 @@
   MockWrite spdy_writes[] = {
       CreateMockWrite(req, 0, ASYNC), CreateMockWrite(rst, 2, ASYNC),
   };
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdySynReplyError(1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyReplyError(1));
   MockRead spdy_reads[] = {
       CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, 0, 3),
   };
@@ -647,7 +647,7 @@
     "set-cookie", "foo=bar",
   };
   const int responseHeadersSize = arraysize(responseHeaders) / 2;
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdySynReplyError(
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyReplyError(
       "302 Found", responseHeaders, responseHeadersSize, 1));
   MockRead spdy_reads[] = {
       CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, 0, 2),
diff --git a/net/log/net_log_event_type_list.h b/net/log/net_log_event_type_list.h
index c7e2b68..6a26dc6 100644
--- a/net/log/net_log_event_type_list.h
+++ b/net/log/net_log_event_type_list.h
@@ -1278,34 +1278,6 @@
 //   }
 EVENT_TYPE(HTTP2_SESSION_INITIALIZED)
 
-// This event is sent for a SPDY SYN_STREAM.  Note that there is no SYN_STREAM
-// frame in HTTP/2.
-// The following parameters are attached:
-//   {
-//     "flags": <The control frame flags>,
-//     "headers": <The list of header:value pairs>,
-//     "fin": <True if this is the final data set by the peer on this stream>,
-//     "unidirectional": <True if this stream is unidirectional>,
-//     "priority": <The priority value of the stream>,
-//     "stream_id": <The stream id>,
-//   }
-EVENT_TYPE(HTTP2_SESSION_SYN_STREAM)
-
-// This event is sent for a SPDY SYN_STREAM pushed by the server, where a
-// net::URLRequest is already waiting for the stream.  Note that there is no
-// SYN_STREAM frame in HTTP/2.
-// The following parameters are attached:
-//   {
-//     "flags": <The control frame flags>,
-//     "headers": <The list of header:value pairs>,
-//     "fin": <True if this is the final data set by the peer on this stream>,
-//     "unidirectional": <True if this stream is unidirectional>,
-//     "priority": <The priority value of the stream>,
-//     "stream_id": <The stream id>,
-//     "associated_stream": <The stream id>,
-//   }
-EVENT_TYPE(HTTP2_SESSION_PUSHED_SYN_STREAM)
-
 // This event is sent for sending an HTTP/2 HEADERS frame.
 // The following parameters are attached:
 //   {
@@ -1319,7 +1291,7 @@
 //   }
 EVENT_TYPE(HTTP2_SESSION_SEND_HEADERS)
 
-// This event is sent for receiving an HTTP/2 (or SPDY) HEADERS frame.
+// This event is sent for receiving an HTTP/2 HEADERS frame.
 // The following parameters are attached:
 //   {
 //     "flags": <The control frame flags>,
@@ -1328,24 +1300,14 @@
 //   }
 EVENT_TYPE(HTTP2_SESSION_RECV_HEADERS)
 
-// This event is sent for a SPDY SYN_REPLY.  Not that there is no SYN_REPLY
-// frame in HTTP/2.
-// The following parameters are attached:
-//   {
-//     "flags": <The control frame flags>,
-//     "headers": <The list of header:value pairs>,
-//     "id": <The stream id>,
-//   }
-EVENT_TYPE(HTTP2_SESSION_SYN_REPLY)
-
-// On sending an HTTP/2 (or SPDY) SETTINGS frame.
+// On sending an HTTP/2 SETTINGS frame.
 // The following parameters are attached:
 //   {
 //     "settings": <The list of setting id, flags and value>,
 //   }
 EVENT_TYPE(HTTP2_SESSION_SEND_SETTINGS)
 
-// Receipt of an HTTP/2 (or SPDY) SETTINGS frame is received.
+// Receipt of an HTTP/2 SETTINGS frame.
 // The following parameters are attached:
 //   {
 //     "host": <The host-port string>,
@@ -1354,7 +1316,7 @@
 //   }
 EVENT_TYPE(HTTP2_SESSION_RECV_SETTINGS)
 
-// Receipt of an individual HTTP/2 (or SPDY) setting.
+// Receipt of an individual HTTP/2 setting.
 // The following parameters are attached:
 //   {
 //     "id":    <The setting id>,
@@ -1363,7 +1325,7 @@
 //   }
 EVENT_TYPE(HTTP2_SESSION_RECV_SETTING)
 
-// The receipt of a RST_STREAM
+// The receipt of a RST_STREAM frame.
 // The following parameters are attached:
 //   {
 //     "stream_id": <The stream ID for the window update>,
@@ -1380,7 +1342,7 @@
 //   }
 EVENT_TYPE(HTTP2_SESSION_SEND_RST_STREAM)
 
-// Sending of an HTTP/2 (or SPDY) PING frame.
+// Sending of an HTTP/2 PING frame.
 // The following parameters are attached:
 //   {
 //     "unique_id": <The unique id of the PING message>,
@@ -1388,7 +1350,7 @@
 //   }
 EVENT_TYPE(HTTP2_SESSION_PING)
 
-// Receipt of an HTTP/2 (or SPDY) GOAWAY frame.
+// Receipt of an HTTP/2 GOAWAY frame.
 // The following parameters are attached:
 //   {
 //     "last_accepted_stream_id": <Last stream id accepted by the server, duh>,
@@ -1398,15 +1360,14 @@
 //   }
 EVENT_TYPE(HTTP2_SESSION_GOAWAY)
 
-// Receipt of an HTTP/2 (or SPDY) WINDOW_UPDATE frame (which controls the send
-// window).
+// Receipt of an HTTP/2 WINDOW_UPDATE frame (which controls the send window).
 //   {
 //     "stream_id": <The stream ID for the window update>,
 //     "delta"    : <The delta window size>,
 //   }
 EVENT_TYPE(HTTP2_SESSION_RECEIVED_WINDOW_UPDATE_FRAME)
 
-// Sending of an HTTP/2 (or SPDY) WINDOW_UPDATE frame (which controls the
+// Sending of an HTTP/2 WINDOW_UPDATE frame (which controls the
 // receive window).
 //   {
 //     "stream_id": <The stream ID for the window update>,
@@ -1444,7 +1405,7 @@
 //   }
 EVENT_TYPE(HTTP2_SESSION_RECV_DATA)
 
-// This event is sent for receiving an HTTP/2 (or SPDY) PUSH_PROMISE frame.
+// This event is sent for receiving an HTTP/2 PUSH_PROMISE frame.
 // The following parameters are attached:
 //   {
 //     "headers": <The list of header:value pairs>,
@@ -1506,8 +1467,8 @@
 //   }
 EVENT_TYPE(HTTP2_SESSION_POOL_CREATED_NEW_SESSION)
 
-// This event indicates that a SSL socket has been upgraded to an HTTP/2 (or
-// SPDY) session.
+// This event indicates that a SSL socket has been upgraded to an HTTP/2
+// session.
 //   {
 //     "source_dependency": <The session id>,
 //   }
@@ -1523,7 +1484,7 @@
 // SpdyStream
 // ------------------------------------------------------------------------
 
-// The begin and end of an HTTP/2 (or SPDY) STREAM.
+// The begin and end of an HTTP/2 STREAM.
 EVENT_TYPE(HTTP2_STREAM)
 
 // A stream is attached to a pushed stream.
@@ -1565,7 +1526,7 @@
 // ------------------------------------------------------------------------
 
 EVENT_TYPE(HTTP2_PROXY_CLIENT_SESSION)
-// Identifies the HTTP/2 (or SPDY) session a source is using.
+// Identifies the HTTP/2 session a source is using.
 //   {
 //     "source_dependency":  <Source identifier for the underlying session>,
 //   }
diff --git a/net/spdy/bidirectional_stream_spdy_impl_unittest.cc b/net/spdy/bidirectional_stream_spdy_impl_unittest.cc
index 6e0f2a3..975ef39d 100644
--- a/net/spdy/bidirectional_stream_spdy_impl_unittest.cc
+++ b/net/spdy/bidirectional_stream_spdy_impl_unittest.cc
@@ -265,7 +265,7 @@
 
   const char* const kExtraHeaders[] = {"X-UpperCase", "yes"};
   SpdySerializedFrame resp(
-      spdy_util_.ConstructSpdyGetSynReply(kExtraHeaders, 1, 1));
+      spdy_util_.ConstructSpdyGetReply(kExtraHeaders, 1, 1));
 
   MockRead reads[] = {
       CreateMockRead(resp, 1), MockRead(ASYNC, 0, 3),
@@ -318,7 +318,7 @@
       CreateMockWrite(rst, 5),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame response_body_frame(
       spdy_util_.ConstructSpdyDataFrame(1, false));
 
diff --git a/net/spdy/spdy_http_stream.h b/net/spdy/spdy_http_stream.h
index fcf3d4e..83829a5 100644
--- a/net/spdy/spdy_http_stream.h
+++ b/net/spdy/spdy_http_stream.h
@@ -155,7 +155,7 @@
   const HttpRequestInfo* request_info_;
 
   // |response_info_| is the HTTP response data object which is filled in
-  // when a SYN_REPLY comes in for the stream.
+  // when a response HEADERS comes in for the stream.
   // It is not owned by this stream object, or point to |push_response_info_|.
   HttpResponseInfo* response_info_;
 
diff --git a/net/spdy/spdy_http_stream_unittest.cc b/net/spdy/spdy_http_stream_unittest.cc
index 01aeebbb..7afb332 100644
--- a/net/spdy/spdy_http_stream_unittest.cc
+++ b/net/spdy/spdy_http_stream_unittest.cc
@@ -192,7 +192,7 @@
   MockWrite writes[] = {
       CreateMockWrite(req, 0),
   };
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   MockRead reads[] = {
       CreateMockRead(resp, 1), MockRead(SYNCHRONOUS, 0, 2)  // EOF
   };
@@ -251,9 +251,9 @@
   MockWrite writes[] = {
       CreateMockWrite(req1, 0), CreateMockWrite(req2, 1),
   };
-  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, "", 0, true));
-  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 3));
+  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
   SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, "", 0, true));
   MockRead reads[] = {
       CreateMockRead(resp1, 2), CreateMockRead(body1, 3),
@@ -345,7 +345,7 @@
       CreateMockWrite(body, 1)  // POST upload frame
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(nullptr, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
   MockRead reads[] = {
       CreateMockRead(resp, 2), CreateMockRead(body, 3),
       MockRead(SYNCHRONOUS, 0, 4)  // EOF
@@ -402,7 +402,7 @@
       CreateMockWrite(chunk, 1),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(nullptr, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
   MockRead reads[] = {
       CreateMockRead(resp, 2), CreateMockRead(chunk, 3),
       MockRead(SYNCHRONOUS, 0, 4)  // EOF
@@ -453,7 +453,7 @@
       CreateMockWrite(body, 1)  // First POST upload frame
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(nullptr, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
   MockRead reads[] = {
       MockRead(ASYNC, ERR_CONNECTION_CLOSED, 2)  // Server hangs up early.
   };
@@ -520,7 +520,7 @@
       CreateMockWrite(chunk1, 1),  // POST upload frames
       CreateMockWrite(chunk2, 2), CreateMockWrite(chunk3, 3),
   };
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(nullptr, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
   MockRead reads[] = {
       CreateMockRead(resp, 4), CreateMockRead(chunk1, 5),
       CreateMockRead(chunk2, 6), CreateMockRead(chunk3, 7),
@@ -613,7 +613,7 @@
       CreateMockWrite(chunk1, 1),  // POST upload frames
       CreateMockWrite(chunk2, 2),
   };
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(nullptr, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
   MockRead reads[] = {
       CreateMockRead(resp, 3), CreateMockRead(chunk1, 4),
       CreateMockRead(chunk2, 5), MockRead(ASYNC, 0, 6)  // EOF
@@ -694,7 +694,7 @@
   MockWrite writes[] = {
       CreateMockWrite(req, 0), CreateMockWrite(chunk, 1),
   };
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(nullptr, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
   MockRead reads[] = {
       CreateMockRead(resp, 2), CreateMockRead(chunk, 3),
       MockRead(ASYNC, 0, 4)  // EOF
@@ -758,7 +758,7 @@
   MockWrite writes[] = {
       CreateMockWrite(req, 0),
   };
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   MockRead reads[] = {
       CreateMockRead(resp, 1), MockRead(SYNCHRONOUS, 0, 2)  // EOF
   };
@@ -802,7 +802,7 @@
   MockWrite writes[] = {
       CreateMockWrite(req, 0), CreateMockWrite(chunk1, 1),
   };
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(nullptr, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
   SpdySerializedFrame window_update(
       spdy_util_.ConstructSpdyWindowUpdate(1, kUploadDataSize));
   MockRead reads[] = {
@@ -904,7 +904,7 @@
       CreateMockWrite(rst_frame, 1, SYNCHRONOUS)  // Reset frame
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(nullptr, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
 
   MockRead reads[] = {
       CreateMockRead(resp, 2), MockRead(SYNCHRONOUS, 0, 3),
@@ -952,7 +952,7 @@
       CreateMockWrite(rst_frame, 1)  // Reset frame
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(nullptr, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0));
 
   MockRead reads[] = {
       MockRead(ASYNC, 0, 2),
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index 7b4ac617..bd229f9 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -494,7 +494,7 @@
                                             << "||||| Expected data: "
                                             << expected;
 
-    // Verify the SYN_REPLY.
+    // Verify the response HEADERS.
     // Copy the response info, because trans goes away.
     *response = *trans->GetResponseInfo();
     *push_response = *trans2->GetResponseInfo();
@@ -562,7 +562,7 @@
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
   MockWrite writes[] = {CreateMockWrite(req, 0)};
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {
       CreateMockRead(resp, 1), CreateMockRead(body, 2),
@@ -615,7 +615,7 @@
     }
 
     SpdySerializedFrame resp(
-        spdy_test_util.ConstructSpdyGetSynReply(nullptr, 0, 1));
+        spdy_test_util.ConstructSpdyGetReply(nullptr, 0, 1));
     SpdySerializedFrame body(spdy_test_util.ConstructSpdyDataFrame(1, true));
     MockRead reads[] = {
         CreateMockRead(resp, 1), CreateMockRead(body, 2),
@@ -648,19 +648,19 @@
 TEST_F(SpdyNetworkTransactionTest, ThreeGets) {
   SpdySerializedFrame req(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false));
   SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true));
 
   SpdySerializedFrame req2(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true));
-  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
   SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false));
   SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true));
 
   SpdySerializedFrame req3(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 5, LOWEST, true));
-  SpdySerializedFrame resp3(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 5));
+  SpdySerializedFrame resp3(spdy_util_.ConstructSpdyGetReply(NULL, 0, 5));
   SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(5, false));
   SpdySerializedFrame fbody3(spdy_util_.ConstructSpdyDataFrame(5, true));
 
@@ -740,13 +740,13 @@
 TEST_F(SpdyNetworkTransactionTest, TwoGetsLateBinding) {
   SpdySerializedFrame req(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false));
   SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true));
 
   SpdySerializedFrame req2(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true));
-  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
   SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false));
   SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true));
 
@@ -822,13 +822,13 @@
 TEST_F(SpdyNetworkTransactionTest, TwoGetsLateBindingFromPreconnect) {
   SpdySerializedFrame req(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false));
   SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true));
 
   SpdySerializedFrame req2(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true));
-  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
   SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false));
   SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true));
 
@@ -919,21 +919,21 @@
   // Each request fully completes before the next starts.
   SpdySerializedFrame req(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false));
   SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true));
   spdy_util_.UpdateWithStreamDestruction(1);
 
   SpdySerializedFrame req2(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true));
-  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
   SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false));
   SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true));
   spdy_util_.UpdateWithStreamDestruction(3);
 
   SpdySerializedFrame req3(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 5, LOWEST, true));
-  SpdySerializedFrame resp3(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 5));
+  SpdySerializedFrame resp3(spdy_util_.ConstructSpdyGetReply(NULL, 0, 5));
   SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(5, false));
   SpdySerializedFrame fbody3(spdy_util_.ConstructSpdyDataFrame(5, true));
 
@@ -1047,27 +1047,27 @@
   // Construct the request.
   SpdySerializedFrame req(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false));
   SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true));
   spdy_util_.UpdateWithStreamDestruction(1);
 
   SpdySerializedFrame req2(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true));
-  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
   SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false));
   SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true));
   spdy_util_.UpdateWithStreamDestruction(3);
 
   SpdySerializedFrame req4(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 5, HIGHEST, true));
-  SpdySerializedFrame resp4(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 5));
+  SpdySerializedFrame resp4(spdy_util_.ConstructSpdyGetReply(NULL, 0, 5));
   SpdySerializedFrame fbody4(spdy_util_.ConstructSpdyDataFrame(5, true));
   spdy_util_.UpdateWithStreamDestruction(5);
 
   SpdySerializedFrame req3(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 7, LOWEST, true));
-  SpdySerializedFrame resp3(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 7));
+  SpdySerializedFrame resp3(spdy_util_.ConstructSpdyGetReply(NULL, 0, 7));
   SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(7, false));
   SpdySerializedFrame fbody3(spdy_util_.ConstructSpdyDataFrame(7, true));
 
@@ -1198,14 +1198,14 @@
   // Construct the request.
   SpdySerializedFrame req(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false));
   SpdySerializedFrame fbody(spdy_util_.ConstructSpdyDataFrame(1, true));
   spdy_util_.UpdateWithStreamDestruction(1);
 
   SpdySerializedFrame req2(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true));
-  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
   SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, false));
   SpdySerializedFrame fbody2(spdy_util_.ConstructSpdyDataFrame(3, true));
 
@@ -1325,14 +1325,14 @@
   // Construct the request.
   SpdySerializedFrame req(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, false));
   SpdySerializedFrame fin_body(spdy_util_.ConstructSpdyDataFrame(1, true));
   spdy_util_.UpdateWithStreamDestruction(1);
 
   SpdySerializedFrame req2(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 3, LOWEST, true));
-  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
 
   SettingsMap settings;
   const uint32_t max_concurrent_streams = 1;
@@ -1423,12 +1423,12 @@
   SpdyHeaderBlock put_headers(
       spdy_util_.ConstructPutHeaderBlock(kDefaultUrl, 0));
   SpdySerializedFrame req(
-      spdy_util_.ConstructSpdySyn(1, std::move(put_headers), LOWEST, true));
+      spdy_util_.ConstructSpdyHeaders(1, std::move(put_headers), LOWEST, true));
   MockWrite writes[] = {
       CreateMockWrite(req, 0),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {
       CreateMockRead(resp, 1), CreateMockRead(body, 2),
@@ -1454,13 +1454,13 @@
 
   SpdyHeaderBlock head_headers(
       spdy_util_.ConstructHeadHeaderBlock(kDefaultUrl, 0));
-  SpdySerializedFrame req(
-      spdy_util_.ConstructSpdySyn(1, std::move(head_headers), LOWEST, true));
+  SpdySerializedFrame req(spdy_util_.ConstructSpdyHeaders(
+      1, std::move(head_headers), LOWEST, true));
   MockWrite writes[] = {
       CreateMockWrite(req, 0),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {
       CreateMockRead(resp, 1), CreateMockRead(body, 2),
@@ -1486,7 +1486,7 @@
       CreateMockWrite(req, 0), CreateMockWrite(body, 1),  // POST upload frame
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(NULL, 0));
   MockRead reads[] = {
       CreateMockRead(resp, 2), CreateMockRead(body, 3),
       MockRead(ASYNC, 0, 4)  // EOF
@@ -1511,7 +1511,7 @@
       CreateMockWrite(req, 0), CreateMockWrite(body, 1),  // POST upload frame
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(NULL, 0));
   MockRead reads[] = {
       CreateMockRead(resp, 2), CreateMockRead(body, 3),
       MockRead(ASYNC, 0, 4)  // EOF
@@ -1557,7 +1557,7 @@
       CreateMockWrite(req, 0), CreateMockWrite(body, 1),  // POST upload frame
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(NULL, 0));
   MockRead reads[] = {
       CreateMockRead(resp, 2), CreateMockRead(body, 3),
       MockRead(ASYNC, 0, 4)  // EOF
@@ -1581,7 +1581,7 @@
       CreateMockWrite(req, 0), CreateMockWrite(body, 1),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(NULL, 0));
   MockRead reads[] = {
       CreateMockRead(resp, 2), CreateMockRead(body, 3),
       MockRead(ASYNC, 0, 4)  // EOF
@@ -1615,7 +1615,7 @@
       CreateMockWrite(chunk2, 2), CreateMockWrite(chunk3, 3),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(NULL, 0));
   MockRead reads[] = {
       CreateMockRead(resp, 4), CreateMockRead(chunk1, 5),
       CreateMockRead(chunk2, 6), CreateMockRead(chunk3, 7),
@@ -1665,13 +1665,13 @@
   SpdyHeaderBlock req_block(
       spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, 0));
   SpdySerializedFrame req(
-      spdy_util_.ConstructSpdySyn(1, std::move(req_block), LOWEST, true));
+      spdy_util_.ConstructSpdyHeaders(1, std::move(req_block), LOWEST, true));
 
   MockWrite writes[] = {
       CreateMockWrite(req, 0),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(NULL, 0));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {
       CreateMockRead(resp, 1), CreateMockRead(body, 2),
@@ -1706,13 +1706,13 @@
   SpdyHeaderBlock req_block(
       spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, kContentLength));
   SpdySerializedFrame req(
-      spdy_util_.ConstructSpdySyn(1, std::move(req_block), LOWEST, true));
+      spdy_util_.ConstructSpdyHeaders(1, std::move(req_block), LOWEST, true));
 
   MockWrite writes[] = {
       CreateMockWrite(req, 0),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(NULL, 0));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {
       CreateMockRead(resp, 1), CreateMockRead(body, 2),
@@ -1737,7 +1737,7 @@
   MockWrite writes[] = {
       CreateMockWrite(req, 0), CreateMockWrite(body, 3),
   };
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(NULL, 0));
   MockRead reads[] = {
       CreateMockRead(resp, 1), CreateMockRead(body, 2),
       MockRead(ASYNC, 0, 4)  // EOF
@@ -1755,7 +1755,7 @@
 
   base::RunLoop().RunUntilIdle();
 
-  // Process the request headers, SYN_REPLY, and response body.
+  // Process the request headers, response headers, and response body.
   // The request body is still in flight.
   const HttpResponseInfo* response = helper.trans()->GetResponseInfo();
   EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
@@ -1784,7 +1784,7 @@
       CreateMockWrite(rst, 3, SYNCHRONOUS),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   MockRead reads[] = {
       CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, 0, 0, 4)  // EOF
   };
@@ -1808,7 +1808,7 @@
 }
 
 // Test that the transaction doesn't crash when we don't have a reply.
-TEST_F(SpdyNetworkTransactionTest, ResponseWithoutSynReply) {
+TEST_F(SpdyNetworkTransactionTest, ResponseWithoutHeaders) {
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {
       CreateMockRead(body, 1), MockRead(ASYNC, 0, 3)  // EOF
@@ -1840,8 +1840,8 @@
       CreateMockWrite(req, 0), CreateMockWrite(rst, 4),
   };
 
-  SpdySerializedFrame resp0(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
-  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp0(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {
       CreateMockRead(resp0, 1), CreateMockRead(resp1, 2),
@@ -1887,7 +1887,7 @@
   const char* const headers[] = {
     "transfer-encoding", "chunked"
   };
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(headers, 1, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(headers, 1, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {
       CreateMockRead(resp, 1), CreateMockRead(body, 3),
@@ -1915,7 +1915,7 @@
       CreateMockWrite(req, 0), CreateMockWrite(rst, 4),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   const char* const headers[] = {
     "transfer-encoding", "chunked"
   };
@@ -1949,7 +1949,7 @@
       CreateMockWrite(req),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   MockRead reads[] = {
       CreateMockRead(resp),
       // This following read isn't used by the test, except during the
@@ -1991,7 +1991,7 @@
       CreateMockWrite(rst, 2, SYNCHRONOUS),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   MockRead reads[] = {
       CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, 0, 0, 3)  // EOF
   };
@@ -2033,7 +2033,7 @@
       0x07, 'h',  'e',  'l',  'l',  'o',  '!',
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   MockRead reads[] = {
       CreateMockRead(resp, 1),
       MockRead(ASYNC, ERR_IO_PENDING, 2),  // Force a pause
@@ -2088,7 +2088,7 @@
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
   MockWrite writes[] = {CreateMockWrite(req, 0)};
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {
       CreateMockRead(resp, 1),
@@ -2135,8 +2135,8 @@
 
   // Setup writes/reads to www.example.org
   SpdySerializedFrame req(
-      spdy_util_.ConstructSpdySyn(1, std::move(headers), LOWEST, true));
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReplyRedirect(1));
+      spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReplyRedirect(1));
   MockWrite writes[] = {
       CreateMockWrite(req, 1),
   };
@@ -2151,12 +2151,12 @@
   headers2["user-agent"] = "";
   headers2["accept-encoding"] = "gzip, deflate";
   SpdySerializedFrame req2(
-      spdy_util_.ConstructSpdySyn(1, std::move(headers2), LOWEST, true));
+      spdy_util_.ConstructSpdyHeaders(1, std::move(headers2), LOWEST, true));
   MockWrite writes2[] = {
       CreateMockWrite(req2, 1),
   };
 
-  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads2[] = {
       CreateMockRead(resp2, 2), CreateMockRead(body2, 3),
@@ -2206,8 +2206,8 @@
 
   // Setup writes/reads to www.example.org
   SpdySerializedFrame req(
-      spdy_util_.ConstructSpdySyn(1, std::move(headers), LOWEST, true));
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame rep(spdy_util_.ConstructSpdyPush(
       NULL, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str(),
       "301 Moved Permanently", "http://www.foo.com/index.php"));
@@ -2229,8 +2229,8 @@
   headers2["user-agent"] = "";
   headers2["accept-encoding"] = "gzip, deflate";
   SpdySerializedFrame req2(
-      spdy_util_.ConstructSpdySyn(1, std::move(headers2), LOWEST, true));
-  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyHeaders(1, std::move(headers2), LOWEST, true));
+  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockWrite writes2[] = {
       CreateMockWrite(req2, 1),
@@ -2292,7 +2292,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush(
       NULL, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str()));
   const char kPushedData[] = "pushed";
@@ -2315,7 +2315,7 @@
                     &response2,
                     expected_push_result);
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   EXPECT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
 
@@ -2324,7 +2324,7 @@
   EXPECT_EQ("HTTP/1.1 200", response2.headers->GetStatusLine());
 }
 
-TEST_F(SpdyNetworkTransactionTest, ServerPushBeforeSynReply) {
+TEST_F(SpdyNetworkTransactionTest, ServerPushBeforeHeaders) {
   SpdySerializedFrame stream1_syn(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
   MockWrite writes[] = {
@@ -2334,7 +2334,7 @@
   SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush(
       nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str()));
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true));
   const char kPushedData[] = "pushed";
   SpdySerializedFrame stream2_body(spdy_util_.ConstructSpdyDataFrame(
@@ -2356,7 +2356,7 @@
                     &response2,
                     expected_push_result);
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   EXPECT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
 
@@ -2373,7 +2373,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush(
       NULL, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str()));
   const char kPushedData[] = "pushed";
@@ -2397,7 +2397,7 @@
                     &response2,
                     expected_push_result);
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   EXPECT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
 
@@ -2415,7 +2415,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush(
       NULL, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str()));
   SpdySerializedFrame stream2_rst(
@@ -2449,7 +2449,7 @@
   EXPECT_TRUE(data.AllReadDataConsumed());
   EXPECT_TRUE(data.AllWriteDataConsumed());
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   HttpResponseInfo response = *trans->GetResponseInfo();
   EXPECT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
@@ -2468,7 +2468,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush(
       NULL, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str()));
   const char kPushedData[] = "pushed";
@@ -2494,7 +2494,7 @@
                     &response2,
                     expected_push_result);
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   EXPECT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
 
@@ -2512,7 +2512,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush(
       NULL, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str()));
   static const char kPushedData[] = "pushed my darling hello my baby";
@@ -2545,7 +2545,7 @@
   SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
   RunServerPushTest(&data, &response, &response2, kPushedData);
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   EXPECT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
 
@@ -2563,7 +2563,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush(
       NULL, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str()));
   static const char kPushedData[] = "pushed my darling hello my baby";
@@ -2595,7 +2595,7 @@
   SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
   RunServerPushTest(&data, &response, &response2, kPushedData);
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   EXPECT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
 
@@ -2614,7 +2614,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush(
       NULL, 0, 2, 0, GetDefaultUrlWithPath("/foo.dat").c_str()));
   MockRead reads[] = {
@@ -2642,7 +2642,7 @@
   EXPECT_TRUE(data.AllReadDataConsumed());
   EXPECT_TRUE(data.AllWriteDataConsumed());
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   HttpResponseInfo response = *trans->GetResponseInfo();
   EXPECT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
@@ -2659,7 +2659,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush(
       NULL, 0, 2, 9, GetDefaultUrlWithPath("/foo.dat").c_str()));
   MockRead reads[] = {
@@ -2689,7 +2689,7 @@
   EXPECT_TRUE(data.AllReadDataConsumed());
   EXPECT_TRUE(data.AllWriteDataConsumed());
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   HttpResponseInfo response = *trans->GetResponseInfo();
   EXPECT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
@@ -2706,7 +2706,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdyHeaderBlock incomplete_headers;
   incomplete_headers[spdy_util_.GetStatusKey()] = "200 OK";
   incomplete_headers["hello"] = "bye";
@@ -2739,7 +2739,7 @@
   EXPECT_TRUE(data.AllReadDataConsumed());
   EXPECT_TRUE(data.AllWriteDataConsumed());
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   HttpResponseInfo response = *trans->GetResponseInfo();
   EXPECT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
@@ -2756,7 +2756,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush(
       nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str()));
   SpdySerializedFrame stream3_syn(spdy_util_.ConstructSpdyPush(
@@ -2783,7 +2783,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true));
   SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush(
       nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str()));
@@ -2826,7 +2826,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame stream2_syn(spdy_util_.ConstructSpdyPush(
       nullptr, 0, 2, 1, GetDefaultUrlWithPath("/foo.dat").c_str()));
   SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, true));
@@ -2879,10 +2879,9 @@
   EXPECT_TRUE(data.AllWriteDataConsumed());
 }
 
-// Verify that various SynReply headers parse correctly through the
-// HTTP layer.
-TEST_F(SpdyNetworkTransactionTest, SynReplyHeaders) {
-  struct SynReplyHeadersTests {
+// Verify that various response headers parse correctly through the HTTP layer.
+TEST_F(SpdyNetworkTransactionTest, ResponseHeaders) {
+  struct ResponseHeadersTests {
     int num_headers;
     const char* extra_headers[5];
     SpdyHeaderBlock expected_headers;
@@ -2923,7 +2922,7 @@
         spdy_test_util.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
     MockWrite writes[] = {CreateMockWrite(req, 0)};
 
-    SpdySerializedFrame resp(spdy_test_util.ConstructSpdyGetSynReply(
+    SpdySerializedFrame resp(spdy_test_util.ConstructSpdyGetReply(
         test_cases[i].extra_headers, test_cases[i].num_headers, 1));
     SpdySerializedFrame body(spdy_test_util.ConstructSpdyDataFrame(1, true));
     MockRead reads[] = {
@@ -2963,11 +2962,11 @@
   }
 }
 
-// Verify that various SynReply headers parse vary fields correctly
-// through the HTTP layer, and the response matches the request.
-TEST_F(SpdyNetworkTransactionTest, SynReplyHeadersVary) {
+// Verify that various response headers parse vary fields correctly through the
+// HTTP layer, and the response matches the request.
+TEST_F(SpdyNetworkTransactionTest, ResponseHeadersVary) {
   // Modify the following data to change/add test cases:
-  struct SynReplyTests {
+  struct ResponseTests {
     bool vary_matches;
     int num_headers[2];
     const char* extra_headers[2][16];
@@ -3069,23 +3068,23 @@
   }
 }
 
-// Verify that we don't crash on invalid SynReply responses.
-TEST_F(SpdyNetworkTransactionTest, InvalidSynReply) {
-  struct InvalidSynReplyTests {
+// Verify that we don't crash on invalid response headers.
+TEST_F(SpdyNetworkTransactionTest, InvalidResponseHeaders) {
+  struct InvalidResponseHeadersTests {
     int num_headers;
     const char* headers[10];
   } test_cases[] = {
-      // SYN_REPLY missing status header
+      // Response headers missing status header
       {
           3,
           {spdy_util_.GetPathKey(), "/index.php", "cookie", "val1", "cookie",
            "val2", NULL},
       },
-      // SYN_REPLY missing version header
+      // Response headers missing version header
       {
           1, {spdy_util_.GetPathKey(), "/index.php", "status", "200", NULL},
       },
-      // SYN_REPLY with no headers
+      // Response headers with no headers
       {
           0, {NULL},
       },
@@ -3130,16 +3129,16 @@
   MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(goaway, 2)};
 
   // This is the length field that's too short.
-  SpdySerializedFrame syn_reply_wrong_length(
-      spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
-  size_t right_size = syn_reply_wrong_length.size() -
+  SpdySerializedFrame reply_wrong_length(
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+  size_t right_size = reply_wrong_length.size() -
                       SpdyConstants::GetControlFrameHeaderSize(HTTP2);
   size_t wrong_size = right_size - 4;
-  test::SetFrameLength(&syn_reply_wrong_length, wrong_size, HTTP2);
+  test::SetFrameLength(&reply_wrong_length, wrong_size, HTTP2);
 
   MockRead reads[] = {
-      MockRead(ASYNC, syn_reply_wrong_length.data(),
-               syn_reply_wrong_length.size() - 4, 1),
+      MockRead(ASYNC, reply_wrong_length.data(), reply_wrong_length.size() - 4,
+               1),
   };
 
   SequencedSocketData data(reads, arraysize(reads), writes, arraysize(writes));
@@ -3158,7 +3157,7 @@
   MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(goaway, 2)};
 
   // Read HEADERS with corrupted payload.
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   memset(resp.data() + 12, 0xcf, resp.size() - 12);
   MockRead reads[] = {CreateMockRead(resp, 1)};
 
@@ -3223,7 +3222,7 @@
 
 // Test that partial writes work.
 TEST_F(SpdyNetworkTransactionTest, PartialWrite) {
-  // Chop the SYN_STREAM frame into 5 chunks.
+  // Chop the HEADERS frame into 5 chunks.
   SpdySerializedFrame req(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
   const int kChunks = 5;
@@ -3232,7 +3231,7 @@
     writes[i].sequence_number = i;
   }
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {
       CreateMockRead(resp, kChunks), CreateMockRead(body, kChunks + 1),
@@ -3258,7 +3257,7 @@
       spdy_util_.ConstructSpdyGet(kExtraHeaders, 1, 1, LOWEST, true));
   MockWrite writes[] = {CreateMockWrite(req, 0)};
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {
       CreateMockRead(resp, 1), CreateMockRead(body, 2),
@@ -3354,7 +3353,7 @@
   SpdySerializedFrame last_frame(
       spdy_util_.ConstructSpdyDataFrame(1, "d", 1, /*fin=*/true));
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   MockRead reads[] = {
       CreateMockRead(resp, 1),
       MockRead(ASYNC, ERR_IO_PENDING, 2),  // Force a pause
@@ -3441,7 +3440,7 @@
       CombineFrames(data_frames, arraysize(data_frames),
                     combined_data_frames, arraysize(combined_data_frames));
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   MockRead reads[] = {
       CreateMockRead(resp, 1),
       MockRead(ASYNC, ERR_IO_PENDING, 2),  // Force a pause
@@ -3518,7 +3517,7 @@
   MockWrite writes[] = {CreateMockWrite(req, 0)};
 
   // 5 data frames in a single read.
-  SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame data_frame(
       spdy_util_.ConstructSpdyDataFrame(1, "message", 7, /*fin=*/false));
   SpdySerializedFrame data_frame_fin(
@@ -3609,7 +3608,7 @@
   int combined_data_frames_len =
       CombineFrames(data_frames, arraysize(data_frames),
                     combined_data_frames, arraysize(combined_data_frames));
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   MockRead reads[] = {
       CreateMockRead(resp, 1),
       MockRead(ASYNC, ERR_IO_PENDING, 2),  // Force a wait
@@ -3689,7 +3688,7 @@
   SpdySerializedFrame data_frame(
       spdy_util_.ConstructSpdyDataFrame(1, "message", 7, /*fin=*/false));
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   MockRead reads[] = {
       CreateMockRead(resp, 1),
       MockRead(ASYNC, ERR_IO_PENDING, 2),                   // Force a wait
@@ -3766,7 +3765,7 @@
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
   MockWrite writes[] = {CreateMockWrite(req, 0)};
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   MockRead reads[] = {
       CreateMockRead(resp, 1), MockRead(SYNCHRONOUS, 0, 2)  // EOF
   };
@@ -3811,7 +3810,7 @@
   // First socket: HTTP/2 request rejected with HTTP_1_1_REQUIRED.
   SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
   SpdySerializedFrame req(
-      spdy_util_.ConstructSpdySyn(1, std::move(headers), LOWEST, true));
+      spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true));
   MockWrite writes0[] = {CreateMockWrite(req, 0)};
   SpdySerializedFrame go_away(spdy_util_.ConstructSpdyGoAway(
       0, GOAWAY_HTTP_1_1_REQUIRED, "Try again using HTTP/1.1 please."));
@@ -3985,7 +3984,7 @@
   const char kHTTP200[] = {"HTTP/1.1 200 OK\r\n\r\n"};
   SpdySerializedFrame req(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
 
   MockWrite writes[] = {
@@ -4010,7 +4009,7 @@
   rv = callback.WaitForResult();
   EXPECT_EQ(0, rv);
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   HttpResponseInfo response = *trans->GetResponseInfo();
   ASSERT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
@@ -4046,7 +4045,7 @@
       CreateMockWrite(req, 0),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {
       CreateMockRead(resp, 1), CreateMockRead(body, 2),
@@ -4096,7 +4095,7 @@
   const char kHTTP200[] = {"HTTP/1.1 200 OK\r\n\r\n"};
   SpdySerializedFrame req2(spdy_util_2.ConstructSpdyGet(
       GetDefaultUrlWithPath("/foo.dat").c_str(), 1, LOWEST));
-  SpdySerializedFrame resp2(spdy_util_2.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp2(spdy_util_2.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body2(spdy_util_2.ConstructSpdyDataFrame(1, true));
 
   MockWrite writes2[] = {
@@ -4149,7 +4148,7 @@
 // This can happen when a server reboots without saying goodbye, or when
 // we're behind a NAT that masked the RST.
 TEST_F(SpdyNetworkTransactionTest, VerifyRetryOnConnectionReset) {
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {
       CreateMockRead(resp, 1), CreateMockRead(body, 2),
@@ -4259,13 +4258,12 @@
     "www-authenticate",
     "Basic realm=\"MyRealm\""
   };
-  SpdySerializedFrame resp_authentication(spdy_util_.ConstructSpdySynReplyError(
+  SpdySerializedFrame resp_authentication(spdy_util_.ConstructSpdyReplyError(
       "401 Authentication Required", kExtraAuthenticationHeaders,
       arraysize(kExtraAuthenticationHeaders) / 2, 1));
   SpdySerializedFrame body_authentication(
       spdy_util_.ConstructSpdyDataFrame(1, true));
-  SpdySerializedFrame resp_data(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+  SpdySerializedFrame resp_data(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
   SpdySerializedFrame body_data(spdy_util_.ConstructSpdyDataFrame(3, true));
   MockRead spdy_reads[] = {
       CreateMockRead(resp_authentication, 1),
@@ -4328,7 +4326,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
 
   SpdyHeaderBlock initial_headers;
   spdy_util_.AddUrlToHeaderBlock(GetDefaultUrlWithPath("/foo.dat"),
@@ -4366,7 +4364,7 @@
                     &response2,
                     expected_push_result);
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   EXPECT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
 
@@ -4384,7 +4382,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdyHeaderBlock initial_headers;
   spdy_util_.AddUrlToHeaderBlock(GetDefaultUrlWithPath("/foo.dat"),
                                  &initial_headers);
@@ -4423,7 +4421,7 @@
   int rv = trans->Start(
       &CreateGetRequest(), callback.callback(), BoundNetLog());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
-  // Run until we've received the primary SYN_STREAM, the pushed SYN_STREAM,
+  // Run until we've received the primary HEADERS, the pushed HEADERS,
   // and the body of the primary stream, but before we've received the HEADERS
   // for the pushed stream.
   data.RunUntilPaused();
@@ -4454,14 +4452,14 @@
       << "||||| Expected data: "
       << expected_push_result;
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   // Copy the response info, because trans goes away.
   response = *trans->GetResponseInfo();
   response2 = *trans2->GetResponseInfo();
 
   VerifyStreamsClosed(helper);
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   EXPECT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
 
@@ -4488,7 +4486,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
 
   SpdyHeaderBlock initial_headers;
   spdy_util_.AddUrlToHeaderBlock(GetDefaultUrlWithPath("/foo.dat"),
@@ -4539,7 +4537,7 @@
   int rv = trans->Start(
       &CreateGetRequest(), callback.callback(), BoundNetLog());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
-  // Run until we've received the primary SYN_STREAM, the pushed SYN_STREAM,
+  // Run until we've received the primary HEADERS, the pushed HEADERS,
   // the first HEADERS frame, and the body of the primary stream, but before
   // we've received the final HEADERS for the pushed stream.
   data.RunUntilPaused();
@@ -4570,14 +4568,14 @@
   // Verify that the received push data is same as the expected push data.
   EXPECT_EQ(expected_push_result, result2);
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   // Copy the response info, because trans goes away.
   response = *trans->GetResponseInfo();
   response2 = *trans2->GetResponseInfo();
 
   VerifyStreamsClosed(helper);
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   EXPECT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
 
@@ -4607,7 +4605,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
 
   SpdyHeaderBlock initial_headers;
   spdy_util_.AddUrlToHeaderBlock(GetDefaultUrlWithPath("/foo.dat"),
@@ -4647,7 +4645,7 @@
   int rv = trans->Start(
       &CreateGetRequest(), callback.callback(), BoundNetLog());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
-  // Run until we've received the primary SYN_STREAM, the pushed SYN_STREAM,
+  // Run until we've received the primary HEADERS, the pushed HEADERS,
   // the first HEADERS frame, and the body of the primary stream, but before
   // we've received the final HEADERS for the pushed stream.
   data.RunUntilPaused();
@@ -4675,13 +4673,13 @@
   // Verify that we haven't received any push data.
   EXPECT_EQ("", result2);
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   // Copy the response info, because trans goes away.
   HttpResponseInfo response = *trans->GetResponseInfo();
 
   VerifyStreamsClosed(helper);
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   EXPECT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
 
@@ -4694,7 +4692,7 @@
   EXPECT_TRUE(data.AllWriteDataConsumed());
 }
 
-TEST_F(SpdyNetworkTransactionTest, SynReplyWithHeaders) {
+TEST_F(SpdyNetworkTransactionTest, ResponseHeadersTwice) {
   SpdySerializedFrame req(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
   SpdySerializedFrame rst(
@@ -4704,7 +4702,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
 
   SpdyHeaderBlock late_headers;
   late_headers["hello"] = "bye";
@@ -4737,7 +4735,7 @@
   };
 
   SpdySerializedFrame stream1_reply(
-      spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame stream1_body(spdy_util_.ConstructSpdyDataFrame(1, false));
 
   SpdyHeaderBlock late_headers;
@@ -4797,7 +4795,7 @@
     };
 
     SpdySerializedFrame stream1_reply(
-        spdy_test_util.ConstructSpdyGetSynReply(nullptr, 0, 1));
+        spdy_test_util.ConstructSpdyGetReply(nullptr, 0, 1));
     SpdySerializedFrame stream2_syn(
         spdy_test_util.ConstructSpdyPush(nullptr, 0, 2, 1, url_to_push));
     const char kPushedData[] = "pushed";
@@ -4852,13 +4850,13 @@
     EXPECT_TRUE(data.AllReadDataConsumed());
     EXPECT_TRUE(data.AllWriteDataConsumed());
 
-    // Verify the SYN_REPLY.
+    // Verify the response headers.
     // Copy the response info, because trans goes away.
     response = *trans->GetResponseInfo();
 
     VerifyStreamsClosed(helper);
 
-    // Verify the SYN_REPLY.
+    // Verify the response headers.
     EXPECT_TRUE(response.headers);
     EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
   }
@@ -4877,7 +4875,7 @@
       CreateMockWrite(headers, 0),
   };
 
-  SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame push(
       spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, url_to_push));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
@@ -4970,8 +4968,7 @@
       CreateMockWrite(headers0, 0),
   };
 
-  SpdySerializedFrame reply0(
-      spdy_util_0.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame reply0(spdy_util_0.ConstructSpdyGetReply(nullptr, 0, 1));
   const char kData0[] = "first";
   SpdySerializedFrame body0(
       spdy_util_0.ConstructSpdyDataFrame(1, kData0, strlen(kData0), true));
@@ -4989,8 +4986,7 @@
       CreateMockWrite(headers1, 0),
   };
 
-  SpdySerializedFrame reply1(
-      spdy_util_1.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame reply1(spdy_util_1.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame push(
       spdy_util_1.ConstructSpdyPush(nullptr, 0, 2, 1, url_to_push));
   const char kData1[] = "second";
@@ -5137,7 +5133,7 @@
       CreateMockWrite(headers, 0), CreateMockWrite(rst, 3),
   };
 
-  SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame push(
       spdy_util_.ConstructSpdyPush(nullptr, 0, 2, 1, url_to_push));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
@@ -5181,7 +5177,7 @@
 
   SpdySerializedFrame refused(
       spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_REFUSED_STREAM));
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(3, true));
   MockRead reads[] = {
       CreateMockRead(refused, 1), CreateMockRead(resp, 3),
@@ -5209,13 +5205,13 @@
   EXPECT_TRUE(data.AllReadDataConsumed());
   EXPECT_TRUE(data.AllWriteDataConsumed());
 
-  // Verify the SYN_REPLY.
+  // Verify the response headers.
   HttpResponseInfo response = *trans->GetResponseInfo();
   EXPECT_TRUE(response.headers);
   EXPECT_EQ("HTTP/1.1 200", response.headers->GetStatusLine());
 }
 
-TEST_F(SpdyNetworkTransactionTest, OutOfOrderSynStream) {
+TEST_F(SpdyNetworkTransactionTest, OutOfOrderHeaders) {
   // This first request will start to establish the SpdySession.
   // Then we will start the second (MEDIUM priority) and then third
   // (HIGHEST priority) request in such a way that the third will actually
@@ -5244,11 +5240,11 @@
       CreateMockWrite(req2, 5), CreateMockWrite(req3, 6),
   };
 
-  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
-  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
+  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(NULL, 0, 3));
   SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
-  SpdySerializedFrame resp3(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 5));
+  SpdySerializedFrame resp3(spdy_util_.ConstructSpdyGetReply(NULL, 0, 5));
   SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(5, true));
   MockRead reads[] = {
       CreateMockRead(resp1, 2),  MockRead(ASYNC, ERR_IO_PENDING, 3),
@@ -5292,7 +5288,7 @@
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
   base::RunLoop().RunUntilIdle();
 
-  // We now have two SYN_STREAM frames queued up which will be
+  // We now have two HEADERS frames queued up which will be
   // dequeued only once the first write completes, which we
   // now allow to happen.
   ASSERT_TRUE(data.IsPaused());
@@ -5308,8 +5304,6 @@
   helper.VerifyDataConsumed();
 }
 
-// The tests below are only for SPDY/3 and above.
-
 // Test that sent data frames and received WINDOW_UPDATE frames change
 // the send_window_size_ correctly.
 
@@ -5320,7 +5314,7 @@
 // stream is created, succeeds and schedules another read.  This way reads
 // and writes are interleaved; after doing a full frame write, SpdyStream
 // will break out of DoLoop and will read and process a WINDOW_UPDATE.
-// Once our WINDOW_UPDATE is read, we cannot send SYN_REPLY right away
+// Once our WINDOW_UPDATE is read, we cannot send HEADERS right away
 // since request has not been completely written, therefore we feed
 // enough number of WINDOW_UPDATEs to finish the first read and cause a
 // write, leading to a complete write of request body; after that we send
@@ -5353,7 +5347,7 @@
       spdy_util_.ConstructSpdyWindowUpdate(1, kDeltaWindowSize));
   SpdySerializedFrame window_update_dummy(
       spdy_util_.ConstructSpdyWindowUpdate(2, kDeltaWindowSize));
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(NULL, 0));
   MockRead reads[] = {
       CreateMockRead(window_update_dummy, 3),
       CreateMockRead(window_update_dummy, 4),
@@ -5471,7 +5465,7 @@
   writes.push_back(CreateMockWrite(req, writes.size()));
 
   std::vector<MockRead> reads;
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   reads.push_back(CreateMockRead(resp, writes.size() + reads.size()));
 
   std::vector<SpdySerializedFrame> body_frames;
@@ -5705,7 +5699,7 @@
     writes.push_back(CreateMockWrite(body4, i++));
   writes.push_back(CreateMockWrite(body5, i++));
 
-  SpdySerializedFrame reply(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
+  SpdySerializedFrame reply(spdy_util_.ConstructSpdyPostReply(NULL, 0));
   reads.push_back(CreateMockRead(reply, i++));
   reads.push_back(CreateMockRead(body2, i++));
   reads.push_back(CreateMockRead(body5, i++));
@@ -5858,7 +5852,7 @@
     writes.push_back(CreateMockWrite(body4, i++));
   writes.push_back(CreateMockWrite(body5, i++));
 
-  SpdySerializedFrame reply(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
+  SpdySerializedFrame reply(spdy_util_.ConstructSpdyPostReply(NULL, 0));
   reads.push_back(CreateMockRead(reply, i++));
   reads.push_back(CreateMockRead(body2, i++));
   reads.push_back(CreateMockRead(body5, i++));
@@ -6022,7 +6016,7 @@
     writes.push_back(CreateMockWrite(body4, i++));
   writes.push_back(CreateMockWrite(body5, i++));
 
-  SpdySerializedFrame reply(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
+  SpdySerializedFrame reply(spdy_util_.ConstructSpdyPostReply(NULL, 0));
   reads.push_back(CreateMockRead(reply, i++));
   reads.push_back(CreateMockRead(body2, i++));
   reads.push_back(CreateMockRead(body5, i++));
@@ -6149,12 +6143,12 @@
   SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
   headers[kKey] = kValue;
   SpdySerializedFrame req(
-      spdy_util_.ConstructSpdySyn(1, std::move(headers), LOWEST, true));
+      spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true));
   MockWrite writes[] = {
       CreateMockWrite(req, 0),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {
       CreateMockRead(resp, 1), CreateMockRead(body, 2),
@@ -6176,7 +6170,7 @@
 TEST_F(SpdyNetworkTransactionTest, LargeResponseHeader) {
   SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
   SpdySerializedFrame req(
-      spdy_util_.ConstructSpdySyn(1, std::move(headers), LOWEST, true));
+      spdy_util_.ConstructSpdyHeaders(1, std::move(headers), LOWEST, true));
   MockWrite writes[] = {
       CreateMockWrite(req, 0),
   };
@@ -6189,7 +6183,7 @@
   response_headers[1] = kValue.data();
 
   SpdySerializedFrame resp(
-      spdy_util_.ConstructSpdyGetSynReply(response_headers, 1, 1));
+      spdy_util_.ConstructSpdyGetReply(response_headers, 1, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {
       CreateMockRead(resp, 1), CreateMockRead(body, 2),
diff --git a/net/spdy/spdy_proxy_client_socket.cc b/net/spdy/spdy_proxy_client_socket.cc
index ecc2912..d33f59a 100644
--- a/net/spdy/spdy_proxy_client_socket.cc
+++ b/net/spdy/spdy_proxy_client_socket.cc
@@ -101,9 +101,9 @@
       redirect_has_load_timing_info_ ? &redirect_load_timing_info_ : NULL);
 }
 
-// Sends a SYN_STREAM frame to the proxy with a CONNECT request
+// Sends a HEADERS frame to the proxy with a CONNECT request
 // for the specified endpoint.  Waits for the server to send back
-// a SYN_REPLY frame.  OK will be returned if the status is 200.
+// a HEADERS frame.  OK will be returned if the status is 200.
 // ERR_TUNNEL_CONNECTION_FAILED will be returned for any other status.
 // In any of these cases, Read() may be called to retrieve the HTTP
 // response body.  Any other return values should be considered fatal.
@@ -376,14 +376,14 @@
   if (result < 0)
     return result;
 
-  // Wait for SYN_REPLY frame from the server
+  // Wait for HEADERS frame from the server
   next_state_ = STATE_READ_REPLY_COMPLETE;
   return ERR_IO_PENDING;
 }
 
 int SpdyProxyClientSocket::DoReadReplyComplete(int result) {
   // We enter this method directly from DoSendRequestComplete, since
-  // we are notified by a callback when the SYN_REPLY frame arrives
+  // we are notified by a callback when the HEADERS frame arrives.
 
   if (result < 0)
     return result;
diff --git a/net/spdy/spdy_proxy_client_socket.h b/net/spdy/spdy_proxy_client_socket.h
index 09c10e4..4d8baa8 100644
--- a/net/spdy/spdy_proxy_client_socket.h
+++ b/net/spdy/spdy_proxy_client_socket.h
@@ -39,10 +39,10 @@
 class NET_EXPORT_PRIVATE SpdyProxyClientSocket : public ProxyClientSocket,
                                                  public SpdyStream::Delegate {
  public:
-  // Create a socket on top of the |spdy_stream| by sending a SYN_STREAM
-  // CONNECT frame for |endpoint|.  After the SYN_REPLY is received,
-  // any data read/written to the socket will be transferred in data
-  // frames. This object will set itself as |spdy_stream|'s delegate.
+  // Create a socket on top of the |spdy_stream| by sending a HEADERS CONNECT
+  // frame for |endpoint|.  After the response HEADERS frame is received, any
+  // data read/written to the socket will be transferred in data frames. This
+  // object will set itself as |spdy_stream|'s delegate.
   SpdyProxyClientSocket(const base::WeakPtr<SpdyStream>& spdy_stream,
                         const std::string& user_agent,
                         const HostPortPair& endpoint,
diff --git a/net/spdy/spdy_proxy_client_socket_unittest.cc b/net/spdy/spdy_proxy_client_socket_unittest.cc
index b2cdcbd..0267264 100644
--- a/net/spdy/spdy_proxy_client_socket_unittest.cc
+++ b/net/spdy/spdy_proxy_client_socket_unittest.cc
@@ -298,33 +298,33 @@
   (*block)[spdy_util_.GetStatusKey()] = status;
 }
 
-// Constructs a standard SPDY SYN_STREAM frame for a CONNECT request.
+// Constructs a standard SPDY HEADERS frame for a CONNECT request.
 SpdySerializedFrame SpdyProxyClientSocketTest::ConstructConnectRequestFrame() {
   SpdyHeaderBlock block;
   PopulateConnectRequestIR(&block);
-  return spdy_util_.ConstructSpdySyn(kStreamId, std::move(block), LOWEST,
-                                     false);
+  return spdy_util_.ConstructSpdyHeaders(kStreamId, std::move(block), LOWEST,
+                                         false);
 }
 
-// Constructs a SPDY SYN_STREAM frame for a CONNECT request which includes
+// Constructs a SPDY HEADERS frame for a CONNECT request which includes
 // Proxy-Authorization headers.
 SpdySerializedFrame
 SpdyProxyClientSocketTest::ConstructConnectAuthRequestFrame() {
   SpdyHeaderBlock block;
   PopulateConnectRequestIR(&block);
   block["proxy-authorization"] = "Basic Zm9vOmJhcg==";
-  return spdy_util_.ConstructSpdySyn(kStreamId, std::move(block), LOWEST,
-                                     false);
+  return spdy_util_.ConstructSpdyHeaders(kStreamId, std::move(block), LOWEST,
+                                         false);
 }
 
-// Constructs a standard SPDY SYN_REPLY frame to match the SPDY CONNECT.
+// Constructs a standard SPDY HEADERS frame to match the SPDY CONNECT.
 SpdySerializedFrame SpdyProxyClientSocketTest::ConstructConnectReplyFrame() {
   SpdyHeaderBlock block;
   PopulateConnectReplyIR(&block, "200");
   return spdy_util_.ConstructSpdyReply(kStreamId, std::move(block));
 }
 
-// Constructs a standard SPDY SYN_REPLY frame to match the SPDY CONNECT,
+// Constructs a standard SPDY HEADERS frame to match the SPDY CONNECT,
 // including Proxy-Authenticate headers.
 SpdySerializedFrame
 SpdyProxyClientSocketTest::ConstructConnectAuthReplyFrame() {
@@ -334,7 +334,7 @@
   return spdy_util_.ConstructSpdyReply(kStreamId, std::move(block));
 }
 
-// Constructs a SPDY SYN_REPLY frame with an HTTP 302 redirect.
+// Constructs a SPDY HEADERS frame with an HTTP 302 redirect.
 SpdySerializedFrame
 SpdyProxyClientSocketTest::ConstructConnectRedirectReplyFrame() {
   SpdyHeaderBlock block;
@@ -344,7 +344,7 @@
   return spdy_util_.ConstructSpdyReply(kStreamId, std::move(block));
 }
 
-// Constructs a SPDY SYN_REPLY frame with an HTTP 500 error.
+// Constructs a SPDY HEADERS frame with an HTTP 500 error.
 SpdySerializedFrame
 SpdyProxyClientSocketTest::ConstructConnectErrorReplyFrame() {
   SpdyHeaderBlock block;
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index 49338ac..37a466b 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -83,7 +83,7 @@
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogSpdySynReplyOrHeadersReceivedCallback(
+std::unique_ptr<base::Value> NetLogSpdyHeadersReceivedCallback(
     const SpdyHeaderBlock* headers,
     bool fin,
     SpdyStreamId stream_id,
@@ -518,13 +518,11 @@
 }
 
 SpdySession::ActiveStreamInfo::ActiveStreamInfo()
-    : stream(NULL),
-      waiting_for_syn_reply(false) {}
+    : stream(NULL), waiting_for_reply_headers_frame(false) {}
 
 SpdySession::ActiveStreamInfo::ActiveStreamInfo(SpdyStream* stream)
     : stream(stream),
-      waiting_for_syn_reply(stream->type() != SPDY_PUSH_STREAM) {
-}
+      waiting_for_reply_headers_frame(stream->type() != SPDY_PUSH_STREAM) {}
 
 SpdySession::ActiveStreamInfo::~ActiveStreamInfo() {}
 
@@ -990,13 +988,11 @@
     const base::WeakPtr<SpdyStream>& stream,
     SpdyFrameType frame_type,
     std::unique_ptr<SpdyBufferProducer> producer) {
-  DCHECK(frame_type == HEADERS ||
-         frame_type == DATA ||
-         frame_type == SYN_STREAM);
+  DCHECK(frame_type == HEADERS || frame_type == DATA);
   EnqueueWrite(stream->priority(), frame_type, std::move(producer), stream);
 }
 
-std::unique_ptr<SpdySerializedFrame> SpdySession::CreateSynStream(
+std::unique_ptr<SpdySerializedFrame> SpdySession::CreateHeaders(
     SpdyStreamId stream_id,
     RequestPriority priority,
     SpdyControlFlags flags,
@@ -1468,9 +1464,9 @@
     if (stream.get())
       CHECK(!stream->IsClosed());
 
-    // Activate the stream only when sending the SYN_STREAM frame to
+    // Activate the stream only when sending the HEADERS frame to
     // guarantee monotonically-increasing stream IDs.
-    if (frame_type == SYN_STREAM) {
+    if (frame_type == HEADERS) {
       CHECK(stream.get());
       CHECK_EQ(stream->stream_id(), 0u);
       std::unique_ptr<SpdyStream> owned_stream =
@@ -2037,8 +2033,8 @@
 
   stream->AddRawReceivedBytes(len);
 
-  if (it->second.waiting_for_syn_reply) {
-    const std::string& error = "Data received before SYN_REPLY.";
+  if (it->second.waiting_for_reply_headers_frame) {
+    const std::string& error = "DATA received before HEADERS.";
     stream->LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
     ResetStreamIterator(it, RST_STREAM_PROTOCOL_ERROR, error);
     return;
@@ -2070,8 +2066,8 @@
   SpdyStream* stream = it->second.stream;
   CHECK_EQ(stream->stream_id(), stream_id);
 
-  if (it->second.waiting_for_syn_reply) {
-    const std::string& error = "Data received before SYN_REPLY.";
+  if (it->second.waiting_for_reply_headers_frame) {
+    const std::string& error = "DATA received before HEADERS.";
     stream->LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error);
     ResetStreamIterator(it, RST_STREAM_PROTOCOL_ERROR, error);
     return;
@@ -2136,7 +2132,7 @@
     SpdyFrameType type,
     size_t payload_len,
     size_t frame_len) {
-  if (type != SYN_STREAM && type != HEADERS)
+  if (type != HEADERS)
     return;
 
   DCHECK(buffered_spdy_framer_.get());
@@ -2241,8 +2237,8 @@
 
   if (net_log().IsCapturing()) {
     net_log().AddEvent(NetLog::TYPE_HTTP2_SESSION_RECV_HEADERS,
-                       base::Bind(&NetLogSpdySynReplyOrHeadersReceivedCallback,
-                                  &headers, fin, stream_id));
+                       base::Bind(&NetLogSpdyHeadersReceivedCallback, &headers,
+                                  fin, stream_id));
   }
 
   ActiveStreamMap::iterator it = active_streams_.find(stream_id);
@@ -2261,8 +2257,8 @@
   base::Time response_time = base::Time::Now();
   base::TimeTicks recv_first_byte_time = time_func_();
 
-  if (it->second.waiting_for_syn_reply) {
-    it->second.waiting_for_syn_reply = false;
+  if (it->second.waiting_for_reply_headers_frame) {
+    it->second.waiting_for_reply_headers_frame = false;
     ignore_result(OnInitialResponseHeadersReceived(
         headers, response_time, recv_first_byte_time, stream));
   } else if (it->second.stream->IsReservedRemote()) {
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index c942d4b..20652c55c 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -362,11 +362,11 @@
                           SpdyFrameType frame_type,
                           std::unique_ptr<SpdyBufferProducer> producer);
 
-  // Creates and returns a SYN frame for |stream_id|.
-  std::unique_ptr<SpdySerializedFrame> CreateSynStream(SpdyStreamId stream_id,
-                                                       RequestPriority priority,
-                                                       SpdyControlFlags flags,
-                                                       SpdyHeaderBlock headers);
+  // Creates and returns a HEADERS frame for |stream_id|.
+  std::unique_ptr<SpdySerializedFrame> CreateHeaders(SpdyStreamId stream_id,
+                                                     RequestPriority priority,
+                                                     SpdyControlFlags flags,
+                                                     SpdyHeaderBlock headers);
 
   // Creates and returns a SpdyBuffer holding a data frame with the
   // given data. May return NULL if stalled by flow control.
@@ -605,7 +605,7 @@
     ~ActiveStreamInfo();
 
     SpdyStream* stream;
-    bool waiting_for_syn_reply;
+    bool waiting_for_reply_headers_frame;
   };
   typedef std::map<SpdyStreamId, ActiveStreamInfo> ActiveStreamMap;
 
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index b5ed654..bc8a5d9 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -604,7 +604,7 @@
       CreateMockWrite(req, 0),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway(1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   size_t joint_size = goaway.size() * 2 + body.size();
@@ -705,9 +705,9 @@
   EXPECT_FALSE(session_);
 }
 
-// Receiving a SYN_STREAM frame after a GOAWAY frame should result in
+// Receiving a HEADERS frame after a GOAWAY frame should result in
 // the stream being refused.
-TEST_F(SpdySessionTest, SynStreamAfterGoAway) {
+TEST_F(SpdySessionTest, HeadersAfterGoAway) {
   session_deps_.host_resolver->set_synchronous_mode(true);
 
   SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway(1));
@@ -750,7 +750,7 @@
   EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_));
   EXPECT_TRUE(session_->IsStreamActive(1));
 
-  // Read and process the SYN_STREAM frame, the subsequent RST_STREAM,
+  // Read and process the HEADERS frame, the subsequent RST_STREAM,
   // and EOF.
   data.Resume();
   base::RunLoop().RunUntilIdle();
@@ -967,9 +967,9 @@
   };
 
   SpdySerializedFrame resp1(
-      spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, kLastStreamId - 2));
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, kLastStreamId - 2));
   SpdySerializedFrame resp2(
-      spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, kLastStreamId));
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, kLastStreamId));
 
   SpdySerializedFrame body1(
       spdy_util_.ConstructSpdyDataFrame(kLastStreamId - 2, true));
@@ -1087,7 +1087,7 @@
   SpdySerializedFrame req(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, MEDIUM, true));
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
 
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
 
@@ -1262,7 +1262,7 @@
             session_->session_recv_window_size_);
   EXPECT_EQ(0, session_->session_unacked_recv_window_bytes_);
 
-  // Shift time to expire the push stream. Read the second SYN_STREAM,
+  // Shift time to expire the push stream. Read the second HEADERS,
   // and verify a RST_STREAM was written.
   g_time_delta = base::TimeDelta::FromSeconds(301);
   data.Resume();
@@ -1679,10 +1679,10 @@
   EXPECT_FALSE(session_);
 }
 
-// Queue up a low-priority SYN_STREAM followed by a high-priority
+// Queue up a low-priority HEADERS followed by a high-priority
 // one. The high priority one should still send first and receive
 // first.
-TEST_F(SpdySessionTest, OutOfOrderSynStreams) {
+TEST_F(SpdySessionTest, OutOfOrderHeaders) {
   // Construct the request.
   SpdySerializedFrame req_highest(
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, HIGHEST, true));
@@ -1693,10 +1693,10 @@
   };
 
   SpdySerializedFrame resp_highest(
-      spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame body_highest(spdy_util_.ConstructSpdyDataFrame(1, true));
   SpdySerializedFrame resp_lowest(
-      spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 3));
+      spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
   SpdySerializedFrame body_lowest(spdy_util_.ConstructSpdyDataFrame(3, true));
   MockRead reads[] = {
       CreateMockRead(resp_highest, 2), CreateMockRead(body_highest, 3),
@@ -1756,7 +1756,7 @@
       CreateMockWrite(req2, 0),
   };
 
-  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {
       CreateMockRead(resp2, 1), MockRead(ASYNC, ERR_IO_PENDING, 2),
@@ -2220,13 +2220,13 @@
   SpdySerializedFrame settings_frame(
       spdy_util_.ConstructSpdySettings(new_settings));
 
-  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
 
-  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 3));
+  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
   SpdySerializedFrame body2(spdy_util_.ConstructSpdyDataFrame(3, true));
 
-  SpdySerializedFrame resp3(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 5));
+  SpdySerializedFrame resp3(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 5));
   SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(5, true));
 
   MockRead reads[] = {
@@ -2439,7 +2439,7 @@
   SpdySerializedFrame finish_data_frame(spdy_util_.ConstructSpdyDataFrame(
       1, payload_data, kPayloadSize - 1, /*fin=*/true));
 
-  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
 
   // Write 1 byte less than kMaxReadBytes to check that DoRead reads up to 32k
   // bytes.
@@ -2506,7 +2506,7 @@
       CreateMockWrite(req1, 0),
   };
 
-  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
 
   MockRead reads[] = {
       CreateMockRead(resp1, 1), MockRead(ASYNC, 0, 2)  // EOF
@@ -2567,7 +2567,7 @@
   SpdySerializedFrame finish_data_frame(
       spdy_util_.ConstructSpdyDataFrame(1, "bar", 3, /*fin=*/true));
 
-  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
 
   MockRead reads[] = {
       CreateMockRead(resp1, 1),
@@ -2643,7 +2643,7 @@
   SpdySerializedFrame finish_data_frame(
       spdy_util_.ConstructSpdyDataFrame(1, "h", 1, /*fin=*/true));
 
-  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
 
   // Write 1 byte more than kMaxReadBytes to check that DoRead yields.
   MockRead reads[] = {
@@ -2743,7 +2743,7 @@
   SpdySerializedFrame finish_data_frame(
       spdy_util_.ConstructSpdyDataFrame(1, "h", 1, /*fin=*/true));
 
-  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
 
   MockRead reads[] = {
       CreateMockRead(resp1, 1),
@@ -2813,7 +2813,7 @@
       CreateMockWrite(req1, 0),
   };
 
-  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true));
   SpdySerializedFrame goaway(spdy_util_.ConstructSpdyGoAway());
 
@@ -3411,7 +3411,7 @@
       CreateMockWrite(req, 0), CreateMockWrite(rst, 4),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   const std::string payload(data_frame_size, 'a');
   SpdySerializedFrame data_frame(spdy_util_.ConstructSpdyDataFrame(
       1, payload.data(), data_frame_size, false));
@@ -3539,7 +3539,7 @@
       CreateMockWrite(req, 0), CreateMockWrite(rst, 6),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   const std::string first_data_frame(first_data_frame_size, 'a');
   SpdySerializedFrame first(spdy_util_.ConstructSpdyDataFrame(
       1, first_data_frame.data(), first_data_frame_size, false));
@@ -3628,7 +3628,7 @@
       CreateMockWrite(req, 0), CreateMockWrite(msg, 2),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame echo(spdy_util_.ConstructSpdyDataFrame(
       1, msg_data.data(), kMsgDataSize, false));
   SpdySerializedFrame window_update(spdy_util_.ConstructSpdyWindowUpdate(
@@ -3694,7 +3694,7 @@
       CreateMockWrite(req, 0),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   MockRead reads[] = {
       MockRead(ASYNC, ERR_IO_PENDING, 1), CreateMockRead(resp, 2),
       MockRead(ASYNC, 0, 3)  // EOF
@@ -3765,7 +3765,7 @@
       CreateMockWrite(req, 0), CreateMockWrite(msg, 2),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame echo(spdy_util_.ConstructSpdyDataFrame(
       1, msg_data.data(), kMsgDataSize, false));
   SpdySerializedFrame window_update(spdy_util_.ConstructSpdyWindowUpdate(
@@ -3870,7 +3870,7 @@
       CreateMockWrite(req, 0), CreateMockWrite(body, 1),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame echo(
       spdy_util_.ConstructSpdyDataFrame(1, kBodyData, kBodyDataSize, false));
   MockRead reads[] = {
@@ -3989,8 +3989,8 @@
       CreateMockWrite(body2, 2), CreateMockWrite(body1, 3),
   };
 
-  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
-  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 3));
+  SpdySerializedFrame resp1(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
+  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
   MockRead reads[] = {
       CreateMockRead(resp1, 4), CreateMockRead(resp2, 5),
       MockRead(ASYNC, 0, 6)  // EOF
@@ -4119,7 +4119,7 @@
       CreateMockWrite(req3, 2), CreateMockWrite(body2, 3),
   };
 
-  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 3));
+  SpdySerializedFrame resp2(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3));
   MockRead reads[] = {
       CreateMockRead(resp2, 4), MockRead(ASYNC, ERR_IO_PENDING, 5),
       MockRead(ASYNC, 0, 6)  // EOF
@@ -4336,7 +4336,7 @@
       CreateMockWrite(req, 0), CreateMockWrite(goaway, 4),
   };
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(nullptr, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
   SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true));
   MockRead reads[] = {
       MockRead(ASYNC, ERR_IO_PENDING, 1), CreateMockRead(resp, 2),
diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc
index 35b84f6..fd0e31d 100644
--- a/net/spdy/spdy_stream.cc
+++ b/net/spdy/spdy_stream.cc
@@ -58,15 +58,15 @@
 
 void SpdyStream::Delegate::OnTrailers(const SpdyHeaderBlock& trailers) {}
 
-// A wrapper around a stream that calls into ProduceSynStreamFrame().
-class SpdyStream::SynStreamBufferProducer : public SpdyBufferProducer {
+// A wrapper around a stream that calls into ProduceHeadersFrame().
+class SpdyStream::HeadersBufferProducer : public SpdyBufferProducer {
  public:
-  SynStreamBufferProducer(const base::WeakPtr<SpdyStream>& stream)
+  HeadersBufferProducer(const base::WeakPtr<SpdyStream>& stream)
       : stream_(stream) {
     DCHECK(stream_.get());
   }
 
-  ~SynStreamBufferProducer() override {}
+  ~HeadersBufferProducer() override {}
 
   std::unique_ptr<SpdyBuffer> ProduceBuffer() override {
     if (!stream_.get()) {
@@ -75,7 +75,7 @@
     }
     DCHECK_GT(stream_->stream_id(), 0u);
     return std::unique_ptr<SpdyBuffer>(
-        new SpdyBuffer(stream_->ProduceSynStreamFrame()));
+        new SpdyBuffer(stream_->ProduceHeadersFrame()));
   }
 
  private:
@@ -200,7 +200,7 @@
   }
 }
 
-std::unique_ptr<SpdySerializedFrame> SpdyStream::ProduceSynStreamFrame() {
+std::unique_ptr<SpdySerializedFrame> SpdyStream::ProduceHeadersFrame() {
   CHECK_EQ(io_state_, STATE_IDLE);
   CHECK(request_headers_valid_);
   CHECK_GT(stream_id_, 0u);
@@ -208,7 +208,7 @@
   SpdyControlFlags flags =
       (pending_send_status_ == NO_MORE_DATA_TO_SEND) ?
       CONTROL_FLAG_FIN : CONTROL_FLAG_NONE;
-  std::unique_ptr<SpdySerializedFrame> frame(session_->CreateSynStream(
+  std::unique_ptr<SpdySerializedFrame> frame(session_->CreateHeaders(
       stream_id_, priority_, flags, std::move(request_headers_)));
   request_headers_valid_ = false;
   send_time_ = base::TimeTicks::Now();
@@ -565,11 +565,10 @@
 void SpdyStream::OnFrameWriteComplete(SpdyFrameType frame_type,
                                       size_t frame_size) {
   DCHECK_NE(type_, SPDY_PUSH_STREAM);
-  CHECK(frame_type == SYN_STREAM ||
-        frame_type == DATA) << frame_type;
+  CHECK(frame_type == HEADERS || frame_type == DATA) << frame_type;
 
-  int result = (frame_type == SYN_STREAM) ?
-      OnRequestHeadersSent() : OnDataSent(frame_size);
+  int result =
+      (frame_type == HEADERS) ? OnRequestHeadersSent() : OnDataSent(frame_size);
   if (result == ERR_IO_PENDING) {
     // The write operation hasn't completed yet.
     return;
@@ -589,7 +588,7 @@
   {
     base::WeakPtr<SpdyStream> weak_this = GetWeakPtr();
     write_handler_guard_ = true;
-    if (frame_type == SYN_STREAM) {
+    if (frame_type == HEADERS) {
       delegate_->OnRequestHeadersSent();
     } else {
       delegate_->OnDataSent();
@@ -695,9 +694,9 @@
   request_headers_valid_ = true;
   url_from_header_block_ = GetUrlFromHeaderBlock(request_headers_);
   pending_send_status_ = send_status;
-  session_->EnqueueStreamWrite(GetWeakPtr(), SYN_STREAM,
+  session_->EnqueueStreamWrite(GetWeakPtr(), HEADERS,
                                std::unique_ptr<SpdyBufferProducer>(
-                                   new SynStreamBufferProducer(GetWeakPtr())));
+                                   new HeadersBufferProducer(GetWeakPtr())));
   return ERR_IO_PENDING;
 }
 
diff --git a/net/spdy/spdy_stream.h b/net/spdy/spdy_stream.h
index d96e5e9..912def1 100644
--- a/net/spdy/spdy_stream.h
+++ b/net/spdy/spdy_stream.h
@@ -291,10 +291,9 @@
   base::Time GetRequestTime() const;
   void SetRequestTime(base::Time t);
 
-  // Called at most once by the SpdySession when the initial response
-  // headers have been received for this stream, i.e., a SYN_REPLY (or
-  // SYN_STREAM for push streams) frame has been received. Returns a status
-  // code; if it is an error, the stream was closed by this function.
+  // Called at most once by the SpdySession when the initial response headers
+  // have been received for this stream. Returns a status code; if it is an
+  // error, the stream was closed by this function.
   int OnInitialResponseHeadersReceived(const SpdyHeaderBlock& response_headers,
                                        base::Time response_time,
                                        base::TimeTicks recv_first_byte_time);
@@ -333,7 +332,7 @@
   // of the HEADERS or PUSH_PROMISE frame and subsequent CONTINUATION frames.
   void OnFrameWriteComplete(SpdyFrameType frame_type, size_t frame_size);
 
-  // SYN_STREAM-specific write handler invoked by OnFrameWriteComplete().
+  // HEADERS-specific write handler invoked by OnFrameWriteComplete().
   int OnRequestHeadersSent();
 
   // DATA-specific write handler invoked by OnFrameWriteComplete().
@@ -432,14 +431,13 @@
   const GURL& GetUrlFromHeaders() const { return url_from_header_block_; }
 
  private:
-  class SynStreamBufferProducer;
-  class HeaderBufferProducer;
+  class HeadersBufferProducer;
 
   // SpdyStream states and transitions are modeled
   // on the HTTP/2 stream state machine. All states and transitions
   // are modeled, with the exceptions of RESERVED_LOCAL (the client
   // cannot initate push streams), and the transition to OPEN due to
-  // a remote SYN_STREAM (the client can only initate streams).
+  // a remote HEADERS (the client can only initate streams).
   enum State {
     STATE_IDLE,
     STATE_OPEN,
@@ -463,9 +461,9 @@
   // to have occurred, driving the state machine forward.
   void PushedStreamReplay();
 
-  // Produces the SYN_STREAM frame for the stream. The stream must
+  // Produces the HEADERS frame for the stream. The stream must
   // already be activated.
-  std::unique_ptr<SpdySerializedFrame> ProduceSynStreamFrame();
+  std::unique_ptr<SpdySerializedFrame> ProduceHeadersFrame();
 
   // Queues the send for next frame of the remaining data in
   // |pending_send_data_|. Must be called only when
diff --git a/net/spdy/spdy_stream_unittest.cc b/net/spdy/spdy_stream_unittest.cc
index 6257f2c0..0776b62 100644
--- a/net/spdy/spdy_stream_unittest.cc
+++ b/net/spdy/spdy_stream_unittest.cc
@@ -138,7 +138,7 @@
       kStreamUrl, 1, kPostBodyLength, LOWEST, NULL, 0));
   AddWrite(req);
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(NULL, 0));
   AddRead(resp);
 
   SpdySerializedFrame msg(
@@ -216,7 +216,7 @@
       spdy_util_.ConstructSpdyDataFrame(1, kPostBody, kPostBodyLength, true));
   AddWrite(msg);
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(NULL, 0));
   AddRead(resp);
 
   SpdySerializedFrame echo(
@@ -339,7 +339,7 @@
       kStreamUrl, 1, kPostBodyLength, LOWEST, NULL, 0));
   AddWrite(req);
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   AddRead(resp);
 
   SpdySerializedFrame msg(
@@ -423,7 +423,7 @@
       1, chunk_data.data(), chunk_data.length(), true));
   AddWrite(last_chunk);
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(NULL, 0));
   AddRead(resp);
 
   AddReadEOF();
@@ -472,7 +472,7 @@
       kStreamUrl, 1, kPostBodyLength, LOWEST, NULL, 0));
   AddWrite(req);
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostSynReply(NULL, 0));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(NULL, 0));
   AddRead(resp);
 
   std::string chunk_data(kMaxSpdyFrameChunkSize, 'x');
@@ -529,7 +529,7 @@
 
   const char* const kExtraHeaders[] = {"X-UpperCase", "yes"};
   SpdySerializedFrame reply(
-      spdy_util_.ConstructSpdyGetSynReply(kExtraHeaders, 1, 1));
+      spdy_util_.ConstructSpdyGetReply(kExtraHeaders, 1, 1));
   AddRead(reply);
 
   SpdySerializedFrame rst(
@@ -574,7 +574,7 @@
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
   AddWrite(syn);
 
-  SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   AddRead(reply);
 
   const char* const extra_headers[] = {"X-UpperCase", "yes"};
@@ -634,7 +634,7 @@
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
   AddWrite(syn);
 
-  SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   AddRead(reply);
 
   SpdySerializedFrame push(
@@ -707,7 +707,7 @@
       spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST, true));
   AddWrite(syn);
 
-  SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   AddRead(reply);
 
   SpdySerializedFrame push(
@@ -874,7 +874,7 @@
       spdy_util_.ConstructSpdyDataFrame(1, kPostBody, kPostBodyLength, true));
   AddWrite(body);
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   AddRead(resp);
 
   AddReadEOF();
@@ -946,7 +946,7 @@
 
   AddReadPause();
 
-  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   AddRead(resp);
 
   SpdySerializedFrame msg(
@@ -1028,7 +1028,7 @@
 
   AddReadPause();
 
-  SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+  SpdySerializedFrame reply(spdy_util_.ConstructSpdyGetReply(NULL, 0, 1));
   AddRead(reply);
 
   AddReadPause();
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc
index e8302a9..757dc066 100644
--- a/net/spdy/spdy_test_util_common.cc
+++ b/net/spdy/spdy_test_util_common.cc
@@ -776,7 +776,8 @@
     SpdyStreamId stream_id,
     RequestPriority request_priority) {
   SpdyHeaderBlock block(ConstructGetHeaderBlock(url));
-  return ConstructSpdySyn(stream_id, std::move(block), request_priority, true);
+  return ConstructSpdyHeaders(stream_id, std::move(block), request_priority,
+                              true);
 }
 
 SpdySerializedFrame SpdyTestUtil::ConstructSpdyGet(
@@ -789,7 +790,8 @@
   block[GetMethodKey()] = "GET";
   AddUrlToHeaderBlock(default_url_.spec(), &block);
   AppendToHeaderBlock(extra_headers, extra_header_count, &block);
-  return ConstructSpdySyn(stream_id, std::move(block), request_priority, true);
+  return ConstructSpdyHeaders(stream_id, std::move(block), request_priority,
+                              true);
 }
 
 SpdySerializedFrame SpdyTestUtil::ConstructSpdyConnect(
@@ -802,7 +804,7 @@
   block[GetMethodKey()] = "CONNECT";
   block[GetHostKey()] = host_port_pair.ToString();
   AppendToHeaderBlock(extra_headers, extra_header_count, &block);
-  return ConstructSpdySyn(stream_id, std::move(block), priority, false);
+  return ConstructSpdyHeaders(stream_id, std::move(block), priority, false);
 }
 
 SpdySerializedFrame SpdyTestUtil::ConstructSpdyPush(
@@ -903,10 +905,10 @@
       response_spdy_framer_.SerializeFrame(spdy_headers));
 }
 
-SpdySerializedFrame SpdyTestUtil::ConstructSpdySyn(int stream_id,
-                                                   SpdyHeaderBlock block,
-                                                   RequestPriority priority,
-                                                   bool fin) {
+SpdySerializedFrame SpdyTestUtil::ConstructSpdyHeaders(int stream_id,
+                                                       SpdyHeaderBlock block,
+                                                       RequestPriority priority,
+                                                       bool fin) {
   // Get the stream id of the next highest priority request
   // (most recent request of the same priority, or last request of
   // an earlier priority).
@@ -941,7 +943,7 @@
   return SpdySerializedFrame(response_spdy_framer_.SerializeFrame(reply));
 }
 
-SpdySerializedFrame SpdyTestUtil::ConstructSpdySynReplyError(
+SpdySerializedFrame SpdyTestUtil::ConstructSpdyReplyError(
     const char* const status,
     const char* const* const extra_headers,
     int extra_header_count,
@@ -954,20 +956,19 @@
   return ConstructSpdyReply(stream_id, std::move(block));
 }
 
-SpdySerializedFrame SpdyTestUtil::ConstructSpdyGetSynReplyRedirect(
-    int stream_id) {
+SpdySerializedFrame SpdyTestUtil::ConstructSpdyGetReplyRedirect(int stream_id) {
   static const char* const kExtraHeaders[] = {
     "location", "http://www.foo.com/index.php",
   };
-  return ConstructSpdySynReplyError("301 Moved Permanently", kExtraHeaders,
-                                    arraysize(kExtraHeaders)/2, stream_id);
+  return ConstructSpdyReplyError("301 Moved Permanently", kExtraHeaders,
+                                 arraysize(kExtraHeaders) / 2, stream_id);
 }
 
-SpdySerializedFrame SpdyTestUtil::ConstructSpdySynReplyError(int stream_id) {
-  return ConstructSpdySynReplyError("500 Internal Server Error", NULL, 0, 1);
+SpdySerializedFrame SpdyTestUtil::ConstructSpdyReplyError(int stream_id) {
+  return ConstructSpdyReplyError("500 Internal Server Error", NULL, 0, 1);
 }
 
-SpdySerializedFrame SpdyTestUtil::ConstructSpdyGetSynReply(
+SpdySerializedFrame SpdyTestUtil::ConstructSpdyGetReply(
     const char* const extra_headers[],
     int extra_header_count,
     int stream_id) {
@@ -988,7 +989,7 @@
     int extra_header_count) {
   SpdyHeaderBlock block(ConstructPostHeaderBlock(url, content_length));
   AppendToHeaderBlock(extra_headers, extra_header_count, &block);
-  return ConstructSpdySyn(stream_id, std::move(block), priority, false);
+  return ConstructSpdyHeaders(stream_id, std::move(block), priority, false);
 }
 
 SpdySerializedFrame SpdyTestUtil::ConstructChunkedSpdyPost(
@@ -998,14 +999,14 @@
   block[GetMethodKey()] = "POST";
   AddUrlToHeaderBlock(default_url_.spec(), &block);
   AppendToHeaderBlock(extra_headers, extra_header_count, &block);
-  return ConstructSpdySyn(1, std::move(block), LOWEST, false);
+  return ConstructSpdyHeaders(1, std::move(block), LOWEST, false);
 }
 
-SpdySerializedFrame SpdyTestUtil::ConstructSpdyPostSynReply(
+SpdySerializedFrame SpdyTestUtil::ConstructSpdyPostReply(
     const char* const extra_headers[],
     int extra_header_count) {
   // TODO(jgraettinger): Remove this method.
-  return ConstructSpdyGetSynReply(extra_headers, extra_header_count, 1);
+  return ConstructSpdyGetReply(extra_headers, extra_header_count, 1);
 }
 
 SpdySerializedFrame SpdyTestUtil::ConstructSpdyDataFrame(int stream_id,
@@ -1101,9 +1102,4 @@
   return headers;
 }
 
-void SpdyTestUtil::SetPriority(RequestPriority priority,
-                               SpdySynStreamIR* ir) const {
-  ir->set_priority(ConvertRequestPriorityToSpdyPriority(priority));
-}
-
 }  // namespace net
diff --git a/net/spdy/spdy_test_util_common.h b/net/spdy/spdy_test_util_common.h
index 130baa1a..bdc822a4 100644
--- a/net/spdy/spdy_test_util_common.h
+++ b/net/spdy/spdy_test_util_common.h
@@ -337,7 +337,8 @@
   SpdySerializedFrame ConstructSpdyRstStream(SpdyStreamId stream_id,
                                              SpdyRstStreamStatus status);
 
-  // Constructs a standard SPDY GET SYN frame for |url| with header compression.
+  // Constructs a standard SPDY GET HEADERS frame for |url| with header
+  // compression.
   // |extra_headers| are the extra header-value pairs, which typically
   // will vary the most between calls.
   // Returns a SpdySerializedFrame.
@@ -345,7 +346,7 @@
                                        SpdyStreamId stream_id,
                                        RequestPriority request_priority);
 
-  // Constructs a standard SPDY GET SYN frame with header compression.
+  // Constructs a standard SPDY GET HEADERS frame with header compression.
   // |extra_headers| are the extra header-value pairs, which typically
   // will vary the most between calls.  If |direct| is false, the
   // the full url will be used instead of simply the path.
@@ -356,14 +357,14 @@
                                        RequestPriority request_priority,
                                        bool direct);
 
-  // Constructs a standard SPDY SYN_STREAM frame for a CONNECT request.
+  // Constructs a SPDY HEADERS frame for a CONNECT request.
   SpdySerializedFrame ConstructSpdyConnect(const char* const extra_headers[],
                                            int extra_header_count,
                                            int stream_id,
                                            RequestPriority priority,
                                            const HostPortPair& host_port_pair);
 
-  // Constructs a standard SPDY push SYN frame.
+  // Constructs a SPDY PUSH_PROMISE frame.
   // |extra_headers| are the extra header-value pairs, which typically
   // will vary the most between calls.
   // Returns a SpdySerializedFrame.
@@ -396,45 +397,44 @@
                                                    bool fin);
 
   // Construct a HEADERS frame carrying exactly the given headers and priority.
-  SpdySerializedFrame ConstructSpdySyn(int stream_id,
-                                       SpdyHeaderBlock headers,
-                                       RequestPriority priority,
-                                       bool fin);
+  SpdySerializedFrame ConstructSpdyHeaders(int stream_id,
+                                           SpdyHeaderBlock headers,
+                                           RequestPriority priority,
+                                           bool fin);
 
   // Construct a reply HEADERS frame carrying exactly the given headers and the
   // default priority.
   SpdySerializedFrame ConstructSpdyReply(int stream_id,
                                          SpdyHeaderBlock headers);
 
-  // Constructs a standard SPDY SYN_REPLY frame to match the SPDY GET.
+  // Constructs a standard SPDY HEADERS frame to match the SPDY GET.
   // |extra_headers| are the extra header-value pairs, which typically
   // will vary the most between calls.
   // Returns a SpdySerializedFrame.
-  SpdySerializedFrame ConstructSpdyGetSynReply(
-      const char* const extra_headers[],
-      int extra_header_count,
-      int stream_id);
+  SpdySerializedFrame ConstructSpdyGetReply(const char* const extra_headers[],
+                                            int extra_header_count,
+                                            int stream_id);
 
-  // Constructs a standard SPDY SYN_REPLY frame to match the SPDY GET.
+  // Constructs a standard SPDY HEADERS frame to match the SPDY GET.
   // |extra_headers| are the extra header-value pairs, which typically
   // will vary the most between calls.
   // Returns a SpdySerializedFrame.
-  SpdySerializedFrame ConstructSpdyGetSynReplyRedirect(int stream_id);
+  SpdySerializedFrame ConstructSpdyGetReplyRedirect(int stream_id);
 
-  // Constructs a standard SPDY SYN_REPLY frame with an Internal Server
+  // Constructs a standard SPDY HEADERS frame with an Internal Server
   // Error status code.
   // Returns a SpdySerializedFrame.
-  SpdySerializedFrame ConstructSpdySynReplyError(int stream_id);
+  SpdySerializedFrame ConstructSpdyReplyError(int stream_id);
 
-  // Constructs a standard SPDY SYN_REPLY frame with the specified status code.
+  // Constructs a standard SPDY HEADERS frame with the specified status code.
   // Returns a SpdySerializedFrame.
-  SpdySerializedFrame ConstructSpdySynReplyError(
+  SpdySerializedFrame ConstructSpdyReplyError(
       const char* const status,
       const char* const* const extra_headers,
       int extra_header_count,
       int stream_id);
 
-  // Constructs a standard SPDY POST SYN frame.
+  // Constructs a standard SPDY POST HEADERS frame.
   // |extra_headers| are the extra header-value pairs, which typically
   // will vary the most between calls.
   // Returns a SpdySerializedFrame.
@@ -445,7 +445,7 @@
                                         const char* const extra_headers[],
                                         int extra_header_count);
 
-  // Constructs a chunked transfer SPDY POST SYN frame.
+  // Constructs a chunked transfer SPDY POST HEADERS frame.
   // |extra_headers| are the extra header-value pairs, which typically
   // will vary the most between calls.
   // Returns a SpdySerializedFrame.
@@ -453,13 +453,12 @@
       const char* const extra_headers[],
       int extra_header_count);
 
-  // Constructs a standard SPDY SYN_REPLY frame to match the SPDY POST.
+  // Constructs a standard SPDY HEADERS frame to match the SPDY POST.
   // |extra_headers| are the extra header-value pairs, which typically
   // will vary the most between calls.
   // Returns a SpdySerializedFrame.
-  SpdySerializedFrame ConstructSpdyPostSynReply(
-      const char* const extra_headers[],
-      int extra_header_count);
+  SpdySerializedFrame ConstructSpdyPostReply(const char* const extra_headers[],
+                                             int extra_header_count);
 
   // Constructs a single SPDY data frame with the contents "hello!"
   SpdySerializedFrame ConstructSpdyDataFrame(int stream_id, bool fin);
@@ -490,9 +489,6 @@
   // class of stream destruction.
   void UpdateWithStreamDestruction(int stream_id);
 
-  // Maps |priority| to SPDY priority, and sets it on |frame_ir|.
-  void SetPriority(RequestPriority priority, SpdySynStreamIR* frame_ir) const;
-
   void set_default_url(const GURL& url) { default_url_ = url; }
 
   const char* GetMethodKey() const;
diff --git a/net/spdy/spdy_write_queue_unittest.cc b/net/spdy/spdy_write_queue_unittest.cc
index 9ffacc8..bcf4078 100644
--- a/net/spdy/spdy_write_queue_unittest.cc
+++ b/net/spdy/spdy_write_queue_unittest.cc
@@ -115,9 +115,9 @@
   std::unique_ptr<SpdyStream> stream_highest(MakeTestStream(HIGHEST));
 
   // A NULL stream should still work.
-  write_queue.Enqueue(LOW, SYN_STREAM, std::move(producer_low),
+  write_queue.Enqueue(LOW, HEADERS, std::move(producer_low),
                       base::WeakPtr<SpdyStream>());
-  write_queue.Enqueue(MEDIUM, SYN_REPLY, std::move(producer_medium),
+  write_queue.Enqueue(MEDIUM, HEADERS, std::move(producer_medium),
                       stream_medium->GetWeakPtr());
   write_queue.Enqueue(HIGHEST, RST_STREAM, std::move(producer_highest),
                       stream_highest->GetWeakPtr());
@@ -131,12 +131,12 @@
   EXPECT_EQ(stream_highest.get(), stream.get());
 
   ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream));
-  EXPECT_EQ(SYN_REPLY, frame_type);
+  EXPECT_EQ(HEADERS, frame_type);
   EXPECT_EQ("MEDIUM", ProducerToString(std::move(frame_producer)));
   EXPECT_EQ(stream_medium.get(), stream.get());
 
   ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream));
-  EXPECT_EQ(SYN_STREAM, frame_type);
+  EXPECT_EQ(HEADERS, frame_type);
   EXPECT_EQ("LOW", ProducerToString(std::move(frame_producer)));
   EXPECT_EQ(nullptr, stream.get());
 
@@ -156,9 +156,9 @@
   std::unique_ptr<SpdyStream> stream2(MakeTestStream(DEFAULT_PRIORITY));
   std::unique_ptr<SpdyStream> stream3(MakeTestStream(DEFAULT_PRIORITY));
 
-  write_queue.Enqueue(DEFAULT_PRIORITY, SYN_STREAM, std::move(producer1),
+  write_queue.Enqueue(DEFAULT_PRIORITY, HEADERS, std::move(producer1),
                       stream1->GetWeakPtr());
-  write_queue.Enqueue(DEFAULT_PRIORITY, SYN_REPLY, std::move(producer2),
+  write_queue.Enqueue(DEFAULT_PRIORITY, HEADERS, std::move(producer2),
                       stream2->GetWeakPtr());
   write_queue.Enqueue(DEFAULT_PRIORITY, RST_STREAM, std::move(producer3),
                       stream3->GetWeakPtr());
@@ -167,12 +167,12 @@
   std::unique_ptr<SpdyBufferProducer> frame_producer;
   base::WeakPtr<SpdyStream> stream;
   ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream));
-  EXPECT_EQ(SYN_STREAM, frame_type);
+  EXPECT_EQ(HEADERS, frame_type);
   EXPECT_EQ(1, ProducerToInt(std::move(frame_producer)));
   EXPECT_EQ(stream1.get(), stream.get());
 
   ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream));
-  EXPECT_EQ(SYN_REPLY, frame_type);
+  EXPECT_EQ(HEADERS, frame_type);
   EXPECT_EQ(2, ProducerToInt(std::move(frame_producer)));
   EXPECT_EQ(stream2.get(), stream.get());
 
@@ -196,7 +196,7 @@
   for (int i = 0; i < 100; ++i) {
     base::WeakPtr<SpdyStream> stream =
         (((i % 3) == 0) ? stream1 : stream2)->GetWeakPtr();
-    write_queue.Enqueue(DEFAULT_PRIORITY, SYN_STREAM, IntToProducer(i), stream);
+    write_queue.Enqueue(DEFAULT_PRIORITY, HEADERS, IntToProducer(i), stream);
   }
 
   write_queue.RemovePendingWritesForStream(stream2->GetWeakPtr());
@@ -206,7 +206,7 @@
     std::unique_ptr<SpdyBufferProducer> frame_producer;
     base::WeakPtr<SpdyStream> stream;
     ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream));
-    EXPECT_EQ(SYN_STREAM, frame_type);
+    EXPECT_EQ(HEADERS, frame_type);
     EXPECT_EQ(i, ProducerToInt(std::move(frame_producer)));
     EXPECT_EQ(stream1.get(), stream.get());
   }
@@ -238,7 +238,7 @@
   };
 
   for (int i = 0; i < 100; ++i) {
-    write_queue.Enqueue(DEFAULT_PRIORITY, SYN_STREAM, IntToProducer(i),
+    write_queue.Enqueue(DEFAULT_PRIORITY, HEADERS, IntToProducer(i),
                         streams[i % arraysize(streams)]);
   }
 
@@ -250,7 +250,7 @@
     base::WeakPtr<SpdyStream> stream;
     ASSERT_TRUE(write_queue.Dequeue(&frame_type, &frame_producer, &stream))
         << "Unable to Dequeue i: " << i;
-    EXPECT_EQ(SYN_STREAM, frame_type);
+    EXPECT_EQ(HEADERS, frame_type);
     EXPECT_EQ(i, ProducerToInt(std::move(frame_producer)));
     EXPECT_EQ(stream1.get(), stream.get());
   }
@@ -268,7 +268,7 @@
   SpdyWriteQueue write_queue;
 
   for (int i = 0; i < 100; ++i) {
-    write_queue.Enqueue(DEFAULT_PRIORITY, SYN_STREAM, IntToProducer(i),
+    write_queue.Enqueue(DEFAULT_PRIORITY, HEADERS, IntToProducer(i),
                         base::WeakPtr<SpdyStream>());
   }
 
@@ -283,7 +283,7 @@
 TEST_F(SpdyWriteQueueTest, RequeingProducerWithoutReentrance) {
   SpdyWriteQueue queue;
   queue.Enqueue(
-      DEFAULT_PRIORITY, SYN_STREAM,
+      DEFAULT_PRIORITY, HEADERS,
       std::unique_ptr<SpdyBufferProducer>(new RequeingBufferProducer(&queue)),
       base::WeakPtr<SpdyStream>());
   {
@@ -309,7 +309,7 @@
 TEST_F(SpdyWriteQueueTest, ReentranceOnClear) {
   SpdyWriteQueue queue;
   queue.Enqueue(
-      DEFAULT_PRIORITY, SYN_STREAM,
+      DEFAULT_PRIORITY, HEADERS,
       std::unique_ptr<SpdyBufferProducer>(new RequeingBufferProducer(&queue)),
       base::WeakPtr<SpdyStream>());
 
@@ -330,7 +330,7 @@
 
   SpdyWriteQueue queue;
   queue.Enqueue(
-      DEFAULT_PRIORITY, SYN_STREAM,
+      DEFAULT_PRIORITY, HEADERS,
       std::unique_ptr<SpdyBufferProducer>(new RequeingBufferProducer(&queue)),
       stream->GetWeakPtr());
 
@@ -351,7 +351,7 @@
 
   SpdyWriteQueue queue;
   queue.Enqueue(
-      DEFAULT_PRIORITY, SYN_STREAM,
+      DEFAULT_PRIORITY, HEADERS,
       std::unique_ptr<SpdyBufferProducer>(new RequeingBufferProducer(&queue)),
       stream->GetWeakPtr());
 
diff --git a/printing/printing.gyp b/printing/printing.gyp
index 3c43ce9..80b83c5 100644
--- a/printing/printing.gyp
+++ b/printing/printing.gyp
@@ -167,13 +167,6 @@
                 ],
               },
             }],
-          ],
-          'defines': [
-            # PRINT_BACKEND_AVAILABLE disables the default dummy implementation
-            # of the print backend and enables a custom implementation instead.
-            'PRINT_BACKEND_AVAILABLE',
-          ],
-          'conditions': [
             ['chromeos==1', {
               'sources': [
                 'backend/cups_connection.cc',
@@ -195,6 +188,11 @@
               ],
             }],
           ],
+          'defines': [
+            # PRINT_BACKEND_AVAILABLE disables the default dummy implementation
+            # of the print backend and enables a custom implementation instead.
+            'PRINT_BACKEND_AVAILABLE',
+          ],
         }],
         ['OS=="linux" and chromeos==1', {
           'defines': [
diff --git a/sandbox/mac/bootstrap_sandbox_unittest.mm b/sandbox/mac/bootstrap_sandbox_unittest.mm
index f81cd114..e39e949 100644
--- a/sandbox/mac/bootstrap_sandbox_unittest.mm
+++ b/sandbox/mac/bootstrap_sandbox_unittest.mm
@@ -138,6 +138,11 @@
 // Run the test with the sandbox enabled without notifications on the policy
 // whitelist.
 TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxDeny) {
+  if (base::mac::IsOSSierraOrLater()) {
+    LOG(ERROR) << "BootstrapSandbox does not work on macOS Sierra or later.";
+    return;
+  }
+
   base::scoped_nsobject<DistributedNotificationObserver> observer(
       [[DistributedNotificationObserver alloc] init]);
 
@@ -151,6 +156,11 @@
 
 // Run the test with notifications permitted.
 TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxAllow) {
+  if (base::mac::IsOSSierraOrLater()) {
+    LOG(ERROR) << "BootstrapSandbox does not work on macOS Sierra or later.";
+    return;
+  }
+
   base::scoped_nsobject<DistributedNotificationObserver> observer(
       [[DistributedNotificationObserver alloc] init]);
 
@@ -181,6 +191,10 @@
 const char kTestServer[] = "org.chromium.test_bootstrap_server";
 
 TEST_F(BootstrapSandboxTest, PolicyDenyError) {
+  if (base::mac::IsOSSierraOrLater()) {
+    LOG(ERROR) << "BootstrapSandbox does not work on macOS Sierra or later.";
+    return;
+  }
   BootstrapSandboxPolicy policy(BaselinePolicy());
   policy.rules[kTestServer] = Rule(POLICY_DENY_ERROR);
   sandbox_->RegisterSandboxPolicy(1, policy);
@@ -204,6 +218,10 @@
 }
 
 TEST_F(BootstrapSandboxTest, PolicyDenyDummyPort) {
+  if (base::mac::IsOSSierraOrLater()) {
+    LOG(ERROR) << "BootstrapSandbox does not work on macOS Sierra or later.";
+    return;
+  }
   BootstrapSandboxPolicy policy(BaselinePolicy());
   policy.rules[kTestServer] = Rule(POLICY_DENY_DUMMY_PORT);
   sandbox_->RegisterSandboxPolicy(1, policy);
@@ -232,6 +250,11 @@
 const char kSubstituteAck[] = "Hello, this is doge!";
 
 TEST_F(BootstrapSandboxTest, PolicySubstitutePort) {
+  if (base::mac::IsOSSierraOrLater()) {
+    LOG(ERROR) << "BootstrapSandbox does not work on macOS Sierra or later.";
+    return;
+  }
+
   mach_port_t task = mach_task_self();
 
   mach_port_t port;
@@ -348,6 +371,11 @@
     "org.chromium.sandbox.test.DefaultRuleAllow.Deny";
 
 TEST_F(BootstrapSandboxTest, DefaultRuleAllow) {
+  if (base::mac::IsOSSierraOrLater()) {
+    LOG(ERROR) << "BootstrapSandbox does not work on macOS Sierra or later.";
+    return;
+  }
+
   mach_port_t task = mach_task_self();
 
   mach_port_t port;
@@ -415,6 +443,11 @@
 }
 
 TEST_F(BootstrapSandboxTest, ChildOutliveSandbox) {
+  if (base::mac::IsOSSierraOrLater()) {
+    LOG(ERROR) << "BootstrapSandbox does not work on macOS Sierra or later.";
+    return;
+  }
+
   const int kTestPolicyId = 1;
   mach_port_t task = mach_task_self();
 
diff --git a/skia/public/interfaces/bitmap_skbitmap_struct_traits.cc b/skia/public/interfaces/bitmap_skbitmap_struct_traits.cc
index 1e934529..5e6ce30 100644
--- a/skia/public/interfaces/bitmap_skbitmap_struct_traits.cc
+++ b/skia/public/interfaces/bitmap_skbitmap_struct_traits.cc
@@ -171,7 +171,7 @@
   SkAutoPixmapUnlock pixmap;
   if (static_cast<uint32_t>(b->width()) != data.width() ||
       static_cast<uint32_t>(b->height()) != data.height() ||
-      !b->requestLock(&pixmap) || !b->lockPixelsAreWritable() ||
+      !b->requestLock(&pixmap) ||
       !b->readyToDraw()) {
     return false;
   }
diff --git a/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter b/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter
index 63b2c766..9b0f10b3 100644
--- a/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter
+++ b/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter
@@ -4,9 +4,10 @@
 -NavigationControllerBrowserTest.BackTwiceToIframeWithContent
 -NavigationControllerBrowserTest.FrameNavigationEntry_BackWithRedirect
 -NavigationControllerBrowserTest.FrameNavigationEntry_SameOriginBackWithRedirect
+-NavigationHandleImplBrowserTest.VerifyFormRequestContextType
 -NavigationHandleImplHttpsUpgradeBrowserTest.*
 -RenderFrameHostManagerTest.RestoreSubframeFileAccessForHistoryNavigation
 -RequestDataResourceDispatcherHostBrowserTest.*
 -ServiceWorker*
 -SitePerProcessIgnoreCertErrorsBrowserTest.CrossSiteRedirectCertificateStore
--SitePerProcessIgnoreCertErrorsBrowserTest.SubresourceWithRedundantCertificateErrors
+-SitePerProcessIgnoreCertErrorsBrowserTest.SubresourceWithRedundantCertificateErrors
\ No newline at end of file
diff --git a/testing/buildbot/filters/browser-side-navigation.linux.unit_tests.filter b/testing/buildbot/filters/browser-side-navigation.linux.unit_tests.filter
index 784639a..8ec4f19 100644
--- a/testing/buildbot/filters/browser-side-navigation.linux.unit_tests.filter
+++ b/testing/buildbot/filters/browser-side-navigation.linux.unit_tests.filter
@@ -21,7 +21,6 @@
 -SearchIPCRouterPolicyTest.ProcessUndoAllMostVisitedDeletions
 -SearchIPCRouterPolicyTest.ProcessUndoMostVisitedDeletion
 -SearchIPCRouterPolicyTest.SendMostVisitedItems
--SearchIPCRouterPolicyTest.SendSetPromoInformation
 -SearchIPCRouterPolicyTest.SendThemeBackgroundInfo
 -SearchIPCRouterTest.IgnoreChromeIdentityCheckMsg
 -SearchIPCRouterTest.IgnoreDeleteMostVisitedItemMsg
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 6745e16..df12b86 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1353,29 +1353,40 @@
 crbug.com/627798 fast/repaint/text-match-document-change.html [ Pass Failure ]
 
 # Very slight rendering changes caused by Skia rect clipping change.
-crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-background-image-space.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/png-with-color-profile.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-image-profile-match.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/exif-orientation-height-image-document.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-reflection.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-background-image-cross-fade-png.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-background-image-repeat.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-background-image-cross-fade.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-image.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-border-image-source.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-munsell-srgb-to-srgb.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-image-canvas-pattern.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-border-image.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-munsell-adobe-to-srgb.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-mask-image-svg.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-group.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-image-canvas.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/jpeg-with-color-profile.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-svg.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-background-image-cover.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-object.html [ NeedsManualRebaseline ]
-crbug.com/627844 virtual/gpu-rasterization/fast/images/ycbcr-with-cmyk-color-profile.html [ NeedsManualRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-background-image-space.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/png-with-color-profile.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-image-profile-match.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/exif-orientation-height-image-document.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-reflection.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-background-image-cross-fade-png.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-background-image-repeat.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-background-image-cross-fade.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-image.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-border-image-source.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-munsell-srgb-to-srgb.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-image-canvas-pattern.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-border-image.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-munsell-adobe-to-srgb.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-mask-image-svg.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-group.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-image-canvas.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/jpeg-with-color-profile.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-svg.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-background-image-cover.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/color-profile-object.html [ NeedsRebaseline ]
+crbug.com/627844 virtual/gpu-rasterization/fast/images/ycbcr-with-cmyk-color-profile.html [ NeedsRebaseline ]
 crbug.com/627844 virtual/gpu/fast/canvas/canvas-createImageBitmap-colorClamping.html [ Pass Failure ]
 
 crbug.com/629711 [ Linux Debug ] fast/workers/shared-worker-exception.html [ Crash Pass ]
 crbug.com/629711 [ Linux Debug ] virtual/sharedarraybuffer/fast/workers/shared-worker-exception.html [ Crash Pass ]
+
+crbug.com/630967 [ Linux Debug ] svg/parser/whitespace-length-invalid-2.html [ Skip ]
+crbug.com/630615 [ Win7 ] fast/images/color-profile-background-image-space.html [ Skip ]
+crbug.com/399507 [ Mac10.9 Mac10.10 ] virtual/threaded/inspector/tracing/timeline-paint/layer-tree.html [ Skip ]
+
+crbug.com/631039 [ Win7 ] virtual/threaded/fast/scroll-behavior/subframe-scrollBy.html [ Pass Failure ]
+crbug.com/631039 [ Win7 ] virtual/threaded/fast/scroll-behavior/main-frame-element-scroll.html [ Pass Failure ]
+crbug.com/631039 [ Win7 ] virtual/threaded/fast/scroll-behavior/main-frame-element-scrollBy.html [ Pass Failure ]
+crbug.com/631039 [ Win7 ] virtual/threaded/fast/scroll-behavior/main-frame-element-scrollTo.html [ Pass Failure ]
+crbug.com/631039 [ Win7 ] virtual/threaded/fast/scroll-behavior/main-frame-scroll.html [ Pass Failure ]
+crbug.com/631039 [ Win7 ] virtual/threaded/fast/scroll-behavior/overflow-scroll-scroll.html [ Pass Failure ]
diff --git a/third_party/WebKit/LayoutTests/fast/images/sprite-no-bleed-expected.png b/third_party/WebKit/LayoutTests/fast/images/sprite-no-bleed-expected.png
new file mode 100644
index 0000000..bb6dcc502
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/images/sprite-no-bleed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/images/sprite-no-bleed.html b/third_party/WebKit/LayoutTests/fast/images/sprite-no-bleed.html
new file mode 100644
index 0000000..ec5d550
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/images/sprite-no-bleed.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+
+<style>
+
+.bg {
+  display: inline-block;
+  margin: 10px;
+  width: 33px;
+  height: 33px;
+  background-color: red;
+  /* 99x99px green/red checkerboard (3x3 tiles) */
+  background: url(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAGMAAABjCAIAAAAAWSnCAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AcWEAYigt4gYwAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAACoSURBVHja7dyxDQAgCEVBvvvvrBvYQcO9BYgXSmLqVnc37SPS/4pTIkWKFClSpESKFClSpEiJFClSpEiRIiVSpEiRIkVKpEiRIkWK1M4GTrQmZgwcs9kpUqRIkSJFSqRIkSJFipRIkSJFihQpUiJFihQpUqREihQpUqRIbS3+NLNTpEiRIkVKpEiRIkWKlEiRIkWKFClSIkWKFClSpESKFClSpEiR0rcHwFgJwTzTOUwAAAAASUVORK5CYII=);
+}
+
+.bg1 {
+  background-position: 0px 0px;
+}
+
+.bg2 {
+  background-position: 0px -66px;
+}
+
+.bg3 {
+  background-position: -33px -33px;
+}
+
+.bg4 {
+  background-position: 0px -66px;
+}
+
+.bg5 {
+  background-position: -66px -66px;
+}
+</style>
+
+<body style="width: 500px">
+
+<div style="transform: scale(0.5, 0.5); transform-origin: top left;">
+  <div class="bg bg1"></div>
+  <div class="bg bg2"></div>
+  <div class="bg bg3"></div>
+  <div class="bg bg4"></div>
+  <div class="bg bg5"></div>
+</div>
+
+<div style="transform: scale(0.75, 0.75); transform-origin: top left;">
+  <div class="bg bg1"></div>
+  <div class="bg bg2"></div>
+  <div class="bg bg3"></div>
+  <div class="bg bg4"></div>
+  <div class="bg bg5"></div>
+</div>
+
+<div>
+  <div class="bg bg1"></div>
+  <div class="bg bg2"></div>
+  <div class="bg bg3"></div>
+  <div class="bg bg4"></div>
+  <div class="bg bg5"></div>
+</div>
+
+<div style="transform: scale(1.25, 1.25); transform-origin: top left;">
+  <div class="bg bg1"></div>
+  <div class="bg bg2"></div>
+  <div class="bg bg3"></div>
+  <div class="bg bg4"></div>
+  <div class="bg bg5"></div>
+</div>
+
+<div style="transform: scale(1.5, 1.5); transform-origin: top left;">
+  <div class="bg bg1"></div>
+  <div class="bg bg2"></div>
+  <div class="bg bg3"></div>
+  <div class="bg bg4"></div>
+  <div class="bg bg5"></div>
+</div>
+</body>
diff --git a/third_party/WebKit/LayoutTests/http/tests/media/video-seek-to-duration-expected.txt b/third_party/WebKit/LayoutTests/http/tests/media/video-seek-to-duration-expected.txt
deleted file mode 100644
index 69945d52..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/media/video-seek-to-duration-expected.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-Test event dispatches and attribute changes for seek to duration
-
-RUN(video.play())
-EVENT(durationchange)
-EVENT(loadedmetadata)
-EVENT(loadeddata)
-EXPECTED (video.currentTime < video.duration == 'true') OK
-EXPECTED (video.ended == 'false') OK
-EXPECTED (video.paused == 'false') OK
-Starting seek to duration by setting video.currentTime to video.duration
-EXPECTED (video.currentTime == video.duration == 'true') OK
-EXPECTED (video.seeking == 'true') OK
-EXPECTED (video.ended == 'true') OK
-EVENT(seeking)
-EXPECTED (video.seeking == 'true') OK
-EXPECTED (video.ended == 'true') OK
-EXPECTED (video.currentTime == video.duration == 'true') OK
-EXPECTED (video.paused == 'false') OK
-EVENT(timeupdate)
-EXPECTED (video.seeking == 'false') OK
-EXPECTED (video.ended == 'true') OK
-EXPECTED (video.currentTime == video.duration == 'true') OK
-EVENT(seeked)
-EXPECTED (video.seeking == 'false') OK
-EXPECTED (video.ended == 'true') OK
-EXPECTED (video.currentTime == video.duration == 'true') OK
-EVENT(pause)
-EXPECTED (video.paused == 'true') OK
-EXPECTED (video.seeking == 'false') OK
-EXPECTED (video.ended == 'true') OK
-EXPECTED (video.currentTime == video.duration == 'true') OK
-EVENT(ended)
-EXPECTED (video.seeking == 'false') OK
-EXPECTED (video.ended == 'true') OK
-EXPECTED (video.paused == 'true') OK
-EXPECTED (video.currentTime == video.duration == 'true') OK
-END OF TEST
-
diff --git a/third_party/WebKit/LayoutTests/http/tests/media/video-seek-to-duration.html b/third_party/WebKit/LayoutTests/http/tests/media/video-seek-to-duration.html
index 75baddb..ba61b9c7 100644
--- a/third_party/WebKit/LayoutTests/http/tests/media/video-seek-to-duration.html
+++ b/third_party/WebKit/LayoutTests/http/tests/media/video-seek-to-duration.html
@@ -1,77 +1,54 @@
-<html>
-    <head>
-        <script src=../../media-resources/media-file.js></script>
-        <!-- TODO(foolip): Convert test to testharness.js. crbug.com/588956
-             (Please avoid writing new tests using video-test.js) -->
-        <script src=../../media-resources/video-test.js></script>
-        <script>
-            function start()
-            {
-                findMediaElement();
+<!DOCTYPE html>
+<title>Test event dispatches and attribute changes for seek to duration.</title>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="../../media-resources/media-file.js"></script>
+<video></video>
+<script>
+async_test(function(t) {
+    var video = document.querySelector("video");
 
-                waitForEvent('durationchange');
-                waitForEvent('loadedmetadata');
-                waitForEventOnce('loadeddata', function ()
-                {
-                    waitForEvent('seeking', function ()
-                    {
-                        testExpected('video.seeking', true, '==');
-                        testExpected('video.ended', true, '==');
-                        testExpected('video.currentTime == video.duration', true, '==');
-                        testExpected('video.paused', false, '==');
+    var watcher = new EventWatcher(t, video, ["durationchange", "loadedmetadata",
+        "loadeddata", "seeking", "timeupdate", "seeked", "pause", "ended"]);
 
-                        waitForEventOnce('timeupdate', function()
-                        {
-                            testExpected('video.seeking', false, '==');
-                            testExpected('video.ended', true, '==');
-                            testExpected('video.currentTime == video.duration', true, '==');
-                        });
-                    });
+    watcher.wait_for(["durationchange", "loadedmetadata", "loadeddata"]).then(t.step_func(function() {
+        // Seek to the duration of the media.
+        assert_less_than(video.currentTime, video.duration);
+        assert_false(video.ended)
+        assert_false(video.paused);
+        //Starting seek to duration by setting video.currentTime to video.duration.
+        video.currentTime = video.duration;
+        testCommonAttributes(true);
+        return watcher.wait_for("seeking");
+    })).then(t.step_func(function() {
+        testCommonAttributes(true);
+        assert_false(video.paused);
+        return watcher.wait_for("timeupdate");
+    })).then(t.step_func(function() {
+        testCommonAttributes(false);
+        return watcher.wait_for("seeked");
+    })).then(t.step_func(function() {
+        testCommonAttributes(false);
+        return watcher.wait_for("timeupdate");
+    })).then(t.step_func(function() {
+        testCommonAttributes(false);
+        return watcher.wait_for("pause");
+    })).then(t.step_func(function() {
+        assert_true(video.paused);
+        testCommonAttributes(false);
+        return watcher.wait_for("ended");
+    })).then(t.step_func_done(function() {
+        testCommonAttributes(false);
+        assert_true(video.paused);
+    }));
 
-                    waitForEvent('seeked', function ()
-                    {
-                        testExpected('video.seeking', false, '==');
-                        testExpected('video.ended', true, '==');
-                        testExpected('video.currentTime == video.duration', true, '==');
+    function testCommonAttributes(seekingExpected) {
+        assert_equals(video.seeking, seekingExpected);
+        assert_true(video.ended);
+        assert_equals(video.currentTime, video.duration);
+    }
 
-                    });
-
-                    waitForEvent('pause', function()
-                    {
-                        testExpected('video.paused', true, '==');
-                        testExpected('video.seeking', false, '==');
-                        testExpected('video.ended', true, '==');
-                        testExpected('video.currentTime == video.duration', true, '==');
-                    });
-
-                    waitForEvent('ended', function ()
-                    {
-                        testExpected('video.seeking', false, '==');
-                        testExpected('video.ended', true, '==');
-                        testExpected('video.paused', true, '==');
-                        testExpected('video.currentTime == video.duration', true, '==');
-                        endTest();
-                    });
-
-                    // Seek to the duration of the media
-                    testExpected('video.currentTime < video.duration', true, '==');
-                    testExpected('video.ended', false, '==');
-                    testExpected('video.paused', false, '==');
-                    consoleWrite('Starting seek to duration by setting video.currentTime to video.duration');
-                    video.currentTime = video.duration;
-                    testExpected('video.currentTime == video.duration', true, '==');
-                    testExpected('video.seeking', true, '==');
-                    testExpected('video.ended', true, '==');
-                });
-
-                var mediaFile = findMediaFile('video', 'resources/test');
-                video.src = '/' + mediaFile;
-                run('video.play()');
-            }
-        </script>
-    </head>
-    <body onload="start()">
-        <video></video>
-        <p>Test event dispatches and attribute changes for seek to duration</p>
-    </body>
-</html>
+    video.src = findMediaFile("video", "resources/test");;
+    video.play();
+});
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/media/video-seek-to-middle-expected.txt b/third_party/WebKit/LayoutTests/http/tests/media/video-seek-to-middle-expected.txt
deleted file mode 100644
index 6d724e6..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/media/video-seek-to-middle-expected.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-Test event dispatches and attribute changes for seek to middle
-
-RUN(video.play())
-EVENT(durationchange)
-EVENT(loadedmetadata)
-EVENT(loadeddata)
-EXPECTED (video.currentTime < video.duration == 'true') OK
-EXPECTED (video.ended == 'false') OK
-EXPECTED (video.seeking == 'false') OK
-EXPECTED (video.paused == 'false') OK
-Starting seek to middle by setting video.currentTime to video.duration / 2
-EXPECTED (video.seeking == 'true') OK
-EXPECTED (video.ended == 'false') OK
-EXPECTED (video.currentTime == (video.duration / 2) == 'true') OK
-EXPECTED (video.paused == 'false') OK
-EXPECTED (video.currentTime < video.duration == 'true') OK
-EXPECTED (video.currentTime > 0 == 'true') OK
-EVENT(seeking)
-EXPECTED (video.seeking == 'true') OK
-EXPECTED (video.ended == 'false') OK
-EXPECTED (video.currentTime == (video.duration / 2) == 'true') OK
-EXPECTED (video.paused == 'false') OK
-EVENT(timeupdate)
-EXPECTED (video.seeking == 'false') OK
-EXPECTED (video.ended == 'false') OK
-EXPECTED (video.currentTime == (video.duration / 2) == 'true') OK
-EXPECTED (video.paused == 'false') OK
-EVENT(seeked)
-EXPECTED (video.seeking == 'false') OK
-EXPECTED (video.ended == 'false') OK
-EXPECTED (video.currentTime == (video.duration / 2) == 'true') OK
-EXPECTED (video.paused == 'false') OK
-END OF TEST
-
diff --git a/third_party/WebKit/LayoutTests/http/tests/media/video-seek-to-middle.html b/third_party/WebKit/LayoutTests/http/tests/media/video-seek-to-middle.html
index 26caf8b..e37af9b 100644
--- a/third_party/WebKit/LayoutTests/http/tests/media/video-seek-to-middle.html
+++ b/third_party/WebKit/LayoutTests/http/tests/media/video-seek-to-middle.html
@@ -1,63 +1,46 @@
-<html>
-    <head>
-        <script src=../../media-resources/media-file.js></script>
-        <!-- TODO(foolip): Convert test to testharness.js. crbug.com/588956
-             (Please avoid writing new tests using video-test.js) -->
-        <script src=../../media-resources/video-test.js></script>
-        <script>
-            function testCommonAttributes(seekingExpected)
-            {
-                testExpected('video.seeking', seekingExpected, '==');
-                testExpected('video.ended', false, '==');
-                testExpected('video.currentTime == (video.duration / 2)', true, '==');
-                testExpected('video.paused', false, '==');
-            }
+<!DOCTYPE html>
+<title>Test event dispatches and attribute changes for seek to middle.</title>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="../../media-resources/media-file.js"></script>
+<video></video>
+<script>
+async_test(function(t) {
+    var video = document.querySelector("video");
 
-            function start()
-            {
-                findMediaElement();
+    video.onended = t.unreached_func();
 
-                waitForEventAndFail('ended');
+    var watcher = new EventWatcher(t, video, ["durationchange", "loadedmetadata", "loadeddata", "seeking", "timeupdate", "seeked"]);
 
-                waitForEvent('durationchange');
-                waitForEvent('loadedmetadata');
-                waitForEventOnce('loadeddata', function ()
-                {
-                    waitForEvent('seeking', function ()
-                    {
-                        testCommonAttributes(true);
-                        waitForEventOnce('timeupdate', function()
-                        {
-                            testCommonAttributes(false);
-                        });
-                    });
+    watcher.wait_for(["durationchange", "loadedmetadata", "loadeddata"]).then(t.step_func(function() {
+        assert_less_than(video.currentTime, video.duration);
+        assert_false(video.ended);
+        assert_false(video.seeking);
+        assert_false(video.paused);
+        // Starting seek to middle by setting video.currentTime to half the duration.
+        video.currentTime = video.duration / 2;
+        testCommonAttributes(true);
+        assert_less_than(video.currentTime, video.duration);
+        assert_greater_than(video.currentTime, 0);
+        return watcher.wait_for("seeking");
+    })).then(t.step_func(function() {
+        testCommonAttributes(true);
+        return watcher.wait_for("timeupdate");
+    })).then(t.step_func(function() {
+        testCommonAttributes(false);
+        return watcher.wait_for("seeked");
+    })).then(t.step_func_done(function() {
+        testCommonAttributes(false);
+    }));
 
-                    waitForEvent('seeked', function ()
-                    {
-                        testCommonAttributes(false);
-                        endTest();
-                    });
+    function testCommonAttributes(seekingExpected) {
+        assert_equals(video.seeking, seekingExpected);
+        assert_false(video.ended);
+        assert_equals(video.currentTime, video.duration / 2);
+        assert_false(video.paused);
+    }
 
-                    // Seek to half the duration of the media
-                    testExpected('video.currentTime < video.duration', true, '==');
-                    testExpected('video.ended', false, '==');
-                    testExpected('video.seeking', false, '==');
-                    testExpected('video.paused', false, '==');
-                    consoleWrite('Starting seek to middle by setting video.currentTime to video.duration / 2');
-                    video.currentTime = video.duration / 2;
-                    testCommonAttributes(true);
-                    testExpected('video.currentTime < video.duration', true, '==');
-                    testExpected('video.currentTime > 0', true, '==');
-                });
-
-                var mediaFile = findMediaFile('video', 'resources/test');
-                video.src = '/' + mediaFile;
-                run('video.play()');
-            }
-        </script>
-    </head>
-    <body onload="start()">
-        <video></video>
-        <p>Test event dispatches and attribute changes for seek to middle</p>
-    </body>
-</html>
+    video.src = findMediaFile("video", "resources/test");
+    video.play();
+});
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/media/video-served-as-text-expected.txt b/third_party/WebKit/LayoutTests/http/tests/media/video-served-as-text-expected.txt
deleted file mode 100644
index e5fc07f..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/media/video-served-as-text-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-EXPECTED (video.error == 'null') OK
-EVENT(loadstart)
-EVENT(loadedmetadata)
-loaded media file served as text OK
-END OF TEST
-
diff --git a/third_party/WebKit/LayoutTests/http/tests/media/video-served-as-text.html b/third_party/WebKit/LayoutTests/http/tests/media/video-served-as-text.html
index 3c2e553..9fc3e16 100644
--- a/third_party/WebKit/LayoutTests/http/tests/media/video-served-as-text.html
+++ b/third_party/WebKit/LayoutTests/http/tests/media/video-served-as-text.html
@@ -1,40 +1,18 @@
 <!DOCTYPE html>
-<html>
-    <head>
-        <title>media file served as 'text/plain'</title>
-        <script src=../../media-resources/media-file.js></script>
-        <!-- TODO(foolip): Convert test to testharness.js. crbug.com/588956
-             (Please avoid writing new tests using video-test.js) -->
-        <script src=../../media-resources/video-test.js></script>
-        <script>
-            function loadedmetadata(evt)
-            {
-                logResult(true, "loaded media file served as text");
-                endTest();
-            }
+<title>Tests that a media file is served as "text/plain".</title>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="../../media-resources/media-file.js"></script>
+<video></video>
+<script>
+async_test(function(t) {
+    var video = document.querySelector("video");
 
-            function error(evt)
-            {
-                logResult(false, "failed trying to load media file served as text");
-                endTest();
-            }
+    var watcher = new EventWatcher(t, video, ["loadstart", "loadedmetadata", "error"]);
+    watcher.wait_for(["loadstart", "loadedmetadata"]).then(t.step_func_done());
+    assert_equals(video.error, null);
 
-            function start()
-            {
-                findMediaElement();
-
-                waitForEvent('loadedmetadata', loadedmetadata);
-                waitForEvent("error", error);
-                waitForEvent("loadstart");
-                testExpected("video.error", null);
-
-                var movie = findMediaFile("video", "resources/test");
-                video.src = "http://127.0.0.1:8000/media/video-throttled-load.cgi?name=" + movie + "&throttle=99999&type=text/plain";
-            }
-        </script>
-    </head>
-
-    <body onload="start()">
-        <video controls></video>
-    </body>
-</html>
+    var movie = findMediaFile("video", "resources/test");
+    video.src = "http://127.0.0.1:8000/media/video-throttled-load.cgi?name=" + movie + "&throttle=99999&type=text/plain";
+});
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/sprite-no-bleed-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/images/sprite-no-bleed-expected.png
new file mode 100644
index 0000000..d19fe5c0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/sprite-no-bleed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/images/sprite-no-bleed-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/images/sprite-no-bleed-expected.txt
new file mode 100644
index 0000000..76dbfd8c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/images/sprite-no-bleed-expected.txt
@@ -0,0 +1,84 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x306
+  LayoutBlockFlow {HTML} at (0,0) size 800x306
+    LayoutBlockFlow {BODY} at (8,8) size 500x290
+      LayoutBlockFlow {DIV} at (0,116) size 500x58
+        LayoutBlockFlow {DIV} at (10,10) size 33x33
+        LayoutText {#text} at (53,38) size 4x19
+          text run at (53,38) width 4: " "
+        LayoutBlockFlow {DIV} at (67,10) size 33x33
+        LayoutText {#text} at (110,38) size 4x19
+          text run at (110,38) width 4: " "
+        LayoutBlockFlow {DIV} at (124,10) size 33x33
+        LayoutText {#text} at (167,38) size 4x19
+          text run at (167,38) width 4: " "
+        LayoutBlockFlow {DIV} at (181,10) size 33x33
+        LayoutText {#text} at (224,38) size 4x19
+          text run at (224,38) width 4: " "
+        LayoutBlockFlow {DIV} at (238,10) size 33x33
+        LayoutText {#text} at (0,0) size 0x0
+layer at (8,8) size 500x58
+  LayoutBlockFlow {DIV} at (0,0) size 500x58
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,38) size 4x19
+      text run at (53,38) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,38) size 4x19
+      text run at (110,38) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,38) size 4x19
+      text run at (167,38) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,38) size 4x19
+      text run at (224,38) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,66) size 500x58
+  LayoutBlockFlow {DIV} at (0,58) size 500x58
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,38) size 4x19
+      text run at (53,38) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,38) size 4x19
+      text run at (110,38) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,38) size 4x19
+      text run at (167,38) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,38) size 4x19
+      text run at (224,38) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,182) size 500x58
+  LayoutBlockFlow {DIV} at (0,174) size 500x58
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,38) size 4x19
+      text run at (53,38) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,38) size 4x19
+      text run at (110,38) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,38) size 4x19
+      text run at (167,38) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,38) size 4x19
+      text run at (224,38) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,240) size 500x58
+  LayoutBlockFlow {DIV} at (0,232) size 500x58
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,38) size 4x19
+      text run at (53,38) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,38) size 4x19
+      text run at (110,38) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,38) size 4x19
+      text run at (167,38) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,38) size 4x19
+      text run at (224,38) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/fast/images/sprite-no-bleed-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/fast/images/sprite-no-bleed-expected.png
new file mode 100644
index 0000000..9618d829
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/fast/images/sprite-no-bleed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/fast/images/sprite-no-bleed-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/fast/images/sprite-no-bleed-expected.txt
new file mode 100644
index 0000000..76dbfd8c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/fast/images/sprite-no-bleed-expected.txt
@@ -0,0 +1,84 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x306
+  LayoutBlockFlow {HTML} at (0,0) size 800x306
+    LayoutBlockFlow {BODY} at (8,8) size 500x290
+      LayoutBlockFlow {DIV} at (0,116) size 500x58
+        LayoutBlockFlow {DIV} at (10,10) size 33x33
+        LayoutText {#text} at (53,38) size 4x19
+          text run at (53,38) width 4: " "
+        LayoutBlockFlow {DIV} at (67,10) size 33x33
+        LayoutText {#text} at (110,38) size 4x19
+          text run at (110,38) width 4: " "
+        LayoutBlockFlow {DIV} at (124,10) size 33x33
+        LayoutText {#text} at (167,38) size 4x19
+          text run at (167,38) width 4: " "
+        LayoutBlockFlow {DIV} at (181,10) size 33x33
+        LayoutText {#text} at (224,38) size 4x19
+          text run at (224,38) width 4: " "
+        LayoutBlockFlow {DIV} at (238,10) size 33x33
+        LayoutText {#text} at (0,0) size 0x0
+layer at (8,8) size 500x58
+  LayoutBlockFlow {DIV} at (0,0) size 500x58
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,38) size 4x19
+      text run at (53,38) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,38) size 4x19
+      text run at (110,38) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,38) size 4x19
+      text run at (167,38) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,38) size 4x19
+      text run at (224,38) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,66) size 500x58
+  LayoutBlockFlow {DIV} at (0,58) size 500x58
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,38) size 4x19
+      text run at (53,38) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,38) size 4x19
+      text run at (110,38) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,38) size 4x19
+      text run at (167,38) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,38) size 4x19
+      text run at (224,38) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,182) size 500x58
+  LayoutBlockFlow {DIV} at (0,174) size 500x58
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,38) size 4x19
+      text run at (53,38) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,38) size 4x19
+      text run at (110,38) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,38) size 4x19
+      text run at (167,38) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,38) size 4x19
+      text run at (224,38) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,240) size 500x58
+  LayoutBlockFlow {DIV} at (0,232) size 500x58
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,38) size 4x19
+      text run at (53,38) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,38) size 4x19
+      text run at (110,38) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,38) size 4x19
+      text run at (167,38) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,38) size 4x19
+      text run at (224,38) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/images/sprite-no-bleed-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/images/sprite-no-bleed-expected.txt
new file mode 100644
index 0000000..a8065996
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/images/sprite-no-bleed-expected.txt
@@ -0,0 +1,84 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x301
+  LayoutBlockFlow {HTML} at (0,0) size 800x301
+    LayoutBlockFlow {BODY} at (8,8) size 500x285
+      LayoutBlockFlow {DIV} at (0,114) size 500x57
+        LayoutBlockFlow {DIV} at (10,10) size 33x33
+        LayoutText {#text} at (53,39) size 4x18
+          text run at (53,39) width 4: " "
+        LayoutBlockFlow {DIV} at (67,10) size 33x33
+        LayoutText {#text} at (110,39) size 4x18
+          text run at (110,39) width 4: " "
+        LayoutBlockFlow {DIV} at (124,10) size 33x33
+        LayoutText {#text} at (167,39) size 4x18
+          text run at (167,39) width 4: " "
+        LayoutBlockFlow {DIV} at (181,10) size 33x33
+        LayoutText {#text} at (224,39) size 4x18
+          text run at (224,39) width 4: " "
+        LayoutBlockFlow {DIV} at (238,10) size 33x33
+        LayoutText {#text} at (0,0) size 0x0
+layer at (8,8) size 500x57
+  LayoutBlockFlow {DIV} at (0,0) size 500x57
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,39) size 4x18
+      text run at (53,39) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,39) size 4x18
+      text run at (110,39) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,39) size 4x18
+      text run at (167,39) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,39) size 4x18
+      text run at (224,39) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,65) size 500x57
+  LayoutBlockFlow {DIV} at (0,57) size 500x57
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,39) size 4x18
+      text run at (53,39) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,39) size 4x18
+      text run at (110,39) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,39) size 4x18
+      text run at (167,39) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,39) size 4x18
+      text run at (224,39) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,179) size 500x57
+  LayoutBlockFlow {DIV} at (0,171) size 500x57
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,39) size 4x18
+      text run at (53,39) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,39) size 4x18
+      text run at (110,39) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,39) size 4x18
+      text run at (167,39) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,39) size 4x18
+      text run at (224,39) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,236) size 500x57
+  LayoutBlockFlow {DIV} at (0,228) size 500x57
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,39) size 4x18
+      text run at (53,39) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,39) size 4x18
+      text run at (110,39) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,39) size 4x18
+      text run at (167,39) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,39) size 4x18
+      text run at (224,39) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/fast/images/sprite-no-bleed-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/fast/images/sprite-no-bleed-expected.txt
new file mode 100644
index 0000000..a8065996
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/fast/images/sprite-no-bleed-expected.txt
@@ -0,0 +1,84 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x301
+  LayoutBlockFlow {HTML} at (0,0) size 800x301
+    LayoutBlockFlow {BODY} at (8,8) size 500x285
+      LayoutBlockFlow {DIV} at (0,114) size 500x57
+        LayoutBlockFlow {DIV} at (10,10) size 33x33
+        LayoutText {#text} at (53,39) size 4x18
+          text run at (53,39) width 4: " "
+        LayoutBlockFlow {DIV} at (67,10) size 33x33
+        LayoutText {#text} at (110,39) size 4x18
+          text run at (110,39) width 4: " "
+        LayoutBlockFlow {DIV} at (124,10) size 33x33
+        LayoutText {#text} at (167,39) size 4x18
+          text run at (167,39) width 4: " "
+        LayoutBlockFlow {DIV} at (181,10) size 33x33
+        LayoutText {#text} at (224,39) size 4x18
+          text run at (224,39) width 4: " "
+        LayoutBlockFlow {DIV} at (238,10) size 33x33
+        LayoutText {#text} at (0,0) size 0x0
+layer at (8,8) size 500x57
+  LayoutBlockFlow {DIV} at (0,0) size 500x57
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,39) size 4x18
+      text run at (53,39) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,39) size 4x18
+      text run at (110,39) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,39) size 4x18
+      text run at (167,39) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,39) size 4x18
+      text run at (224,39) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,65) size 500x57
+  LayoutBlockFlow {DIV} at (0,57) size 500x57
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,39) size 4x18
+      text run at (53,39) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,39) size 4x18
+      text run at (110,39) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,39) size 4x18
+      text run at (167,39) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,39) size 4x18
+      text run at (224,39) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,179) size 500x57
+  LayoutBlockFlow {DIV} at (0,171) size 500x57
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,39) size 4x18
+      text run at (53,39) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,39) size 4x18
+      text run at (110,39) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,39) size 4x18
+      text run at (167,39) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,39) size 4x18
+      text run at (224,39) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,236) size 500x57
+  LayoutBlockFlow {DIV} at (0,228) size 500x57
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,39) size 4x18
+      text run at (53,39) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,39) size 4x18
+      text run at (110,39) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,39) size 4x18
+      text run at (167,39) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,39) size 4x18
+      text run at (224,39) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/images/sprite-no-bleed-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/images/sprite-no-bleed-expected.txt
new file mode 100644
index 0000000..d9862aa5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/images/sprite-no-bleed-expected.txt
@@ -0,0 +1,84 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x301
+  LayoutBlockFlow {HTML} at (0,0) size 800x301
+    LayoutBlockFlow {BODY} at (8,8) size 500x285
+      LayoutBlockFlow {DIV} at (0,114) size 500x57
+        LayoutBlockFlow {DIV} at (10,10) size 33x33
+        LayoutText {#text} at (53,39) size 4x17
+          text run at (53,39) width 4: " "
+        LayoutBlockFlow {DIV} at (67,10) size 33x33
+        LayoutText {#text} at (110,39) size 4x17
+          text run at (110,39) width 4: " "
+        LayoutBlockFlow {DIV} at (124,10) size 33x33
+        LayoutText {#text} at (167,39) size 4x17
+          text run at (167,39) width 4: " "
+        LayoutBlockFlow {DIV} at (181,10) size 33x33
+        LayoutText {#text} at (224,39) size 4x17
+          text run at (224,39) width 4: " "
+        LayoutBlockFlow {DIV} at (238,10) size 33x33
+        LayoutText {#text} at (0,0) size 0x0
+layer at (8,8) size 500x57
+  LayoutBlockFlow {DIV} at (0,0) size 500x57
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,39) size 4x17
+      text run at (53,39) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,39) size 4x17
+      text run at (110,39) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,39) size 4x17
+      text run at (167,39) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,39) size 4x17
+      text run at (224,39) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,65) size 500x57
+  LayoutBlockFlow {DIV} at (0,57) size 500x57
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,39) size 4x17
+      text run at (53,39) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,39) size 4x17
+      text run at (110,39) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,39) size 4x17
+      text run at (167,39) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,39) size 4x17
+      text run at (224,39) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,179) size 500x57
+  LayoutBlockFlow {DIV} at (0,171) size 500x57
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,39) size 4x17
+      text run at (53,39) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,39) size 4x17
+      text run at (110,39) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,39) size 4x17
+      text run at (167,39) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,39) size 4x17
+      text run at (224,39) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,236) size 500x57
+  LayoutBlockFlow {DIV} at (0,228) size 500x57
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,39) size 4x17
+      text run at (53,39) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,39) size 4x17
+      text run at (110,39) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,39) size 4x17
+      text run at (167,39) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,39) size 4x17
+      text run at (224,39) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/fast/images/sprite-no-bleed-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/fast/images/sprite-no-bleed-expected.txt
new file mode 100644
index 0000000..d9862aa5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/fast/images/sprite-no-bleed-expected.txt
@@ -0,0 +1,84 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x301
+  LayoutBlockFlow {HTML} at (0,0) size 800x301
+    LayoutBlockFlow {BODY} at (8,8) size 500x285
+      LayoutBlockFlow {DIV} at (0,114) size 500x57
+        LayoutBlockFlow {DIV} at (10,10) size 33x33
+        LayoutText {#text} at (53,39) size 4x17
+          text run at (53,39) width 4: " "
+        LayoutBlockFlow {DIV} at (67,10) size 33x33
+        LayoutText {#text} at (110,39) size 4x17
+          text run at (110,39) width 4: " "
+        LayoutBlockFlow {DIV} at (124,10) size 33x33
+        LayoutText {#text} at (167,39) size 4x17
+          text run at (167,39) width 4: " "
+        LayoutBlockFlow {DIV} at (181,10) size 33x33
+        LayoutText {#text} at (224,39) size 4x17
+          text run at (224,39) width 4: " "
+        LayoutBlockFlow {DIV} at (238,10) size 33x33
+        LayoutText {#text} at (0,0) size 0x0
+layer at (8,8) size 500x57
+  LayoutBlockFlow {DIV} at (0,0) size 500x57
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,39) size 4x17
+      text run at (53,39) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,39) size 4x17
+      text run at (110,39) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,39) size 4x17
+      text run at (167,39) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,39) size 4x17
+      text run at (224,39) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,65) size 500x57
+  LayoutBlockFlow {DIV} at (0,57) size 500x57
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,39) size 4x17
+      text run at (53,39) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,39) size 4x17
+      text run at (110,39) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,39) size 4x17
+      text run at (167,39) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,39) size 4x17
+      text run at (224,39) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,179) size 500x57
+  LayoutBlockFlow {DIV} at (0,171) size 500x57
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,39) size 4x17
+      text run at (53,39) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,39) size 4x17
+      text run at (110,39) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,39) size 4x17
+      text run at (167,39) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,39) size 4x17
+      text run at (224,39) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,236) size 500x57
+  LayoutBlockFlow {DIV} at (0,228) size 500x57
+    LayoutBlockFlow {DIV} at (10,10) size 33x33
+    LayoutText {#text} at (53,39) size 4x17
+      text run at (53,39) width 4: " "
+    LayoutBlockFlow {DIV} at (67,10) size 33x33
+    LayoutText {#text} at (110,39) size 4x17
+      text run at (110,39) width 4: " "
+    LayoutBlockFlow {DIV} at (124,10) size 33x33
+    LayoutText {#text} at (167,39) size 4x17
+      text run at (167,39) width 4: " "
+    LayoutBlockFlow {DIV} at (181,10) size 33x33
+    LayoutText {#text} at (224,39) size 4x17
+      text run at (224,39) width 4: " "
+    LayoutBlockFlow {DIV} at (238,10) size 33x33
+    LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/fast/images/sprite-no-bleed-expected.png b/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/fast/images/sprite-no-bleed-expected.png
new file mode 100644
index 0000000..71f1aa4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/fast/images/sprite-no-bleed-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/webmidi/send-messages-expected.txt b/third_party/WebKit/LayoutTests/webmidi/send-messages-expected.txt
index 460a060a..45be94a 100644
--- a/third_party/WebKit/LayoutTests/webmidi/send-messages-expected.txt
+++ b/third_party/WebKit/LayoutTests/webmidi/send-messages-expected.txt
@@ -26,14 +26,14 @@
 PASS output.send([0xf2]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
 PASS output.send([0xf2, 0x00]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
 PASS output.send([0xf3]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Message is incomplete..
-PASS output.send([0x80, 0x80, 0x00]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Unexpected status byte at index at index 1 (128)..
-PASS output.send([0x80, 0x00, 0x80]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Unexpected status byte at index at index 2 (128)..
+PASS output.send([0x80, 0x80, 0x00]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Unexpected status byte at index 1 (128)..
+PASS output.send([0x80, 0x00, 0x80]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Unexpected status byte at index 2 (128)..
 PASS output.send([0xf0, 0x80, 0xf7]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': System exclusive message contains a status byte at index 1 (128)..
 PASS output.send([0xf0, 0xf0, 0xf7]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': System exclusive message contains a status byte at index 1 (240)..
 PASS output.send([0xf0, 0xff, 0xf7, 0xf7]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Unexpected end of system exclusive message at index 3 (247)..
 PASS output.send([0xf4, 0x80, 0x00, 0x00]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Reserved status is not allowed at index 0 (244)..
-PASS output.send([0x80, 0xf4, 0x00, 0x00]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Unexpected status byte at index at index 1 (244)..
-PASS output.send([0x80, 0x00, 0xf4, 0x00]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Unexpected status byte at index at index 2 (244)..
+PASS output.send([0x80, 0xf4, 0x00, 0x00]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Unexpected status byte at index 1 (244)..
+PASS output.send([0x80, 0x00, 0xf4, 0x00]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Unexpected status byte at index 2 (244)..
 PASS output.send([0x80, 0x00, 0x00, 0xf4]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': Reserved status is not allowed at index 3 (244)..
 PASS output.send([0xf0, 0xff, 0xf4, 0xf7]) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': System exclusive message contains a status byte at index 2 (244)..
 PASS output.send([], NaN) threw exception TypeError: Failed to execute 'send' on 'MIDIOutput': The provided double value is non-finite..
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptFunction.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptFunction.cpp
index 41d5aac5..c5c313e 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptFunction.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptFunction.cpp
@@ -10,10 +10,15 @@
 
 v8::Local<v8::Function> ScriptFunction::bindToV8Function()
 {
+#if ENABLE(ASSERT)
+    DCHECK(!m_bindToV8FunctionAlreadyCalled);
+    m_bindToV8FunctionAlreadyCalled = true;
+#endif
+
     v8::Isolate* isolate = m_scriptState->isolate();
     v8::Local<v8::External> wrapper = v8::External::New(isolate, this);
     m_scriptState->world().registerDOMObjectHolder(isolate, this, wrapper);
-    return createClosure(&ScriptFunction::callCallback, wrapper, isolate);
+    return v8::Function::New(m_scriptState->context(), callCallback, wrapper, 0, v8::ConstructorBehavior::kThrow).ToLocalChecked();
 }
 
 void ScriptFunction::callCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptFunction.h b/third_party/WebKit/Source/bindings/core/v8/ScriptFunction.h
index 2159275..2c30631 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptFunction.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptFunction.h
@@ -53,15 +53,19 @@
 class CORE_EXPORT ScriptFunction : public GarbageCollectedFinalized<ScriptFunction> {
 public:
     virtual ~ScriptFunction() { }
-    ScriptState* getScriptState() const { return m_scriptState.get(); }
     DEFINE_INLINE_VIRTUAL_TRACE() { }
 
 protected:
     explicit ScriptFunction(ScriptState* scriptState)
         : m_scriptState(scriptState)
+#if ENABLE(ASSERT)
+        , m_bindToV8FunctionAlreadyCalled(false)
+#endif
     {
     }
 
+    ScriptState* getScriptState() const { return m_scriptState.get(); }
+
     v8::Local<v8::Function> bindToV8Function();
 
 private:
@@ -69,6 +73,10 @@
     static void callCallback(const v8::FunctionCallbackInfo<v8::Value>&);
 
     RefPtr<ScriptState> m_scriptState;
+#if ENABLE(ASSERT)
+    // bindToV8Function must not be called twice.
+    bool m_bindToV8FunctionAlreadyCalled;
+#endif
 };
 
 } // namespace blink
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8Binding.h b/third_party/WebKit/Source/bindings/core/v8/V8Binding.h
index 906fe6f..a0ce774 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8Binding.h
+++ b/third_party/WebKit/Source/bindings/core/v8/V8Binding.h
@@ -908,12 +908,6 @@
 }
 v8::Local<v8::Function> getBoundFunction(v8::Local<v8::Function>);
 
-// Attaches |environment| to |function| and returns it.
-inline v8::Local<v8::Function> createClosure(v8::FunctionCallback function, v8::Local<v8::Value> environment, v8::Isolate* isolate)
-{
-    return v8::Function::New(isolate->GetCurrentContext(), function, environment, 0, v8::ConstructorBehavior::kThrow).ToLocalChecked();
-}
-
 // FIXME: This will be soon embedded in the generated code.
 template<typename Collection> static void indexedPropertyEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info)
 {
diff --git a/third_party/WebKit/Source/core/dom/ElementVisibilityObserver.cpp b/third_party/WebKit/Source/core/dom/ElementVisibilityObserver.cpp
index 05f57138..6c6de4357 100644
--- a/third_party/WebKit/Source/core/dom/ElementVisibilityObserver.cpp
+++ b/third_party/WebKit/Source/core/dom/ElementVisibilityObserver.cpp
@@ -4,28 +4,41 @@
 
 #include "core/dom/ElementVisibilityObserver.h"
 
+#include "bindings/core/v8/ExceptionState.h"
 #include "core/dom/Element.h"
+#include "core/dom/IntersectionObserver.h"
 #include "core/dom/IntersectionObserverEntry.h"
+#include "core/dom/IntersectionObserverInit.h"
 #include "wtf/Functional.h"
 
 namespace blink {
 
-ElementVisibilityObserver::ElementVisibilityObserver(Element* element, std::unique_ptr<VisibilityCallback> callback)
-    : m_element(element)
-    , m_callback(std::move(callback))
+ElementVisibilityObserver* ElementVisibilityObserver::create(Element* element, Client* client)
 {
+    ElementVisibilityObserver* observer = new ElementVisibilityObserver(client);
+    observer->start(element);
+    return observer;
+}
+
+ElementVisibilityObserver::ElementVisibilityObserver(Client* client)
+    : m_client(client)
+{
+    DCHECK(m_client);
 }
 
 ElementVisibilityObserver::~ElementVisibilityObserver() = default;
 
-void ElementVisibilityObserver::start()
+void ElementVisibilityObserver::start(Element* element)
 {
+    IntersectionObserverInit options;
+    DoubleOrDoubleArray threshold;
+    threshold.setDouble(std::numeric_limits<float>::min());
+    options.setThreshold(threshold);
+
     DCHECK(!m_intersectionObserver);
-    m_intersectionObserver = IntersectionObserver::create(
-        Vector<Length>(), Vector<float>({std::numeric_limits<float>::min()}), &m_element->document(),
-        WTF::bind(&ElementVisibilityObserver::onVisibilityChanged, wrapWeakPersistent(this)));
+    m_intersectionObserver = IntersectionObserver::create(options, *this, ASSERT_NO_EXCEPTION);
     DCHECK(m_intersectionObserver);
-    m_intersectionObserver->observe(m_element.release());
+    m_intersectionObserver->observe(element);
 }
 
 void ElementVisibilityObserver::stop()
@@ -34,18 +47,30 @@
 
     m_intersectionObserver->disconnect();
     m_intersectionObserver = nullptr;
+    // Client will no longer be called upon, release.
+    m_client = nullptr;
+}
+
+void ElementVisibilityObserver::handleEvent(const HeapVector<Member<IntersectionObserverEntry>>& entries, IntersectionObserver&)
+{
+    if (!m_client)
+        return;
+    bool isVisible = entries.last()->intersectionRatio() > 0.f;
+    m_client->onVisibilityChanged(isVisible);
+}
+
+ExecutionContext* ElementVisibilityObserver::getExecutionContext() const
+{
+    if (!m_client)
+        return nullptr;
+    return m_client->getElementVisibilityExecutionContext();
 }
 
 DEFINE_TRACE(ElementVisibilityObserver)
 {
-    visitor->trace(m_element);
+    visitor->trace(m_client);
     visitor->trace(m_intersectionObserver);
-}
-
-void ElementVisibilityObserver::onVisibilityChanged(const HeapVector<Member<IntersectionObserverEntry>>& entries)
-{
-    bool isVisible = entries.last()->intersectionRatio() > 0.f;
-    (*m_callback.get())(isVisible);
+    IntersectionObserverCallback::trace(visitor);
 }
 
 } // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/ElementVisibilityObserver.h b/third_party/WebKit/Source/core/dom/ElementVisibilityObserver.h
index be62f5b..7a01edf 100644
--- a/third_party/WebKit/Source/core/dom/ElementVisibilityObserver.h
+++ b/third_party/WebKit/Source/core/dom/ElementVisibilityObserver.h
@@ -5,44 +5,51 @@
 #ifndef ElementVisibilityObserver_h
 #define ElementVisibilityObserver_h
 
-#include "core/dom/IntersectionObserver.h"
-#include "platform/heap/Heap.h"
-#include "platform/heap/Member.h"
+#include "core/CoreExport.h"
+#include "core/dom/IntersectionObserverCallback.h"
+#include "platform/heap/Handle.h"
 
 namespace blink {
 
 class Element;
+class IntersectionObserver;
+class IntersectionObserverEntry;
 
 // ElementVisibilityObserver is a helper class to be used to track the
-// visibility of an Element in the viewport. Creating an
-// ElementVisibilityObserver is a no-op with regards to CPU cycle. The observing
-// has be started by calling |start()| and can be stopped with |stop()|.
-// When creating an instance, the caller will have to pass a callback taking
-// a boolean as an argument. The boolean will be the new visibility state.
-// The ElementVisibilityObserver is implemented on top of IntersectionObserver.
-// It is a layer meant to simplify the usage for C++ Blink code checking for the
-// visibility of an element.
-class ElementVisibilityObserver final : public GarbageCollectedFinalized<ElementVisibilityObserver> {
+// visibility of an Element in the viewport; it is implemented on top of
+// IntersectionObserver.
+//
+// When creating an ElementVisibilityObserver instance, alongside the element
+// reference, the caller will have to supply an object reference implementing
+// the |Client| interface and its |onVisibilityChanged| method. The callback
+// method will be invoked when the element changes visibility state,
+// the boolean argument indicating which.
+class ElementVisibilityObserver final : public IntersectionObserverCallback {
     WTF_MAKE_NONCOPYABLE(ElementVisibilityObserver);
 public:
-    using VisibilityCallback = Function<void(bool), WTF::SameThreadAffinity>;
+    class CORE_EXPORT Client : public GarbageCollectedMixin {
+    public:
+        virtual void onVisibilityChanged(bool isVisible) = 0;
+        virtual ExecutionContext* getElementVisibilityExecutionContext() const = 0;
+    };
 
-    ElementVisibilityObserver(Element*, std::unique_ptr<VisibilityCallback>);
-    virtual ~ElementVisibilityObserver();
-
-    void start();
-    void stop();
-
+    static ElementVisibilityObserver* create(Element*, Client*);
+    ~ElementVisibilityObserver();
     DECLARE_VIRTUAL_TRACE();
 
+    void stop();
+
+    // IntersectionObserverCallback implementation:
+    void handleEvent(const HeapVector<Member<IntersectionObserverEntry>>&, IntersectionObserver&) override;
+    ExecutionContext* getExecutionContext() const override;
+
 private:
-    class ElementVisibilityCallback;
+    explicit ElementVisibilityObserver(Client*);
 
-    void onVisibilityChanged(const HeapVector<Member<IntersectionObserverEntry>>&);
+    void start(Element*);
 
-    Member<Element> m_element;
+    Member<Client> m_client;
     Member<IntersectionObserver> m_intersectionObserver;
-    std::unique_ptr<VisibilityCallback> m_callback;
 };
 
 } // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/IntersectionObserver.h b/third_party/WebKit/Source/core/dom/IntersectionObserver.h
index 1dbac53..a21b08c5 100644
--- a/third_party/WebKit/Source/core/dom/IntersectionObserver.h
+++ b/third_party/WebKit/Source/core/dom/IntersectionObserver.h
@@ -30,6 +30,7 @@
     using EventCallback = Function<void(const HeapVector<Member<IntersectionObserverEntry>>&), WTF::SameThreadAffinity>;
 
     static IntersectionObserver* create(const IntersectionObserverInit&, IntersectionObserverCallback&, ExceptionState&);
+    // TODO(sof): unused, consider retiring?
     static IntersectionObserver* create(const Vector<Length>& rootMargin, const Vector<float>& thresholds, Document*, std::unique_ptr<EventCallback>);
     static void resumeSuspendedObservers();
 
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
index f8f4ba8..bb0fc955 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
@@ -1639,10 +1639,8 @@
                     // We might end up in a situation where the previous
                     // observer didn't had time to fire yet. We can avoid
                     // creating a new one in this case.
-                    if (!m_autoplayVisibilityObserver) {
-                        m_autoplayVisibilityObserver = new ElementVisibilityObserver(this, WTF::bind(&HTMLMediaElement::onVisibilityChangedForAutoplay, wrapWeakPersistent(this)));
-                        m_autoplayVisibilityObserver->start();
-                    }
+                    if (!m_autoplayVisibilityObserver)
+                        m_autoplayVisibilityObserver = ElementVisibilityObserver::create(this, this);
                 } else {
                     m_paused = false;
                     invalidateCachedTime();
@@ -3671,8 +3669,8 @@
     visitor->trace(m_srcObject);
     visitor->trace(m_autoplayVisibilityObserver);
     visitor->template registerWeakMembers<HTMLMediaElement, &HTMLMediaElement::clearWeakMembers>(this);
-    Supplementable<HTMLMediaElement>::trace(visitor);
     HTMLElement::trace(visitor);
+    Supplementable<HTMLMediaElement>::trace(visitor);
     ActiveDOMObject::trace(visitor);
 }
 
@@ -3910,7 +3908,7 @@
     autoplayUnmuteHistogram.count(status);
 }
 
-void HTMLMediaElement::onVisibilityChangedForAutoplay(bool isVisible)
+void HTMLMediaElement::onVisibilityChanged(bool isVisible)
 {
     if (!isVisible)
         return;
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.h b/third_party/WebKit/Source/core/html/HTMLMediaElement.h
index 2505616..72d6c1d0 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.h
@@ -30,6 +30,7 @@
 #include "bindings/core/v8/ScriptPromise.h"
 #include "core/CoreExport.h"
 #include "core/dom/ActiveDOMObject.h"
+#include "core/dom/ElementVisibilityObserver.h"
 #include "core/dom/ExceptionCode.h"
 #include "core/events/GenericEventQueue.h"
 #include "core/html/AutoplayExperimentHelper.h"
@@ -70,7 +71,7 @@
 class WebLayer;
 class WebRemotePlaybackClient;
 
-class CORE_EXPORT HTMLMediaElement : public HTMLElement, public Supplementable<HTMLMediaElement>, public ActiveScriptWrappable, public ActiveDOMObject, private WebMediaPlayerClient {
+class CORE_EXPORT HTMLMediaElement : public HTMLElement, public Supplementable<HTMLMediaElement>, public ActiveScriptWrappable, public ActiveDOMObject, private WebMediaPlayerClient, private ElementVisibilityObserver::Client {
     DEFINE_WRAPPERTYPEINFO();
     USING_GARBAGE_COLLECTED_MIXIN(HTMLMediaElement);
     USING_PRE_FINALIZER(HTMLMediaElement, dispose);
@@ -502,7 +503,9 @@
     void recordAutoplaySourceMetric(int source);
     void recordAutoplayUnmuteStatus(AutoplayUnmuteActionStatus);
 
-    void onVisibilityChangedForAutoplay(bool isVisible);
+    // ElementVisibilityObserver::Client implementation
+    void onVisibilityChanged(bool isVisible) override;
+    ExecutionContext* getElementVisibilityExecutionContext() const override { return getExecutionContext(); }
 
     UnthrottledTimer<HTMLMediaElement> m_loadTimer;
     UnthrottledTimer<HTMLMediaElement> m_progressEventTimer;
diff --git a/third_party/WebKit/Source/modules/webmidi/MIDIOutput.cpp b/third_party/WebKit/Source/modules/webmidi/MIDIOutput.cpp
index 28b98656..50eda06 100644
--- a/third_party/WebKit/Source/modules/webmidi/MIDIOutput.cpp
+++ b/third_party/WebKit/Source/modules/webmidi/MIDIOutput.cpp
@@ -96,7 +96,7 @@
                     if (isEndOfData())
                         exceptionState.throwTypeError("Message is incomplete.");
                     else
-                        exceptionState.throwTypeError("Unexpected status byte at index " + getPositionString());
+                        exceptionState.throwTypeError("Unexpected status byte " + getPositionString());
                     return false;
                 }
             }
diff --git a/third_party/WebKit/Source/wtf/HashTable.cpp b/third_party/WebKit/Source/wtf/HashTable.cpp
index 1b5eed2..a30f07b 100644
--- a/third_party/WebKit/Source/wtf/HashTable.cpp
+++ b/third_party/WebKit/Source/wtf/HashTable.cpp
@@ -19,12 +19,13 @@
 
 #include "wtf/HashTable.h"
 
+#if DUMP_HASHTABLE_STATS
+
 #include "wtf/DataLog.h"
+#include "wtf/ThreadingPrimitives.h"
 
 namespace WTF {
 
-#if DUMP_HASHTABLE_STATS
-
 int HashTableStats::numAccesses;
 int HashTableStats::numCollisions;
 int HashTableStats::collisionGraph[4096];
@@ -63,6 +64,6 @@
     dataLogF("%d reinserts\n", numReinserts);
 }
 
-#endif
-
 } // namespace WTF
+
+#endif
diff --git a/third_party/WebKit/Source/wtf/HashTable.h b/third_party/WebKit/Source/wtf/HashTable.h
index f6720ab..ec55677 100644
--- a/third_party/WebKit/Source/wtf/HashTable.h
+++ b/third_party/WebKit/Source/wtf/HashTable.h
@@ -32,6 +32,11 @@
 #define DUMP_HASHTABLE_STATS 0
 #define DUMP_HASHTABLE_STATS_PER_TABLE 0
 
+#if DUMP_HASHTABLE_STATS
+#include "wtf/Atomics.h"
+#include "wtf/Threading.h"
+#endif
+
 #if DUMP_HASHTABLE_STATS_PER_TABLE
 #include "wtf/DataLog.h"
 #endif
@@ -74,7 +79,7 @@
 
 #if DUMP_HASHTABLE_STATS
 
-struct HashTableStats {
+struct WTF_EXPORT HashTableStats {
     STATIC_ONLY(HashTableStats);
     // The following variables are all atomically incremented when modified.
     static int numAccesses;
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/factory.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/factory.py
index e50f3d4e..4045d66 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/factory.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/factory.py
@@ -56,7 +56,7 @@
         optparse.make_option('--debug', action='store_const', const='Debug', dest="configuration",
                              help='Set the configuration to Debug'),
         optparse.make_option("-t", "--target", dest="target",
-                             help="specify the target configuration to use (Debug/Release)"),
+                             help="Specify the target build subdirectory under src/out/"),
         optparse.make_option('--release', action='store_const', const='Release', dest="configuration",
                              help='Set the configuration to Release'),
     ]
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py b/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py
index 53ae40e..341d7ec 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py
@@ -81,6 +81,8 @@
                                  help='Set the configuration to Debug'),
             optparse.make_option('--release', action='store_const', const='Release', dest="configuration",
                                  help='Set the configuration to Release'),
+            optparse.make_option('-t', '--target', dest='configuration',
+                                 help='Specify the target build subdirectory under src/out/'),
             optparse.make_option("--platform",
                                  help="Specify port/platform being tested (e.g. mac)"),
             optparse.make_option("--chromium",
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index c6ba576..35c0485 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -26585,6 +26585,18 @@
   </summary>
 </histogram>
 
+<histogram name="Net.CertificateTransparency.CanInclusionCheckSCT"
+    enum="SCTCanBeChecked">
+  <owner>eranm@chromium.org</owner>
+  <summary>
+    Whether an observed Signed Certificate Timestamp (SCT) can be checked for
+    inclusion. An SCT can be checked for inclusion if the client has a valid
+    Signed Tree Head (STH) and the STH currently known to the client was issued
+    24 hours after the timestamp in the SCT (24 hours being the typical Maximum
+    Merge Delay).
+  </summary>
+</histogram>
+
 <histogram name="Net.CertificateTransparency.MainFrameValidSCTCount">
   <owner>eranm@chromium.org</owner>
   <summary>
@@ -73127,6 +73139,7 @@
       label="Disable Certificate Transparency enforcement for these sites"/>
   <int value="336"
       label="Configure the list of installed apps on the login screen"/>
+  <int value="337" label="Enable Android Backup Service"/>
 </enum>
 
 <enum name="EnterprisePolicyInvalidations" type="int">
@@ -89825,6 +89838,12 @@
   <int value="1" label="Scroll on main-thread"/>
 </enum>
 
+<enum name="SCTCanBeChecked" type="int">
+  <int value="0" label="No valid STH"/>
+  <int value="1" label="Requires newer STH"/>
+  <int value="2" label="Can be checked for inclusion"/>
+</enum>
+
 <enum name="SCTOrigin" type="int">
   <int value="0" label="SCT_EMBEDDED"/>
   <int value="1" label="SCT_FROM_TLS_EXTENSION"/>
@@ -100885,7 +100904,7 @@
   <affected-histogram name="SimpleCache.SyncOpenResult"/>
 </histogram_suffixes>
 
-<histogram_suffixes name="SkiaDrawScaleFactorFilterQuality">
+<histogram_suffixes name="SkiaDrawScaleFactorFilterQuality" separator=".">
   <affected-histogram name="Skia.DrawScaleFactor"/>
   <suffix name="AnyFilterQuality" label="Any filter quality was used."/>
   <suffix name="HighFilterQuality" label="High filter quality was used."/>
diff --git a/ui/gl/init/OWNERS b/ui/gl/init/OWNERS
new file mode 100644
index 0000000..19e118f
--- /dev/null
+++ b/ui/gl/init/OWNERS
@@ -0,0 +1,4 @@
+per-file *_ozone*=alexst@chromium.org
+per-file *_ozone*=dnicoara@chromium.org
+per-file *_ozone*=spang@chromium.org
+per-file *_ozone*=kylechar@chromium.org
diff --git a/ui/gl/init/gl_factory_ozone.cc b/ui/gl/init/gl_factory_ozone.cc
index 097f249a..9df9e01 100644
--- a/ui/gl/init/gl_factory_ozone.cc
+++ b/ui/gl/init/gl_factory_ozone.cc
@@ -24,6 +24,79 @@
 namespace gl {
 namespace init {
 
+namespace {
+
+ui::SurfaceFactoryOzone* GetSurfaceFactory() {
+  return ui::OzonePlatform::GetInstance()->GetSurfaceFactoryOzone();
+}
+
+bool HasDefaultImplementation(GLImplementation impl) {
+  return impl == kGLImplementationOSMesaGL || impl == kGLImplementationMockGL;
+}
+
+scoped_refptr<GLSurface> CreateDefaultViewGLSurface(
+    gfx::AcceleratedWidget window) {
+  switch (GetGLImplementation()) {
+    case kGLImplementationOSMesaGL:
+      return InitializeGLSurface(new GLSurfaceOSMesaHeadless());
+    case kGLImplementationMockGL:
+      return InitializeGLSurface(new GLSurfaceStub());
+    default:
+      NOTREACHED();
+  }
+  return nullptr;
+}
+
+scoped_refptr<GLSurface> CreateDefaultOffscreenGLSurface(
+    const gfx::Size& size) {
+  switch (GetGLImplementation()) {
+    case kGLImplementationOSMesaGL:
+      return InitializeGLSurface(
+          new GLSurfaceOSMesa(GLSurface::SURFACE_OSMESA_BGRA, size));
+    case kGLImplementationMockGL:
+      return InitializeGLSurface(new GLSurfaceStub);
+    default:
+      NOTREACHED();
+  }
+  return nullptr;
+}
+
+// TODO(kylechar): Remove when all implementations are switched over.
+scoped_refptr<GLSurface> CreateViewGLSurfaceOld(gfx::AcceleratedWidget window) {
+  switch (GetGLImplementation()) {
+    case kGLImplementationEGLGLES2: {
+      DCHECK_NE(window, gfx::kNullAcceleratedWidget);
+      scoped_refptr<GLSurface> surface;
+      if (!surface && GLSurfaceEGL::IsEGLSurfacelessContextSupported())
+        surface = CreateViewGLSurfaceOzoneSurfacelessSurfaceImpl(window);
+      if (!surface)
+        surface = CreateViewGLSurfaceOzone(window);
+      return surface;
+    }
+    default:
+      NOTREACHED();
+      return nullptr;
+  }
+}
+
+// TODO(kylechar): Remove when all implementations are switched over.
+scoped_refptr<GLSurface> CreateOffscreenGLSurfaceOld(const gfx::Size& size) {
+  switch (GetGLImplementation()) {
+    case kGLImplementationEGLGLES2:
+      if (GLSurfaceEGL::IsEGLSurfacelessContextSupported() &&
+          (size.width() == 0 && size.height() == 0)) {
+        return InitializeGLSurface(new SurfacelessEGL(size));
+      } else {
+        return InitializeGLSurface(new PbufferGLSurfaceEGL(size));
+      }
+    default:
+      NOTREACHED();
+  }
+  return nullptr;
+}
+
+}  // namespace
+
 scoped_refptr<GLContext> CreateGLContext(GLShareGroup* share_group,
                                          GLSurface* compatible_surface,
                                          GpuPreference gpu_preference) {
@@ -46,26 +119,19 @@
 
 scoped_refptr<GLSurface> CreateViewGLSurface(gfx::AcceleratedWidget window) {
   TRACE_EVENT0("gpu", "gl::init::CreateViewGLSurface");
-  switch (GetGLImplementation()) {
-    case kGLImplementationOSMesaGL:
-      return InitializeGLSurface(new GLSurfaceOSMesaHeadless());
-    case kGLImplementationEGLGLES2: {
-      DCHECK_NE(window, gfx::kNullAcceleratedWidget);
-      scoped_refptr<GLSurface> surface;
-      if (GLSurfaceEGL::IsEGLSurfacelessContextSupported())
-        surface = CreateViewGLSurfaceOzoneSurfacelessSurfaceImpl(window);
-      if (!surface)
-        surface = CreateViewGLSurfaceOzone(window);
-      return surface;
-    }
-    case kGLImplementationMockGL:
-      return InitializeGLSurface(new GLSurfaceStub());
-    default:
-      NOTREACHED();
-      return nullptr;
-  }
+
+  if (HasDefaultImplementation(GetGLImplementation()))
+    return CreateDefaultViewGLSurface(window);
+
+  // TODO(kylechar): This is deprecated, remove when possible.
+  if (!GetSurfaceFactory()->UseNewSurfaceAPI())
+    return CreateViewGLSurfaceOld(window);
+
+  return GetSurfaceFactory()->CreateViewGLSurface(GetGLImplementation(),
+                                                  window);
 }
 
+// TODO(kylechar): Update to use new API.
 scoped_refptr<GLSurface> CreateSurfacelessViewGLSurface(
     gfx::AcceleratedWidget window) {
   TRACE_EVENT0("gpu", "gl::init::CreateSurfacelessViewGLSurface");
@@ -79,23 +145,16 @@
 
 scoped_refptr<GLSurface> CreateOffscreenGLSurface(const gfx::Size& size) {
   TRACE_EVENT0("gpu", "gl::init::CreateOffscreenGLSurface");
-  switch (GetGLImplementation()) {
-    case kGLImplementationOSMesaGL:
-      return InitializeGLSurface(
-          new GLSurfaceOSMesa(GLSurface::SURFACE_OSMESA_BGRA, size));
-    case kGLImplementationEGLGLES2:
-      if (GLSurfaceEGL::IsEGLSurfacelessContextSupported() &&
-          (size.width() == 0 && size.height() == 0)) {
-        return InitializeGLSurface(new SurfacelessEGL(size));
-      } else {
-        return InitializeGLSurface(new PbufferGLSurfaceEGL(size));
-      }
-    case kGLImplementationMockGL:
-      return new GLSurfaceStub;
-    default:
-      NOTREACHED();
-      return nullptr;
-  }
+
+  if (HasDefaultImplementation(GetGLImplementation()))
+    return CreateDefaultOffscreenGLSurface(size);
+
+  // TODO(kylechar): This is deprecated, remove when possible.
+  if (!GetSurfaceFactory()->UseNewSurfaceAPI())
+    return CreateOffscreenGLSurfaceOld(size);
+
+  return GetSurfaceFactory()->CreateOffscreenGLSurface(GetGLImplementation(),
+                                                       size);
 }
 
 }  // namespace init
diff --git a/ui/ozone/platform/x11/BUILD.gn b/ui/ozone/platform/x11/BUILD.gn
index 2d086772..e3ed897 100644
--- a/ui/ozone/platform/x11/BUILD.gn
+++ b/ui/ozone/platform/x11/BUILD.gn
@@ -33,6 +33,7 @@
     "//ui/gfx",
     "//ui/gfx/geometry",
     "//ui/gfx/x",
+    "//ui/gl",
     "//ui/ozone:ozone_base",
     "//ui/ozone/common",
     "//ui/platform_window",
diff --git a/ui/ozone/platform/x11/x11_surface_factory.cc b/ui/ozone/platform/x11/x11_surface_factory.cc
index 7dcc6db..986dda1 100644
--- a/ui/ozone/platform/x11/x11_surface_factory.cc
+++ b/ui/ozone/platform/x11/x11_surface_factory.cc
@@ -6,54 +6,39 @@
 
 #include <X11/Xlib.h>
 
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
 #include "third_party/khronos/EGL/egl.h"
-#include "ui/gfx/vsync_provider.h"
 #include "ui/gfx/x/x11_types.h"
+#include "ui/gl/egl_util.h"
+#include "ui/gl/gl_surface_egl.h"
 #include "ui/ozone/common/egl_util.h"
-#include "ui/ozone/public/surface_ozone_egl.h"
 
 namespace ui {
 
 namespace {
 
-class X11SurfaceEGL : public SurfaceOzoneEGL {
+// GLSurface implementation for Ozone X11 EGL.
+class GLSurfaceEGLOzoneX11 : public gl::NativeViewGLSurfaceEGL {
  public:
-  explicit X11SurfaceEGL(gfx::AcceleratedWidget widget) : widget_(widget) {}
-  ~X11SurfaceEGL() override {}
+  explicit GLSurfaceEGLOzoneX11(EGLNativeWindowType window);
 
-  intptr_t GetNativeWindow() override { return widget_; }
-
-  bool OnSwapBuffers() override { return true; }
-
-  void OnSwapBuffersAsync(const SwapCompletionCallback& callback) override {
-    NOTREACHED();
-  }
-
-  bool ResizeNativeWindow(const gfx::Size& viewport_size) override {
-    return true;
-  }
-
-  std::unique_ptr<gfx::VSyncProvider> CreateVSyncProvider() override {
-    return nullptr;
-  }
-
-  void* /* EGLConfig */ GetEGLSurfaceConfig(
-      const EglConfigCallbacks& egl) override;
+  // gl::NativeViewGLSurfaceEGL:
+  EGLConfig GetConfig() override;
+  bool Resize(const gfx::Size& size,
+              float scale_factor,
+              bool has_alpha) override;
 
  private:
-  gfx::AcceleratedWidget widget_;
+  ~GLSurfaceEGLOzoneX11() override;
 
-  DISALLOW_COPY_AND_ASSIGN(X11SurfaceEGL);
+  DISALLOW_COPY_AND_ASSIGN(GLSurfaceEGLOzoneX11);
 };
 
-void* /* EGLConfig */ X11SurfaceEGL::GetEGLSurfaceConfig(
-    const EglConfigCallbacks& egl) {
-  // Try matching the window depth with an alpha channel,
-  // because we're worried the destination alpha width could
-  // constrain blending precision.
-  EGLConfig config;
+GLSurfaceEGLOzoneX11::GLSurfaceEGLOzoneX11(EGLNativeWindowType window)
+    : NativeViewGLSurfaceEGL(window) {}
+
+EGLConfig GLSurfaceEGLOzoneX11::GetConfig() {
+  // Try matching the window depth with an alpha channel, because we're worried
+  // the destination alpha width could constrain blending precision.
   const int kBufferSizeOffset = 1;
   const int kAlphaSizeOffset = 3;
   EGLint config_attribs[] = {EGL_BUFFER_SIZE,
@@ -72,23 +57,27 @@
                              EGL_WINDOW_BIT,
                              EGL_NONE};
 
-  // Get the depth of XWindow for surface
+  // Get the depth of XWindow for surface.
   XWindowAttributes win_attribs;
-  if (XGetWindowAttributes(gfx::GetXDisplay(), widget_, &win_attribs)) {
+  if (XGetWindowAttributes(gfx::GetXDisplay(), window_, &win_attribs)) {
     config_attribs[kBufferSizeOffset] = win_attribs.depth;
   }
 
+  EGLDisplay display = GetDisplay();
+
+  EGLConfig config;
   EGLint num_configs;
-  if (!egl.choose_config.Run(config_attribs, &config, 1, &num_configs)) {
+  if (!eglChooseConfig(display, config_attribs, &config, 1, &num_configs)) {
     LOG(ERROR) << "eglChooseConfig failed with error "
-               << egl.get_last_error_string.Run();
+               << GetLastEGLErrorString();
     return nullptr;
   }
+
   if (num_configs > 0) {
     EGLint config_depth;
-    if (!egl.get_config_attribute.Run(config, EGL_BUFFER_SIZE, &config_depth)) {
+    if (!eglGetConfigAttrib(display, config, EGL_BUFFER_SIZE, &config_depth)) {
       LOG(ERROR) << "eglGetConfigAttrib failed with error "
-                 << egl.get_last_error_string.Run();
+                 << GetLastEGLErrorString();
       return nullptr;
     }
     if (config_depth == config_attribs[kBufferSizeOffset]) {
@@ -98,11 +87,12 @@
 
   // Try without an alpha channel.
   config_attribs[kAlphaSizeOffset] = 0;
-  if (!egl.choose_config.Run(config_attribs, &config, 1, &num_configs)) {
+  if (!eglChooseConfig(display, config_attribs, &config, 1, &num_configs)) {
     LOG(ERROR) << "eglChooseConfig failed with error "
-               << egl.get_last_error_string.Run();
+               << GetLastEGLErrorString();
     return nullptr;
   }
+
   if (num_configs == 0) {
     LOG(ERROR) << "No suitable EGL configs found.";
     return nullptr;
@@ -110,15 +100,55 @@
   return config;
 }
 
+bool GLSurfaceEGLOzoneX11::Resize(const gfx::Size& size,
+                                  float scale_factor,
+                                  bool has_alpha) {
+  if (size == GetSize())
+    return true;
+
+  size_ = size;
+
+  eglWaitGL();
+  XResizeWindow(gfx::GetXDisplay(), window_, size.width(), size.height());
+  eglWaitNative(EGL_CORE_NATIVE_ENGINE);
+
+  return true;
+}
+
+GLSurfaceEGLOzoneX11::~GLSurfaceEGLOzoneX11() {
+  Destroy();
+}
+
 }  // namespace
 
 X11SurfaceFactory::X11SurfaceFactory() {}
 
 X11SurfaceFactory::~X11SurfaceFactory() {}
 
-std::unique_ptr<SurfaceOzoneEGL> X11SurfaceFactory::CreateEGLSurfaceForWidget(
+bool X11SurfaceFactory::UseNewSurfaceAPI() {
+  return true;
+}
+
+scoped_refptr<gl::GLSurface> X11SurfaceFactory::CreateViewGLSurface(
+    gl::GLImplementation implementation,
     gfx::AcceleratedWidget widget) {
-  return base::WrapUnique(new X11SurfaceEGL(widget));
+  if (implementation != gl::kGLImplementationEGLGLES2) {
+    NOTREACHED();
+    return nullptr;
+  }
+
+  return gl::InitializeGLSurface(new GLSurfaceEGLOzoneX11(widget));
+}
+
+scoped_refptr<gl::GLSurface> X11SurfaceFactory::CreateOffscreenGLSurface(
+    gl::GLImplementation implementation,
+    const gfx::Size& size) {
+  if (implementation != gl::kGLImplementationEGLGLES2) {
+    NOTREACHED();
+    return nullptr;
+  }
+
+  return gl::InitializeGLSurface(new gl::PbufferGLSurfaceEGL(size));
 }
 
 bool X11SurfaceFactory::LoadEGLGLES2Bindings(
diff --git a/ui/ozone/platform/x11/x11_surface_factory.h b/ui/ozone/platform/x11/x11_surface_factory.h
index 9c28046..93620ad4 100644
--- a/ui/ozone/platform/x11/x11_surface_factory.h
+++ b/ui/ozone/platform/x11/x11_surface_factory.h
@@ -7,6 +7,8 @@
 
 #include <memory>
 
+#include "base/macros.h"
+#include "ui/gl/gl_surface.h"
 #include "ui/ozone/public/surface_factory_ozone.h"
 
 namespace ui {
@@ -18,9 +20,13 @@
   ~X11SurfaceFactory() override;
 
   // SurfaceFactoryOzone:
-
-  std::unique_ptr<SurfaceOzoneEGL> CreateEGLSurfaceForWidget(
+  bool UseNewSurfaceAPI() override;
+  scoped_refptr<gl::GLSurface> CreateViewGLSurface(
+      gl::GLImplementation implementation,
       gfx::AcceleratedWidget widget) override;
+  scoped_refptr<gl::GLSurface> CreateOffscreenGLSurface(
+      gl::GLImplementation implementation,
+      const gfx::Size& size) override;
   bool LoadEGLGLES2Bindings(
       AddGLLibraryCallback add_gl_library,
       SetGLGetProcAddressProcCallback set_gl_get_proc_address) override;
diff --git a/ui/views/controls/button/image_button.cc b/ui/views/controls/button/image_button.cc
index a9fda37..483d971bb 100644
--- a/ui/views/controls/button/image_button.cc
+++ b/ui/views/controls/button/image_button.cc
@@ -215,7 +215,7 @@
   toggled_ = toggled;
   SchedulePaint();
 
-  NotifyAccessibilityEvent(ui::AX_EVENT_VALUE_CHANGED, true);
+  NotifyAccessibilityEvent(ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED, true);
 }
 
 void ToggleImageButton::SetToggledImage(ButtonState image_state,
@@ -270,6 +270,15 @@
 void ToggleImageButton::GetAccessibleState(ui::AXViewState* state) {
   ImageButton::GetAccessibleState(state);
   GetTooltipText(gfx::Point(), &state->name);
+
+  // Use the visual pressed image as a cue for making this control into an
+  // accessible toggle button.
+  if ((toggled_ && !images_[ButtonState::STATE_NORMAL].isNull()) ||
+      (!toggled_ && !alternate_images_[ButtonState::STATE_NORMAL].isNull())) {
+    state->role = ui::AX_ROLE_TOGGLE_BUTTON;
+    if (toggled_)
+      state->AddStateFlag(ui::AX_STATE_PRESSED);
+  }
 }
 
 }  // namespace views