diff --git a/DEPS b/DEPS
index 865f2b3..be83482 100644
--- a/DEPS
+++ b/DEPS
@@ -91,7 +91,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': 'f7df8568ae1f63ee17f08a73b49261afe9f7ea8c',
+  'catapult_revision': '184187257422cb5901d928ae75b3caa6b9b1ae42',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
diff --git a/android_webview/browser/browser_view_renderer.cc b/android_webview/browser/browser_view_renderer.cc
index 121512c..f9c9d19 100644
--- a/android_webview/browser/browser_view_renderer.cc
+++ b/android_webview/browser/browser_view_renderer.cc
@@ -263,7 +263,7 @@
   DCHECK(compositor_frame_consumer);
   if (compositor_frame_consumer != current_compositor_frame_consumer_)
     return;
-  PostInvalidate();
+  PostInvalidate(compositor_);
   external_draw_constraints_ =
       current_compositor_frame_consumer_->GetParentDrawConstraintsOnUI();
   UpdateMemoryPolicy();
@@ -359,7 +359,7 @@
 
   clear_view_ = true;
   // Always invalidate ignoring the compositor to actually clear the webview.
-  PostInvalidate();
+  PostInvalidate(compositor_);
 }
 
 void BrowserViewRenderer::SetOffscreenPreRaster(bool enable) {
@@ -570,10 +570,14 @@
   }
 }
 
-void BrowserViewRenderer::DidUpdateContent() {
+void BrowserViewRenderer::DidUpdateContent(
+    content::SynchronousCompositor* compositor) {
   TRACE_EVENT_INSTANT0("android_webview",
                        "BrowserViewRenderer::DidUpdateContent",
                        TRACE_EVENT_SCOPE_THREAD);
+  if (compositor != compositor_)
+    return;
+
   clear_view_ = false;
   if (on_new_picture_enable_)
     client_->OnNewPicture();
@@ -608,12 +612,16 @@
 }
 
 void BrowserViewRenderer::UpdateRootLayerState(
+    content::SynchronousCompositor* compositor,
     const gfx::Vector2dF& total_scroll_offset_dip,
     const gfx::Vector2dF& max_scroll_offset_dip,
     const gfx::SizeF& scrollable_size_dip,
     float page_scale_factor,
     float min_page_scale_factor,
     float max_page_scale_factor) {
+  if (compositor != compositor_)
+    return;
+
   TRACE_EVENT_INSTANT1(
       "android_webview",
       "BrowserViewRenderer::UpdateRootLayerState",
@@ -666,9 +674,13 @@
 }
 
 void BrowserViewRenderer::DidOverscroll(
+    content::SynchronousCompositor* compositor,
     const gfx::Vector2dF& accumulated_overscroll,
     const gfx::Vector2dF& latest_overscroll_delta,
     const gfx::Vector2dF& current_fling_velocity) {
+  if (compositor != compositor_)
+    return;
+
   const float physical_pixel_scale = dip_scale_ * page_scale_factor_;
   if (accumulated_overscroll == latest_overscroll_delta)
     overscroll_rounding_error_ = gfx::Vector2dF();
@@ -684,9 +696,13 @@
   client_->DidOverscroll(rounded_overscroll_delta, fling_velocity_pixels);
 }
 
-void BrowserViewRenderer::PostInvalidate() {
+void BrowserViewRenderer::PostInvalidate(
+    content::SynchronousCompositor* compositor) {
   TRACE_EVENT_INSTANT0("android_webview", "BrowserViewRenderer::PostInvalidate",
                        TRACE_EVENT_SCOPE_THREAD);
+  if (compositor != compositor_)
+    return;
+
   client_->PostInvalidate();
 }
 
diff --git a/android_webview/browser/browser_view_renderer.h b/android_webview/browser/browser_view_renderer.h
index f7574e8e..37e21742 100644
--- a/android_webview/browser/browser_view_renderer.h
+++ b/android_webview/browser/browser_view_renderer.h
@@ -116,15 +116,17 @@
   void DidDestroyCompositor(content::SynchronousCompositor* compositor,
                             int process_id,
                             int routing_id) override;
-  void PostInvalidate() override;
-  void DidUpdateContent() override;
-  void UpdateRootLayerState(const gfx::Vector2dF& total_scroll_offset_dip,
+  void PostInvalidate(content::SynchronousCompositor* compositor) override;
+  void DidUpdateContent(content::SynchronousCompositor* compositor) override;
+  void UpdateRootLayerState(content::SynchronousCompositor* compositor,
+                            const gfx::Vector2dF& total_scroll_offset_dip,
                             const gfx::Vector2dF& max_scroll_offset_dip,
                             const gfx::SizeF& scrollable_size_dip,
                             float page_scale_factor,
                             float min_page_scale_factor,
                             float max_page_scale_factor) override;
-  void DidOverscroll(const gfx::Vector2dF& accumulated_overscroll,
+  void DidOverscroll(content::SynchronousCompositor* compositor,
+                     const gfx::Vector2dF& accumulated_overscroll,
                      const gfx::Vector2dF& latest_overscroll_delta,
                      const gfx::Vector2dF& current_fling_velocity) override;
 
@@ -153,7 +155,6 @@
 
   void UpdateMemoryPolicy();
 
-  uint32_t GetCompositorID(content::SynchronousCompositor* compositor);
   // For debug tracing or logging. Return the string representation of this
   // view renderer's state.
   std::string ToString() const;
diff --git a/android_webview/browser/browser_view_renderer_unittest.cc b/android_webview/browser/browser_view_renderer_unittest.cc
index 1bcfd0f..b3640f27 100644
--- a/android_webview/browser/browser_view_renderer_unittest.cc
+++ b/android_webview/browser/browser_view_renderer_unittest.cc
@@ -20,7 +20,9 @@
 namespace android_webview {
 
 class SmokeTest : public RenderingTest {
-  void StartTest() override { browser_view_renderer_->PostInvalidate(); }
+  void StartTest() override {
+    browser_view_renderer_->PostInvalidate(compositor_.get());
+  }
 
   void DidDrawOnRT() override { EndTest(); }
 };
@@ -32,7 +34,7 @@
   ClearViewTest() : on_draw_count_(0) {}
 
   void StartTest() override {
-    browser_view_renderer_->PostInvalidate();
+    browser_view_renderer_->PostInvalidate(compositor_.get());
     browser_view_renderer_->ClearView();
   }
 
@@ -41,8 +43,9 @@
     if (on_draw_count_ == 1) {
       // First OnDraw should be skipped due to ClearView.
       EXPECT_FALSE(success);
-      browser_view_renderer_->DidUpdateContent();  // Unset ClearView.
-      browser_view_renderer_->PostInvalidate();
+      browser_view_renderer_->DidUpdateContent(
+          compositor_.get());  // Unset ClearView.
+      browser_view_renderer_->PostInvalidate(compositor_.get());
     } else {
       // Following OnDraws should succeed.
       EXPECT_TRUE(success);
@@ -65,7 +68,7 @@
     new_constraints_ = ParentCompositorDrawConstraints(
         false, gfx::Transform(), window_->surface_size().IsEmpty());
     new_constraints_.transform.Scale(2.0, 2.0);
-    browser_view_renderer_->PostInvalidate();
+    browser_view_renderer_->PostInvalidate(compositor_.get());
   }
 
   void WillOnDraw() override {
@@ -157,7 +160,7 @@
   CompositorNoFrameTest() : on_draw_count_(0) {}
 
   void StartTest() override {
-    browser_view_renderer_->PostInvalidate();
+    browser_view_renderer_->PostInvalidate(compositor_.get());
   }
 
   void WillOnDraw() override {
@@ -175,11 +178,11 @@
     if (0 == on_draw_count_) {
       // Should fail as there has been no frames from compositor.
       EXPECT_FALSE(success);
-      browser_view_renderer_->PostInvalidate();
+      browser_view_renderer_->PostInvalidate(compositor_.get());
     } else if (1 == on_draw_count_) {
       // Should succeed with frame from compositor.
       EXPECT_TRUE(success);
-      browser_view_renderer_->PostInvalidate();
+      browser_view_renderer_->PostInvalidate(compositor_.get());
     } else if (2 == on_draw_count_) {
       // Should still succeed with last frame, even if no frame from compositor.
       EXPECT_TRUE(success);
@@ -242,7 +245,7 @@
   bool AdvanceFrame() {
     next_frame_ = GetFrame(frame_number_++);
     if (next_frame_) {
-      browser_view_renderer_->PostInvalidate();
+      browser_view_renderer_->PostInvalidate(compositor_.get());
       return true;
     }
     return false;
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 7632d43d..89c6797 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -358,6 +358,7 @@
     "//components/user_manager",
     "//content/public/browser",
     "//content/test:test_support",
+    "//mojo/edk/system",
     "//skia",
     "//testing/gtest",
     "//third_party/icu",
diff --git a/ash/test/DEPS b/ash/test/DEPS
index 73c7fc62..c1cc0f73 100644
--- a/ash/test/DEPS
+++ b/ash/test/DEPS
@@ -2,6 +2,7 @@
   "+content/public/test/web_contents_tester.h",
   "+content/public/test/test_browser_thread_bundle.h",
   "+content/public/test/test_browser_context.h",
+  "+mojo/edk",
   "+win8/viewer",
 ]
 
diff --git a/ash/test/ash_unittests.cc b/ash/test/ash_unittests.cc
index 9365587..91fcbfc 100644
--- a/ash/test/ash_unittests.cc
+++ b/ash/test/ash_unittests.cc
@@ -5,10 +5,12 @@
 #include "ash/test/test_suite.h"
 #include "base/bind.h"
 #include "base/test/launcher/unit_test_launcher.h"
+#include "mojo/edk/embedder/embedder.h"
 
 int main(int argc, char** argv) {
   ash::test::AuraShellTestSuite test_suite(argc, argv);
 
+  mojo::edk::Init();
   return base::LaunchUnitTests(argc, argv,
                                base::Bind(&ash::test::AuraShellTestSuite::Run,
                                           base::Unretained(&test_suite)));
diff --git a/ash/wm/system_modal_container_layout_manager.cc b/ash/wm/system_modal_container_layout_manager.cc
index 8a9e859..8446cfd 100644
--- a/ash/wm/system_modal_container_layout_manager.cc
+++ b/ash/wm/system_modal_container_layout_manager.cc
@@ -11,6 +11,7 @@
 #include "ash/shell.h"
 #include "ash/wm/dim_window.h"
 #include "ash/wm/window_util.h"
+#include "base/stl_util.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/capture_client.h"
 #include "ui/aura/window.h"
@@ -87,9 +88,13 @@
   if (key != aura::client::kModalKey)
     return;
 
-  if (window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_NONE) {
+  ui::ModalType new_modal = window->GetProperty(aura::client::kModalKey);
+  if (static_cast<ui::ModalType>(old) == new_modal)
+    return;
+
+  if (new_modal != ui::MODAL_TYPE_NONE) {
     AddModalWindow(window);
-  } else if (static_cast<ui::ModalType>(old) != ui::MODAL_TYPE_NONE) {
+  } else {
     RemoveModalWindow(window);
     Shell::GetInstance()->OnModalWindowRemoved(window);
   }
@@ -185,6 +190,8 @@
     if (capture_window)
       capture_window->ReleaseCapture();
   }
+  DCHECK(!ContainsValue(modal_windows_, window));
+
   modal_windows_.push_back(window);
   Shell::GetInstance()->CreateModalBackground(window);
   window->parent()->StackChildAtTop(window);
diff --git a/ash/wm/system_modal_container_layout_manager_unittest.cc b/ash/wm/system_modal_container_layout_manager_unittest.cc
index c1579cf..f9b53e0 100644
--- a/ash/wm/system_modal_container_layout_manager_unittest.cc
+++ b/ash/wm/system_modal_container_layout_manager_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "ash/common/session/session_state_delegate.h"
 #include "ash/common/shell_window_ids.h"
+#include "ash/common/wm_shell.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
@@ -13,6 +14,7 @@
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/run_loop.h"
+#include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/compositor/layer.h"
@@ -648,5 +650,31 @@
   ShowKeyboard(false);
 }
 
+TEST_F(SystemModalContainerLayoutManagerTest, UpdateModalType) {
+  aura::Window* modal_container = Shell::GetContainer(
+      Shell::GetPrimaryRootWindow(), kShellWindowId_SystemModalContainer);
+  views::Widget* widget = views::Widget::CreateWindowWithParent(
+      new TestWindow(false), modal_container);
+  widget->Show();
+  aura::Window* window = widget->GetNativeWindow();
+  EXPECT_FALSE(WmShell::Get()->IsSystemModalWindowOpen());
+
+  window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
+  EXPECT_TRUE(WmShell::Get()->IsSystemModalWindowOpen());
+
+  // Setting twice should not cause error.
+  window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
+  EXPECT_TRUE(WmShell::Get()->IsSystemModalWindowOpen());
+
+  window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_NONE);
+  EXPECT_FALSE(WmShell::Get()->IsSystemModalWindowOpen());
+
+  window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
+  EXPECT_TRUE(WmShell::Get()->IsSystemModalWindowOpen());
+
+  widget->Close();
+  EXPECT_FALSE(WmShell::Get()->IsSystemModalWindowOpen());
+}
+
 }  // namespace test
 }  // namespace ash
diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn
index 1445e4af..6376732 100644
--- a/build/config/BUILDCONFIG.gn
+++ b/build/config/BUILDCONFIG.gn
@@ -537,6 +537,12 @@
   configs = _executable_configs
 }
 
+if (is_ios) {
+  set_defaults("ios_app_bundle") {
+    configs = _executable_configs
+  }
+}
+
 # Static library defaults.
 set_defaults("static_library") {
   configs = _native_compiler_configs
diff --git a/chrome/VERSION b/chrome/VERSION
index 1b2f145..f2040b0 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=53
 MINOR=0
-BUILD=2771
+BUILD=2772
 PATCH=0
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index faa2d14..7b8f074 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -746,6 +746,15 @@
   <message name="IDS_FILE_BROWSER_MANY_ENTRIES_SELECTED" desc="Many items (both files and directories) selected.">
     <ph name="SELCTED_FILES_COUNT">$1<ex>21</ex></ph> items selected
   </message>
+  <message name="IDS_FILE_BROWSER_METADATA_BOX_FILE_SIZE" desc="Label fo file size.">
+    Size
+  </message>
+  <message name="IDS_FILE_BROWSER_METADATA_BOX_GENERAL_INFO" desc="Category label for general file info.">
+    General Info
+  </message>
+  <message name="IDS_FILE_BROWSER_METADATA_BOX_MODIFICATION_TIME" desc="Label for file modification time.">
+    Modified Time
+  </message>
   <message name="IDS_FILE_BROWSER_CALCULATING_SIZE" desc="Message informing the user that total items size is being calculated.">
     Calculating size
   </message>
@@ -2545,6 +2554,12 @@
   <message name="IDS_OPTIONS_SETTINGS_STORAGE_MANAGER_TAB_TITLE" desc="Title for the storage manager tab.">
     Storage
   </message>
+  <message name="IDS_OPTIONS_SETTINGS_STORAGE_CLEAR_DRIVE_CACHE_TAB_TITLE" desc="Title for the storage manager confirmation dialog tab to remove Google Drive offline files.">
+    Offline files
+  </message>
+  <message name="IDS_OPTIONS_SETTINGS_STORAGE_CLEAR_DRIVE_CACHE_DESCRIPTION" desc="Description in the storage manager confirmation dialog to remove Google Drive offline files.">
+    You can free up space by deleting Google Drive offline files. Files marked as "Available offline" or in use will not be deleted.
+  </message>
   <message name="IDS_OPTIONS_SETTINGS_STORAGE_SIZE_CALCULATING" desc="In storage manager overlay, label for storage item's size indicating the size is being calculated.">
     Calculating...
   </message>
@@ -2563,9 +2578,15 @@
   <message name="IDS_OPTIONS_SETTINGS_STORAGE_SUBITEM_LABEL_DOWNLOADS" desc="In storage manager overlay, label for the size of Downloads directory.">
     Downloads
   </message>
+  <message name="IDS_OPTIONS_SETTINGS_STORAGE_SUBITEM_LABEL_DRIVE_CACHE" desc="In storage manager overlay, label for the size of Google Drive's offline files.">
+    Offline files
+  </message>
   <message name="IDS_OPTIONS_SETTINGS_STORAGE_SUBITEM_LABEL_ARC" desc="In storage manager overlay, label for the total size size of Android apps and cache.">
     Android apps and cache
   </message>
+  <message name="IDS_OPTIONS_SETTINGS_STORAGE_DELETE_ALL_BUTTON_TITLE" desc="In storage manager overlay, label for the total size size of Android apps and cache.">
+    Delete all
+  </message>
   <message name="IDS_OPTIONS_ACCOUNTS_ALLOW_BWSI_DESCRIPTION" desc="In the Accounts settings tab, the text on the checkbox to allow browse without signing in.">
     Enable Guest browsing
   </message>
@@ -5772,6 +5793,29 @@
     To check for updates, please use Ethernet or Wi-Fi.
   </message>
 
+  <!-- EOL Notification Strings -->
+  <message name="IDS_ABOUT_PAGE_EOL_SECURITY_ONLY" desc="The message in the about page to inform the user that this device is now receiving security updates only, containing the URLs.">
+     This device is receiving security updates only. <ph name="BEGIN_LINK_EOL">&lt;a target="_blank" href="$1"&gt;</ph>More info<ph name="END_LINK_EOL">&lt;/a&gt;</ph>
+  </message>
+  <message name="IDS_ABOUT_PAGE_EOL_EOL" desc="The message in the about page to inform the user that this device will no longer receive regular security updates, containing the URLs.">
+    This device will no longer receive regular security updates. <ph name="BEGIN_LINK_EOL">&lt;a target="_blank" href="$1"&gt;</ph>More info<ph name="END_LINK_EOL">&lt;/a&gt;</ph>
+  </message>
+  <message name="IDS_EOL_NOTIFICATION_SECURITY_ONLY" desc="Notification shown to inform the user that this device will no longer receive software updates.">
+    This device will no longer receive software updates.
+  </message>
+  <message name="IDS_EOL_NOTIFICATION_EOL" desc="Notification shown to inform the user that this device will no longer receive regular security updates.">
+    This device will no longer receive regular security updates.
+  </message>
+  <message name="IDS_EOL_MORE_INFO_BUTTON" desc="A button label shown in the notification for eol status change to get more information.">
+    More info
+  </message>
+  <message name="IDS_FLAGS_ENABLE_EOL_NOTIFICATION_NAME" desc="Name of the about:flag option to enable eol notification.">
+    Enable Device End of Life notification.
+  </message>
+  <message name="IDS_FLAGS_ENABLE_EOL_NOTIFICATION_DESCRIPTION" desc="Description of the about:flag option to enable eol notification.">
+    Enable Notifcation when Device is End of Life.
+  </message>
+
   <!-- Genius App -->
   <message name="IDS_GENIUS_APP_NAME" desc="Name of the genius app in the app shelf">
     Get Help
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 3b447f4..9a8f1ec 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -543,10 +543,7 @@
           </message>
 
           <message name="IDS_CONTENT_CONTEXT_RELOADFRAME" desc="The name of the Reload Frame command in the content area context menu">
-            Reload frame
-          </message>
-          <message name="IDS_CONTENT_CONTEXT_SAVEFRAMEAS" desc="The name of the Save Frame As command in the content area context menu">
-            Save &amp;frame as...
+            Reload &amp;frame
           </message>
           <message name="IDS_CONTENT_CONTEXT_PRINTFRAME" desc="The name of the Print Frame command in the content area context menu">
             &amp;Print frame...
@@ -756,10 +753,7 @@
           </message>
 
           <message name="IDS_CONTENT_CONTEXT_RELOADFRAME" desc="In Title Case: The name of the Reload Frame command in the content area context menu">
-            Reload Frame
-          </message>
-          <message name="IDS_CONTENT_CONTEXT_SAVEFRAMEAS" desc="In Title Case: The name of the Save Frame As command in the content area context menu">
-            Save &amp;Frame As...
+            Reload &amp;Frame
           </message>
           <message name="IDS_CONTENT_CONTEXT_PRINTFRAME" desc="In Title Case: The name of the Print Frame command in the content area context menu">
             &amp;Print Frame...
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index f75d048..075118b 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1890,8 +1890,7 @@
      FEATURE_VALUE_TYPE(chrome::android::kImportantSitesInCBD)},
     {"enable-autoplay-muted-videos",
      IDS_FLAGS_ENABLE_AUTOPLAY_MUTED_VIDEOS_NAME,
-     IDS_FLAGS_ENABLE_AUTOPLAY_MUTED_VIDEOS_DESCRIPTION,
-     kOsAndroid,
+     IDS_FLAGS_ENABLE_AUTOPLAY_MUTED_VIDEOS_DESCRIPTION, kOsAndroid,
      SINGLE_VALUE_TYPE(switches::kEnableAutoplayMutedVideos)},
 #endif
     {"enable-pointer-events",  // FLAGS:RECORD_UMA
@@ -1910,8 +1909,7 @@
      IDS_FLAGS_FONT_CACHE_SCALING_DESCRIPTION, kOsAll,
      FEATURE_VALUE_TYPE(features::kFontCacheScaling)},
 #if defined(OS_ANDROID)
-    {"enable-vr-shell",
-     IDS_FLAGS_ENABLE_VR_SHELL_NAME,
+    {"enable-vr-shell", IDS_FLAGS_ENABLE_VR_SHELL_NAME,
      IDS_FLAGS_ENABLE_VR_SHELL_DESCRIPTION, kOsAndroid,
      ENABLE_DISABLE_VALUE_TYPE(switches::kEnableVrShell,
                                switches::kDisableVrShell)},
@@ -1920,10 +1918,14 @@
      IDS_FLAGS_ENABLE_ANDROID_PAY_INTEGRATION_V1_DESCRIPTION, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kAndroidPayIntegrationV1)},
 #endif
-    {"enable-weak-memorycache",
-     IDS_FLAGS_ENABLE_WEAK_MEMORYCACHE_NAME,
+    {"enable-weak-memorycache", IDS_FLAGS_ENABLE_WEAK_MEMORYCACHE_NAME,
      IDS_FLAGS_ENABLE_WEAK_MEMORYCACHE_DESCRIPTION, kOsAll,
      FEATURE_VALUE_TYPE(features::kWeakMemoryCache)},
+#if defined(OS_CHROMEOS)
+    {"enable-eol-notification", IDS_FLAGS_ENABLE_EOL_NOTIFICATION_NAME,
+     IDS_FLAGS_ENABLE_EOL_NOTIFICATION_DESCRIPTION, kOsCrOS,
+     SINGLE_VALUE_TYPE(chromeos::switches::kEnableEolNotification)},
+#endif  // defined(OS_CHROMEOS)
 
     // NOTE: Adding new command-line switches requires adding corresponding
     // entries to enum "LoginCustomFlags" in histograms.xml. See note in
diff --git a/chrome/browser/chromeos/eol_notification.cc b/chrome/browser/chromeos/eol_notification.cc
new file mode 100644
index 0000000..8b5f49eb
--- /dev/null
+++ b/chrome/browser/chromeos/eol_notification.cc
@@ -0,0 +1,152 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/eol_notification.h"
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/notifications/notification.h"
+#include "chrome/browser/notifications/notification_ui_manager.h"
+#include "chrome/browser/ui/browser_navigator.h"
+#include "chrome/browser/ui/browser_navigator_params.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/update_engine_client.h"
+#include "components/prefs/pref_service.h"
+#include "grit/ash_resources.h"
+#include "third_party/cros_system_api/dbus/update_engine/dbus-constants.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+
+using message_center::MessageCenter;
+
+namespace chromeos {
+namespace {
+
+const char kEolNotificationId[] = "eol";
+const char kDelegateId[] = "eol_delegate";
+
+class EolNotificationDelegate : public NotificationDelegate {
+ public:
+  explicit EolNotificationDelegate(Profile* profile);
+
+ private:
+  ~EolNotificationDelegate() override;
+
+  // NotificationDelegate overrides:
+  void Close(bool by_user) override;
+  void ButtonClick(int button_index) override;
+  std::string id() const override;
+
+  Profile* const profile_;
+
+  void OpenMoreInfoPage();
+
+  DISALLOW_COPY_AND_ASSIGN(EolNotificationDelegate);
+};
+
+EolNotificationDelegate::EolNotificationDelegate(Profile* profile)
+    : profile_(profile) {}
+
+EolNotificationDelegate::~EolNotificationDelegate() {}
+
+void EolNotificationDelegate::Close(bool by_user) {
+  if (by_user) {
+    // set dismiss pref.
+    profile_->GetPrefs()->SetBoolean(prefs::kEolNotificationDismissed, true);
+  }
+}
+
+void EolNotificationDelegate::ButtonClick(int button_index) {
+  // show eol link
+  OpenMoreInfoPage();
+}
+
+std::string EolNotificationDelegate::id() const {
+  return kDelegateId;
+}
+
+void EolNotificationDelegate::OpenMoreInfoPage() {
+  chrome::NavigateParams params(profile_, GURL(chrome::kEolNotificationURL),
+                                ui::PAGE_TRANSITION_LINK);
+  params.disposition = NEW_FOREGROUND_TAB;
+  params.window_action = chrome::NavigateParams::SHOW_WINDOW;
+  chrome::Navigate(&params);
+}
+
+}  // namespace
+
+EolNotification::EolNotification(Profile* profile)
+    : profile_(profile),
+      status_(update_engine::EndOfLifeStatus::kSupported),
+      weak_factory_(this) {}
+
+EolNotification::~EolNotification() {}
+
+void EolNotification::CheckEolStatus() {
+  UpdateEngineClient* update_engine_client =
+      DBusThreadManager::Get()->GetUpdateEngineClient();
+
+  // Request the Eol Status.
+  update_engine_client->GetEolStatus(
+      base::Bind(&EolNotification::OnEolStatus, weak_factory_.GetWeakPtr()));
+}
+
+void EolNotification::OnEolStatus(int status) {
+  status_ = status;
+
+  const int pre_eol_status =
+      profile_->GetPrefs()->GetInteger(prefs::kEolStatus);
+  profile_->GetPrefs()->SetInteger(prefs::kEolStatus, status_);
+
+  if (status_ == update_engine::EndOfLifeStatus::kSupported)
+    return;
+
+  if (pre_eol_status != status_) {
+    // If Eol status has changed, we should reset
+    // kEolNotificationDismissed and show notification.
+    profile_->GetPrefs()->SetBoolean(prefs::kEolNotificationDismissed, false);
+  }
+
+  bool user_dismissed_eol_notification =
+      profile_->GetPrefs()->GetBoolean(prefs::kEolNotificationDismissed);
+  if (user_dismissed_eol_notification)
+    return;
+
+  // When device is in Security-Only state, only show notification the first
+  // time.
+  if (status_ == update_engine::EndOfLifeStatus::kSecurityOnly)
+    profile_->GetPrefs()->SetBoolean(prefs::kEolNotificationDismissed, true);
+
+  Update();
+}
+
+void EolNotification::Update() {
+  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+  message_center::RichNotificationData data;
+  data.buttons.push_back(message_center::ButtonInfo(
+      l10n_util::GetStringUTF16(IDS_EOL_MORE_INFO_BUTTON)));
+
+  Notification notification(
+      message_center::NOTIFICATION_TYPE_SIMPLE,
+      base::string16(),  // title
+      // TODO(xiaoyinh): Update the Eol icon once it's ready.
+      GetEolMessage(), bundle.GetImageNamed(IDR_AURA_NOTIFICATION_DISPLAY),
+      message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
+                                 kEolNotificationId),
+      base::string16(),  // display_source
+      GURL(), kEolNotificationId, data, new EolNotificationDelegate(profile_));
+  g_browser_process->notification_ui_manager()->Add(notification, profile_);
+}
+
+base::string16 EolNotification::GetEolMessage() {
+  if (status_ == update_engine::EndOfLifeStatus::kSecurityOnly) {
+    return l10n_util::GetStringUTF16(IDS_EOL_NOTIFICATION_SECURITY_ONLY);
+  } else {
+    return l10n_util::GetStringUTF16(IDS_EOL_NOTIFICATION_EOL);
+  }
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/eol_notification.h b/chrome/browser/chromeos/eol_notification.h
new file mode 100644
index 0000000..2ca80e8
--- /dev/null
+++ b/chrome/browser/chromeos/eol_notification.h
@@ -0,0 +1,54 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EOL_NOTIFICATION_H_
+#define CHROME_BROWSER_CHROMEOS_EOL_NOTIFICATION_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
+#include "chrome/browser/profiles/profile.h"
+
+namespace message_center {
+class MessageCenter;
+}
+
+namespace chromeos {
+
+// EolNotification is created when user logs in. It is
+// used to check current EndOfLife Status of the device,
+// and show notification accordingly.
+class EolNotification final {
+ public:
+  explicit EolNotification(Profile* profile);
+  ~EolNotification();
+
+  // Check Eol status from update engine.
+  void CheckEolStatus();
+
+ private:
+  // Callback invoked when |GetEolStatus()| has finished.
+  void OnEolStatus(int status);
+
+  // Create or updates the notfication.
+  void Update();
+
+  // Returns messages that applys to this eol status.
+  base::string16 GetEolMessage();
+
+  // Profile which is associated with the EndOfLife notification.
+  Profile* const profile_;
+
+  // Device Eol status.
+  int status_;
+
+  // Factory of callbacks.
+  base::WeakPtrFactory<EolNotification> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(EolNotification);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_EOL_NOTIFICATION_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
index 8db17b7..58ac1e62 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
@@ -474,6 +474,11 @@
              IDS_FILE_BROWSER_MANY_DIRECTORIES_SELECTED);
   SET_STRING("MANY_ENTRIES_SELECTED", IDS_FILE_BROWSER_MANY_ENTRIES_SELECTED);
   SET_STRING("MANY_FILES_SELECTED", IDS_FILE_BROWSER_MANY_FILES_SELECTED);
+  SET_STRING("METADATA_BOX_FILE_SIZE", IDS_FILE_BROWSER_METADATA_BOX_FILE_SIZE);
+  SET_STRING("METADATA_BOX_GENERAL_INFO",
+             IDS_FILE_BROWSER_METADATA_BOX_GENERAL_INFO);
+  SET_STRING("METADATA_BOX_MODIFICATION_TIME",
+             IDS_FILE_BROWSER_METADATA_BOX_MODIFICATION_TIME);
   SET_STRING("MOUNT_ARCHIVE", IDS_FILE_BROWSER_MOUNT_ARCHIVE);
   SET_STRING("MOVE_FILESYSTEM_ERROR", IDS_FILE_BROWSER_MOVE_FILESYSTEM_ERROR);
   SET_STRING("MOVE_FILE_NAME", IDS_FILE_BROWSER_MOVE_FILE_NAME);
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 2ae53b8..59abe8a5 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -1210,6 +1210,12 @@
   // resolved.
   if (delegate_)
     delegate_->OnProfilePrepared(profile, browser_launched);
+
+  // Check to see if this profile should show EndOfLife Notification and show
+  // the message accordingly.
+  if (!ShouldShowEolNotification(profile))
+    return;
+  CheckEolStatus(profile);
 }
 
 void UserSessionManager::ActivateWizard(const std::string& screen_name) {
@@ -1669,6 +1675,18 @@
   return state;
 }
 
+void UserSessionManager::CheckEolStatus(Profile* profile) {
+  std::map<Profile*, std::unique_ptr<EolNotification>, ProfileCompare>::iterator
+      iter = eol_notification_handler_.find(profile);
+  if (iter == eol_notification_handler_.end()) {
+    auto eol_notification = base::WrapUnique(new EolNotification(profile));
+    iter = eol_notification_handler_
+               .insert(std::make_pair(profile, std::move(eol_notification)))
+               .first;
+  }
+  iter->second->CheckEolStatus();
+}
+
 EasyUnlockKeyManager* UserSessionManager::GetEasyUnlockKeyManager() {
   if (!easy_unlock_key_manager_)
     easy_unlock_key_manager_.reset(new EasyUnlockKeyManager);
@@ -1851,4 +1869,22 @@
     token_handle_util_.reset(new TokenHandleUtil());
 }
 
+bool UserSessionManager::ShouldShowEolNotification(Profile* profile) {
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          chromeos::switches::kEnableEolNotification)) {
+    return false;
+  }
+
+  // Do not show end of life notification if this device is managed by
+  // enterprise user.
+  if (g_browser_process->platform_part()
+          ->browser_policy_connector_chromeos()
+          ->IsEnterpriseManaged()) {
+    return false;
+  }
+
+  // Do not show end of life notification if this is a guest session
+  return !profile->IsGuestSession();
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.h b/chrome/browser/chromeos/login/session/user_session_manager.h
index a97dd1f1..a0c7968 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.h
+++ b/chrome/browser/chromeos/login/session/user_session_manager.h
@@ -14,6 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "chrome/browser/chromeos/base/locale_util.h"
+#include "chrome/browser/chromeos/eol_notification.h"
 #include "chrome/browser/chromeos/login/signin/oauth2_login_manager.h"
 #include "chrome/browser/chromeos/login/signin/token_handle_util.h"
 #include "chromeos/dbus/session_manager_client.h"
@@ -229,6 +230,9 @@
   scoped_refptr<input_method::InputMethodManager::State> GetDefaultIMEState(
       Profile* profile);
 
+  // Check given profile's EndofLife Status and show notification accordingly.
+  void CheckEolStatus(Profile* profile);
+
   // Note this could return NULL if not enabled.
   EasyUnlockKeyManager* GetEasyUnlockKeyManager();
 
@@ -377,6 +381,10 @@
 
   void CreateTokenUtilIfMissing();
 
+  // Returns |true| if given profile show see EndofLife Notification when
+  // applicable.
+  bool ShouldShowEolNotification(Profile* profile);
+
   // Test API methods.
 
   // Injects |user_context| that will be used to create StubAuthenticator
@@ -462,6 +470,10 @@
   std::map<Profile*, scoped_refptr<input_method::InputMethodManager::State>,
       ProfileCompare> default_ime_states_;
 
+  // Per-user-session EndofLife Notification
+  std::map<Profile*, std::unique_ptr<EolNotification>, ProfileCompare>
+      eol_notification_handler_;
+
   // Manages Easy unlock cryptohome keys.
   std::unique_ptr<EasyUnlockKeyManager> easy_unlock_key_manager_;
   bool running_easy_unlock_key_ops_;
diff --git a/chrome/browser/chromeos/preferences.cc b/chrome/browser/chromeos/preferences.cc
index 9e157c5..3f14ce9e 100644
--- a/chrome/browser/chromeos/preferences.cc
+++ b/chrome/browser/chromeos/preferences.cc
@@ -45,6 +45,7 @@
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/browser_thread.h"
+#include "third_party/cros_system_api/dbus/update_engine/dbus-constants.h"
 #include "third_party/icu/source/i18n/unicode/timezone.h"
 #include "ui/base/ime/chromeos/extension_ime_util.h"
 #include "ui/base/ime/chromeos/ime_keyboard.h"
@@ -316,6 +317,11 @@
 
   registry->RegisterInt64Pref(prefs::kHatsLastInteractionTimestamp,
                               base::Time().ToInternalValue());
+
+  // We don't sync EOL related prefs because they are device specific.
+  registry->RegisterBooleanPref(prefs::kEolNotificationDismissed, false);
+  registry->RegisterIntegerPref(prefs::kEolStatus,
+                                update_engine::EndOfLifeStatus::kSupported);
 }
 
 void Preferences::InitUserPrefs(syncable_prefs::PrefServiceSyncable* prefs) {
diff --git a/chrome/browser/resources/help/help_content.html b/chrome/browser/resources/help/help_content.html
index d1f8a12..3d1a967 100644
--- a/chrome/browser/resources/help/help_content.html
+++ b/chrome/browser/resources/help/help_content.html
@@ -52,6 +52,7 @@
 </if>
       <button id="relaunch" i18n-content="relaunch" hidden></button>
 <if expr="chromeos">
+      <div id ="eol-message" hidden></div>
       <button id="relaunch-and-powerwash"
           i18n-content="relaunchAndPowerwash" hidden>
       </button>
diff --git a/chrome/browser/resources/help/help_page.js b/chrome/browser/resources/help/help_page.js
index 2889d929..92d6465 100644
--- a/chrome/browser/resources/help/help_page.js
+++ b/chrome/browser/resources/help/help_page.js
@@ -64,6 +64,18 @@
      */
     haveNeverCheckedForUpdates_: true,
 
+    /**
+     *  Last EndofLife status received from the version updater.
+     *  @private
+     */
+    eolStatus_: null,
+
+    /**
+     *  Last EndofLife message received from the version updater.
+     * @private
+     */
+    eolMessage_: null,
+
     /** @override */
     initializePage: function() {
       Page.prototype.initializePage.call(this);
@@ -306,6 +318,18 @@
     },
 
     /**
+     * @param {string} eolStatus: The EndofLife status of the device.
+     * @param {string} eolMessage: The EndofLife message to display.
+     * @private
+     */
+    updateEolMessage_: function(eolStatus, eolMessage) {
+      this.eolStatus_ = eolStatus;
+      this.eolMessage_ = eolMessage;
+
+      this.updateUI_();
+    },
+
+    /**
       * Updates UI elements on the page according to current state.
       * @private
       */
@@ -313,6 +337,8 @@
       var status = this.status_;
       var message = this.message_;
       var channel = this.targetChannel_;
+      var eolStatus = this.eolStatus_;
+      var eolMessage = this.eolMessage_;
 
       if (this.channelList_.indexOf(channel) >= 0) {
         $('current-channel').textContent = loadTimeData.getStringF(
@@ -370,6 +396,15 @@
         $('update-status-message').innerHTML = message;
       }
 
+      // Show EndofLife Strings if applicable
+      if (eolStatus == 'device_supported') {
+          $('eol-message').hidden = true;
+      } else if (eolStatus == 'device_endoflife') {
+          $('eol-message').innerHTML = eolMessage;
+          $('eol-message').hidden = false;
+      }
+
+
       if (cr.isChromeOS) {
         $('change-channel').disabled = !this.canChangeChannel_ ||
             status == 'nearly_updated';
@@ -392,9 +427,11 @@
         // Re-enable the update button if we are in a stale 'updated' status or
         // update has failed, and disable it if there's an update in progress or
         // updates are disabled by policy.
+        // In addition, Update button will be disabled when device is in eol
+        // status
         $('request-update').disabled =
             !((this.haveNeverCheckedForUpdates_ && status == 'updated') ||
-                   status == 'failed');
+                   status == 'failed') || (eolStatus == 'device_endoflife');
         // If updates are disabled by policy, unhide the
         // controlled-feature-icon.
         $('controlled-feature-icon').hidden = (status != 'disabled_by_admin');
@@ -413,7 +450,8 @@
         if (cr.isChromeOS) {
           // Assume the "updated" status is stale if we haven't checked yet.
           if (status == 'updated' && this.haveNeverCheckedForUpdates_ ||
-              status == 'disabled_by_admin') {
+              status == 'disabled_by_admin' ||
+              eolStatus == 'device_endoflife') {
             container.hidden = true;
           }
 
@@ -732,6 +770,11 @@
     HelpPage.getInstance().setRegulatoryLabelText_(text);
   };
 
+  HelpPage.updateEolMessage = function(eolStatus, eolMessage) {
+    assert(cr.isChromeOS);
+    HelpPage.getInstance().updateEolMessage_(eolStatus, eolMessage);
+  };
+
   // Export
   return {
     HelpPage: HelpPage
diff --git a/chrome/browser/resources/options/chromeos/storage_clear_drive_cache_overlay.html b/chrome/browser/resources/options/chromeos/storage_clear_drive_cache_overlay.html
new file mode 100644
index 0000000..5bf2aa9
--- /dev/null
+++ b/chrome/browser/resources/options/chromeos/storage_clear_drive_cache_overlay.html
@@ -0,0 +1,14 @@
+<div id="clear-drive-cache-overlay-page" class="page" role="dialog" hidden>
+  <div class="close-button"></div>
+  <h1 i18n-content="storageClearDriveCachePage"></h1>
+  <div class="content-area">
+    <div i18n-content="storageClearDriveCacheDescription"></div>
+  </div>
+  <div class="action-area button-strip">
+    <button id="clear-drive-cache-overlay-cancel-button" i18n-content="cancel">
+    </button>
+    <button id="clear-drive-cache-overlay-delete-button" class="default-button"
+        i18n-content="storageDeleteAllButtonTitle">
+    </button>
+  </div>
+</div>
diff --git a/chrome/browser/resources/options/chromeos/storage_clear_drive_cache_overlay.js b/chrome/browser/resources/options/chromeos/storage_clear_drive_cache_overlay.js
new file mode 100644
index 0000000..38c30720
--- /dev/null
+++ b/chrome/browser/resources/options/chromeos/storage_clear_drive_cache_overlay.js
@@ -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.
+
+cr.define('options', function() {
+  var Page = cr.ui.pageManager.Page;
+  var PageManager = cr.ui.pageManager.PageManager;
+
+  function StorageClearDriveCacheOverlay() {
+    Page.call(this, 'storageClearDriveCache',
+              loadTimeData.getString('storageClearDriveCachePageTabTitle'),
+              'clear-drive-cache-overlay-page');
+  }
+
+  cr.addSingletonGetter(StorageClearDriveCacheOverlay);
+
+  StorageClearDriveCacheOverlay.prototype = {
+    __proto__: Page.prototype,
+
+    /** @override */
+    initializePage: function() {
+      Page.prototype.initializePage.call(this);
+
+      $('clear-drive-cache-overlay-delete-button').onclick = function(e) {
+        chrome.send('clearDriveCache');
+        PageManager.closeOverlay();
+      };
+      $('clear-drive-cache-overlay-cancel-button').onclick = function(e) {
+        PageManager.closeOverlay();
+      };
+    },
+  };
+
+  return {
+    StorageClearDriveCacheOverlay: StorageClearDriveCacheOverlay
+  };
+});
diff --git a/chrome/browser/resources/options/chromeos/storage_manager.html b/chrome/browser/resources/options/chromeos/storage_manager.html
index da5f6dd..97345b7 100644
--- a/chrome/browser/resources/options/chromeos/storage_manager.html
+++ b/chrome/browser/resources/options/chromeos/storage_manager.html
@@ -21,6 +21,14 @@
             class="storage-manager-item-size"
             i18n-content="storageSizeCalculating"></span>
     </div>
+    <div class="storage-manager-subitem">
+      <a is="action-link"
+         id="storage-manager-label-drive-cache"
+         i18n-content="storageSubitemLabelDriveCache"></a>
+      <span id="storage-manager-size-drive-cache"
+            class="storage-manager-item-size"
+            i18n-content="storageSizeCalculating"></span>
+    </div>
     <div id="storage-manager-item-arc"
          class="storage-manager-subitem" hidden>
       <a is="action-link"
diff --git a/chrome/browser/resources/options/chromeos/storage_manager.js b/chrome/browser/resources/options/chromeos/storage_manager.js
index 65aa19993..426e9a8 100644
--- a/chrome/browser/resources/options/chromeos/storage_manager.js
+++ b/chrome/browser/resources/options/chromeos/storage_manager.js
@@ -34,6 +34,9 @@
       $('storage-manager-label-downloads').onclick = function() {
         chrome.send('openDownloads');
       };
+      $('storage-manager-label-drive-cache').onclick = function() {
+        PageManager.showPageByName('storageClearDriveCache');
+      };
       $('storage-manager-label-arc').onclick = function() {
         chrome.send('openArcStorage');
       };
@@ -72,6 +75,16 @@
     },
 
     /**
+     * Updates the size of Google Drive offline files.
+     * @param {string} size Formatted string of the size of Google Drive offline
+     *     files.
+     * @private
+     */
+    setDriveCacheSize_: function(size) {
+      $('storage-manager-size-drive-cache').textContent = size;
+    },
+
+    /**
      * Updates the total size of Android apps and cache.
      * @param {string} size Formatted string of the size of Android apps and
      * cache.
@@ -95,6 +108,7 @@
   cr.makePublic(StorageManager, [
     'setArcSize',
     'setDownloadsSize',
+    'setDriveCacheSize',
     'setSizeStat',
     'showArcItem',
   ]);
diff --git a/chrome/browser/resources/options/options.html b/chrome/browser/resources/options/options.html
index 82b3d365..23175cf1 100644
--- a/chrome/browser/resources/options/options.html
+++ b/chrome/browser/resources/options/options.html
@@ -167,6 +167,7 @@
   <include src="chromeos/display_overscan.html">
   <include src="chromeos/internet_detail.html">
   <include src="chromeos/preferred_networks.html">
+  <include src="chromeos/storage_clear_drive_cache_overlay.html">
   <include src="chromeos/third_party_ime_confirm_overlay.html">
   <include src="../help/channel_change_page.html">
 </if>
diff --git a/chrome/browser/resources/options/options.js b/chrome/browser/resources/options/options.js
index 9387495..f950327 100644
--- a/chrome/browser/resources/options/options.js
+++ b/chrome/browser/resources/options/options.js
@@ -203,6 +203,8 @@
     PageManager.registerOverlay(ChangePictureOptions.getInstance(),
                                 BrowserOptions.getInstance(),
                                 [$('account-picture')]);
+    PageManager.registerOverlay(StorageClearDriveCacheOverlay.getInstance(),
+                                StorageManager.getInstance());
     PageManager.registerOverlay(ConsumerManagementOverlay.getInstance(),
                                 BrowserOptions.getInstance());
     PageManager.registerOverlay(DetailsInternetPage.getInstance(),
diff --git a/chrome/browser/resources/options/options_bundle.js b/chrome/browser/resources/options/options_bundle.js
index a458baa..d6a330c 100644
--- a/chrome/browser/resources/options/options_bundle.js
+++ b/chrome/browser/resources/options/options_bundle.js
@@ -39,6 +39,7 @@
 <include src="chromeos/display_overscan.js">
 <include src="chromeos/keyboard_overlay.js">
 <include src="chromeos/pointer_overlay.js">
+<include src="chromeos/storage_clear_drive_cache_overlay.js">
 <include src="chromeos/storage_manager.js">
 <include src="chromeos/third_party_ime_confirm_overlay.js">
 <include src="chromeos/power_overlay.js">
@@ -53,6 +54,7 @@
 var KeyboardOverlay = options.KeyboardOverlay;
 var PointerOverlay = options.PointerOverlay;
 var PowerOverlay = options.PowerOverlay;
+var StorageClearDriveCacheOverlay = options.StorageClearDriveCacheOverlay;
 var StorageManager = options.StorageManager;
 var UIAccountTweaks = uiAccountTweaks.UIAccountTweaks;
 </if>
diff --git a/chrome/browser/ui/webui/help/help_browsertest.js b/chrome/browser/ui/webui/help/help_browsertest.js
index a5101e3..bc92d5cbe 100644
--- a/chrome/browser/ui/webui/help/help_browsertest.js
+++ b/chrome/browser/ui/webui/help/help_browsertest.js
@@ -123,4 +123,19 @@
   expectFalse(policyIcon.hidden);
 });
 
+// Test that the EndofLife String is shown and hidden properly.
+TEST_F('HelpPageWebUITest', 'testUpdateEolMessage', function() {
+  var container = $('update-status-container');
+  var update = $('request-update');
+  var message = $('eol-message');
+
+  help.HelpPage.updateEolMessage('device_supported', '');
+  expectTrue(message.hidden);
+
+  help.HelpPage.updateEolMessage('device_endoflife', '');
+  expectFalse(message.hidden);
+  expectTrue(update.disabled);
+  expectTrue(container.hidden);
+});
+
 GEN('#endif');
diff --git a/chrome/browser/ui/webui/help/help_handler.cc b/chrome/browser/ui/webui/help/help_handler.cc
index 423444a..f2050af5 100644
--- a/chrome/browser/ui/webui/help/help_handler.cc
+++ b/chrome/browser/ui/webui/help/help_handler.cc
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "ash/common/system/chromeos/devicetype_utils.h"
+#include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
@@ -73,6 +74,7 @@
 #include "chromeos/system/statistics_provider.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
+#include "third_party/cros_system_api/dbus/update_engine/dbus-constants.h"
 #endif
 
 using base::ListValue;
@@ -205,6 +207,20 @@
   return std::string();
 }
 
+// Returns messages that applys to this eol status
+base::string16 GetEolMessage(int status) {
+  if (status == update_engine::EndOfLifeStatus::kSecurityOnly) {
+    return l10n_util::GetStringFUTF16(
+        IDS_ABOUT_PAGE_EOL_SECURITY_ONLY,
+        base::ASCIIToUTF16(chrome::kEolNotificationURL));
+
+  } else {
+    return l10n_util::GetStringFUTF16(
+        IDS_ABOUT_PAGE_EOL_EOL,
+        base::ASCIIToUTF16(chrome::kEolNotificationURL));
+  }
+}
+
 #endif  // defined(OS_CHROMEOS)
 
 }  // namespace
@@ -516,6 +532,12 @@
   version_updater_->GetChannel(false,
       base::Bind(&HelpHandler::OnTargetChannel, weak_factory_.GetWeakPtr()));
 
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          chromeos::switches::kEnableEolNotification)) {
+    version_updater_->GetEolStatus(
+        base::Bind(&HelpHandler::OnEolStatus, weak_factory_.GetWeakPtr()));
+  }
+
   base::PostTaskAndReplyWithResult(
       content::BrowserThread::GetBlockingPool(),
       FROM_HERE,
@@ -742,4 +764,22 @@
       base::StringValue(base::CollapseWhitespaceASCII(text, true)));
 }
 
+void HelpHandler::OnEolStatus(const int status) {
+  if (status == update_engine::EndOfLifeStatus::kSupported ||
+      IsEnterpriseManaged()) {
+    return;
+  }
+
+  if (status == update_engine::EndOfLifeStatus::kSupported) {
+    web_ui()->CallJavascriptFunctionUnsafe(
+        "help.HelpPage.updateEolMessage", base::StringValue("device_supported"),
+        base::StringValue(""));
+  } else {
+    base::string16 message = GetEolMessage(status);
+    web_ui()->CallJavascriptFunctionUnsafe(
+        "help.HelpPage.updateEolMessage", base::StringValue("device_endoflife"),
+        base::StringValue(message));
+  }
+}
+
 #endif  // defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/webui/help/help_handler.h b/chrome/browser/ui/webui/help/help_handler.h
index 95e41f61..3dfde200 100644
--- a/chrome/browser/ui/webui/help/help_handler.h
+++ b/chrome/browser/ui/webui/help/help_handler.h
@@ -113,6 +113,9 @@
 
   // Callback for setting the regulatory label alt text.
   void OnRegulatoryLabelTextRead(const std::string& text);
+
+  // Callback for setting the eol string text.
+  void OnEolStatus(const int status);
 #endif
 
   // Specialized instance of the VersionUpdater used to update the browser.
diff --git a/chrome/browser/ui/webui/help/version_updater.h b/chrome/browser/ui/webui/help/version_updater.h
index 47cb81a..87cda9e 100644
--- a/chrome/browser/ui/webui/help/version_updater.h
+++ b/chrome/browser/ui/webui/help/version_updater.h
@@ -42,6 +42,7 @@
   // types.
 #if defined(OS_CHROMEOS)
   typedef base::Callback<void(const std::string&)> ChannelCallback;
+  typedef base::Callback<void(int status)> EolStatusCallback;
 #endif
 
   // Used to update the client of status changes. int parameter is the progress
@@ -78,6 +79,7 @@
                           bool is_powerwash_allowed) = 0;
   virtual void GetChannel(bool get_current_channel,
                           const ChannelCallback& callback) = 0;
+  virtual void GetEolStatus(const EolStatusCallback& callback) = 0;
 #endif
 };
 
diff --git a/chrome/browser/ui/webui/help/version_updater_chromeos.cc b/chrome/browser/ui/webui/help/version_updater_chromeos.cc
index 22e1c7ae..49d8391 100644
--- a/chrome/browser/ui/webui/help/version_updater_chromeos.cc
+++ b/chrome/browser/ui/webui/help/version_updater_chromeos.cc
@@ -179,6 +179,14 @@
   update_engine_client->GetChannel(get_current_channel, cb);
 }
 
+void VersionUpdaterCros::GetEolStatus(const EolStatusCallback& cb) {
+  UpdateEngineClient* update_engine_client =
+      DBusThreadManager::Get()->GetUpdateEngineClient();
+
+  // Request the Eol Status.
+  update_engine_client->GetEolStatus(cb);
+}
+
 VersionUpdaterCros::VersionUpdaterCros(content::WebContents* web_contents)
     : context_(web_contents ? web_contents->GetBrowserContext() : nullptr),
       last_operation_(UpdateEngineClient::UPDATE_STATUS_IDLE),
diff --git a/chrome/browser/ui/webui/help/version_updater_chromeos.h b/chrome/browser/ui/webui/help/version_updater_chromeos.h
index 0d4cbf9..44d4ee8 100644
--- a/chrome/browser/ui/webui/help/version_updater_chromeos.h
+++ b/chrome/browser/ui/webui/help/version_updater_chromeos.h
@@ -30,6 +30,8 @@
   // Gets the last update status, without triggering a new check or download.
   void GetUpdateStatus(const StatusCallback& callback);
 
+  void GetEolStatus(const EolStatusCallback& callback) override;
+
  protected:
   friend class VersionUpdater;
 
diff --git a/chrome/browser/ui/webui/options/chromeos/storage_manager_handler.cc b/chrome/browser/ui/webui/options/chromeos/storage_manager_handler.cc
index 34afd55..d821454 100644
--- a/chrome/browser/ui/webui/options/chromeos/storage_manager_handler.cc
+++ b/chrome/browser/ui/webui/options/chromeos/storage_manager_handler.cc
@@ -10,10 +10,12 @@
 #include "base/sys_info.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/arc/arc_auth_service.h"
+#include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/platform_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/drive/chromeos/file_system_interface.h"
 #include "content/public/browser/browser_thread.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/text/bytes_formatting.h"
@@ -47,6 +49,8 @@
 
   RegisterTitle(localized_strings, "storageManagerPage",
                 IDS_OPTIONS_SETTINGS_STORAGE_MANAGER_TAB_TITLE);
+  RegisterTitle(localized_strings, "storageClearDriveCachePage",
+                IDS_OPTIONS_SETTINGS_STORAGE_CLEAR_DRIVE_CACHE_TAB_TITLE);
 
   localized_strings->SetString(
       "storageItemLabelCapacity", l10n_util::GetStringUTF16(
@@ -61,11 +65,20 @@
       "storageSubitemLabelDownloads", l10n_util::GetStringUTF16(
           IDS_OPTIONS_SETTINGS_STORAGE_SUBITEM_LABEL_DOWNLOADS));
   localized_strings->SetString(
+      "storageSubitemLabelDriveCache", l10n_util::GetStringUTF16(
+          IDS_OPTIONS_SETTINGS_STORAGE_SUBITEM_LABEL_DRIVE_CACHE));
+  localized_strings->SetString(
       "storageSubitemLabelArc", l10n_util::GetStringUTF16(
           IDS_OPTIONS_SETTINGS_STORAGE_SUBITEM_LABEL_ARC));
   localized_strings->SetString(
       "storageSizeCalculating", l10n_util::GetStringUTF16(
           IDS_OPTIONS_SETTINGS_STORAGE_SIZE_CALCULATING));
+  localized_strings->SetString(
+      "storageClearDriveCacheDescription", l10n_util::GetStringUTF16(
+          IDS_OPTIONS_SETTINGS_STORAGE_CLEAR_DRIVE_CACHE_DESCRIPTION));
+  localized_strings->SetString(
+      "storageDeleteAllButtonTitle", l10n_util::GetStringUTF16(
+          IDS_OPTIONS_SETTINGS_STORAGE_DELETE_ALL_BUTTON_TITLE));
 }
 
 void StorageManagerHandler::InitializePage() {
@@ -87,12 +100,17 @@
       "openArcStorage",
       base::Bind(&StorageManagerHandler::HandleOpenArcStorage,
                  base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "clearDriveCache",
+      base::Bind(&StorageManagerHandler::HandleClearDriveCache,
+                 base::Unretained(this)));
 }
 
 void StorageManagerHandler::HandleUpdateStorageInfo(
     const base::ListValue* unused_args) {
   UpdateSizeStat();
   UpdateDownloadsSize();
+  UpdateDriveCacheSize();
   UpdateArcSize();
 }
 
@@ -113,6 +131,16 @@
   arc::ArcStorageManager::Get()->OpenPrivateVolumeSettings();
 }
 
+void StorageManagerHandler::HandleClearDriveCache(
+    const base::ListValue* unused_args) {
+  drive::FileSystemInterface* const file_system =
+      drive::util::GetFileSystemByProfile(Profile::FromWebUI(web_ui()));
+  file_system->FreeDiskSpaceIfNeededFor(
+      std::numeric_limits<int64_t>::max(),  // Removes as much as possible.
+      base::Bind(&StorageManagerHandler::OnClearDriveCacheDone,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
 void StorageManagerHandler::UpdateSizeStat() {
   Profile* const profile = Profile::FromWebUI(web_ui());
   const base::FilePath downloads_path =
@@ -164,6 +192,20 @@
       base::StringValue(ui::FormatBytes(size)));
 }
 
+void StorageManagerHandler::UpdateDriveCacheSize() {
+  drive::FileSystemInterface* const file_system =
+      drive::util::GetFileSystemByProfile(Profile::FromWebUI(web_ui()));
+  file_system->CalculateCacheSize(
+      base::Bind(&StorageManagerHandler::OnGetDriveCacheSize,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+void StorageManagerHandler::OnGetDriveCacheSize(int64_t size) {
+  web_ui()->CallJavascriptFunctionUnsafe(
+      "options.StorageManager.setDriveCacheSize",
+      base::StringValue(ui::FormatBytes(size)));
+}
+
 void StorageManagerHandler::UpdateArcSize() {
   Profile* const profile = Profile::FromWebUI(web_ui());
   if (!arc::ArcAuthService::IsAllowedForProfile(profile) ||
@@ -202,5 +244,9 @@
                                          arc_size);
 }
 
+void StorageManagerHandler::OnClearDriveCacheDone(bool success) {
+  UpdateDriveCacheSize();
+}
+
 }  // namespace options
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/options/chromeos/storage_manager_handler.h b/chrome/browser/ui/webui/options/chromeos/storage_manager_handler.h
index a4f39694..a8a1fc28 100644
--- a/chrome/browser/ui/webui/options/chromeos/storage_manager_handler.h
+++ b/chrome/browser/ui/webui/options/chromeos/storage_manager_handler.h
@@ -31,6 +31,7 @@
   void HandleUpdateStorageInfo(const base::ListValue* unused_args);
   void HandleOpenDownloads(const base::ListValue* unused_args);
   void HandleOpenArcStorage(const base::ListValue* unused_args);
+  void HandleClearDriveCache(const base::ListValue* unused_args);
 
   // Requests updating disk space information.
   void UpdateSizeStat();
@@ -44,12 +45,21 @@
   // Callback to update the UI about the size of Downloads directory.
   void OnGetDownloadsSize(int64_t size);
 
+  // Requests updating the size of Drive Cache.
+  void UpdateDriveCacheSize();
+
+  // Callback to update the UI about the size of Drive Cache.
+  void OnGetDriveCacheSize(int64_t size);
+
   // Requests updating the space size used by Android apps and cache.
   void UpdateArcSize();
 
   // Callback to update the UI about Android apps and cache.
   void OnGetArcSize(bool succeeded, arc::mojom::ApplicationsSizePtr size);
 
+  // Callback called when clearing Drive cache is done.
+  void OnClearDriveCacheDone(bool success);
+
   base::WeakPtrFactory<StorageManagerHandler> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(StorageManagerHandler);
diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi
index ca2f15c5..adf9416d 100644
--- a/chrome/chrome_browser_chromeos.gypi
+++ b/chrome/chrome_browser_chromeos.gypi
@@ -174,6 +174,8 @@
         'browser/chromeos/drive/write_on_cache_file.h',
         'browser/chromeos/enrollment_dialog_view.cc',
         'browser/chromeos/enrollment_dialog_view.h',
+	'browser/chromeos/eol_notification.cc',
+	'browser/chromeos/eol_notification.h',
         'browser/chromeos/events/event_rewriter.cc',
         'browser/chromeos/events/event_rewriter.h',
         'browser/chromeos/events/event_rewriter_controller.cc',
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 6b8fa7e..8389fe8 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -873,6 +873,17 @@
 // The salt and hash for the pin quick unlock mechanism.
 const char kQuickUnlockPinSalt[] = "quick_unlock.pin.salt";
 const char kQuickUnlockPinSecret[] = "quick_unlock.pin.secret";
+
+// An integer pref. Holds one of several values:
+// 0: Supported. Device is in supported state.
+// 1: Security Only. Device is in Security-Only update (after initial 5 years).
+// 2: EOL. Device is End of Life(No more updates expected).
+// This value needs to be consistent with EndOfLifeStatus enum.
+const char kEolStatus[] = "eol_status";
+
+// Boolean pref indicating the End Of Life notification was dismissed by the
+// user.
+const char kEolNotificationDismissed[] = "eol_notification_dismissed";
 #endif  // defined(OS_CHROMEOS)
 
 // A boolean pref set to true if a Home button to open the Home pages should be
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 2510bd88..47d3a78 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -292,6 +292,8 @@
 extern const char kHatsLastInteractionTimestamp[];
 extern const char kQuickUnlockPinSalt[];
 extern const char kQuickUnlockPinSecret[];
+extern const char kEolStatus[];
+extern const char kEolNotificationDismissed[];
 #endif  // defined(OS_CHROMEOS)
 extern const char kShowHomeButton[];
 extern const char kRecentlySelectedEncoding[];
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 92b8408c..a502c7f0 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -784,4 +784,8 @@
 const char kChooserUsbOverviewURL[] =
     "https://support.google.com/chrome?p=webusb";
 
+#if defined(OS_CHROMEOS)
+const char kEolNotificationURL[] = "https://www.google.com/chromebook/older/";
+#endif
+
 }  // namespace chrome
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index ab0f3a0..f4eaece0 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -594,6 +594,11 @@
 // The URL for the WebUsb help center article.
 extern const char kChooserUsbOverviewURL[];
 
+#if defined(OS_CHROMEOS)
+// The URL for EOL notification
+extern const char kEolNotificationURL[];
+#endif
+
 }  // namespace chrome
 
 #endif  // CHROME_COMMON_URL_CONSTANTS_H_
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 7636096..a1b8c7f4 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-8460.0.0
\ No newline at end of file
+8466.0.0
\ No newline at end of file
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index 9ad4ade..ddfe311 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -134,6 +134,9 @@
 // Enables details panel in Files app.
 const char kEnableFilesDetailsPanel[] = "enable-files-details-panel";
 
+// Enables notification when device is in end of life status.
+const char kEnableEolNotification[] = "enable-eol-notification";
+
 // If this switch is set, the device cannot be remotely disabled by its owner.
 const char kDisableDeviceDisabling[] = "disable-device-disabling";
 
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index 58bf141..b900ca8 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -65,6 +65,7 @@
 CHROMEOS_EXPORT extern const char kEnableExperimentalAccessibilityFeatures[];
 CHROMEOS_EXPORT extern const char kEnableExtensionAssetsSharing[];
 CHROMEOS_EXPORT extern const char kEnableFilesDetailsPanel[];
+CHROMEOS_EXPORT extern const char kEnableEolNotification[];
 CHROMEOS_EXPORT extern const char kEnableFirstRunUITransitions[];
 CHROMEOS_EXPORT extern const char kEnableKioskMode[];
 CHROMEOS_EXPORT extern const char kEnableNetworkPortalNotification[];
diff --git a/chromeos/dbus/fake_update_engine_client.cc b/chromeos/dbus/fake_update_engine_client.cc
index a594a3e..7add923 100644
--- a/chromeos/dbus/fake_update_engine_client.cc
+++ b/chromeos/dbus/fake_update_engine_client.cc
@@ -75,6 +75,9 @@
                                         const GetChannelCallback& callback) {
 }
 
+void FakeUpdateEngineClient::GetEolStatus(
+    const GetEolStatusCallback& callback) {}
+
 void FakeUpdateEngineClient::set_default_status(
     const UpdateEngineClient::Status& status) {
   default_status_ = status;
diff --git a/chromeos/dbus/fake_update_engine_client.h b/chromeos/dbus/fake_update_engine_client.h
index 68c0aa43..a1aa3aa 100644
--- a/chromeos/dbus/fake_update_engine_client.h
+++ b/chromeos/dbus/fake_update_engine_client.h
@@ -35,6 +35,7 @@
                   bool is_powerwash_allowed) override;
   void GetChannel(bool get_current_channel,
                   const GetChannelCallback& callback) override;
+  void GetEolStatus(const GetEolStatusCallback& callback) override;
 
   // Pushes UpdateEngineClient::Status in the queue to test changing status.
   // GetLastStatus() returns the status set by this method in FIFO order.
diff --git a/chromeos/dbus/update_engine_client.cc b/chromeos/dbus/update_engine_client.cc
index fcd6ab1..12bc71e5 100644
--- a/chromeos/dbus/update_engine_client.cc
+++ b/chromeos/dbus/update_engine_client.cc
@@ -207,6 +207,17 @@
                    callback));
   }
 
+  void GetEolStatus(const GetEolStatusCallback& callback) override {
+    dbus::MethodCall method_call(update_engine::kUpdateEngineInterface,
+                                 update_engine::kGetEolStatus);
+
+    VLOG(1) << "Requesting to get end of life status";
+    update_engine_proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::Bind(&UpdateEngineClientImpl::OnGetEolStatus,
+                   weak_ptr_factory_.GetWeakPtr(), callback));
+  }
+
  protected:
   void Init(dbus::Bus* bus) override {
     update_engine_proxy_ = bus->GetObjectProxy(
@@ -349,6 +360,34 @@
     callback.Run(channel);
   }
 
+  // Called when a response for GetEolStatus() is received.
+  void OnGetEolStatus(const GetEolStatusCallback& callback,
+                      dbus::Response* response) {
+    if (!response) {
+      LOG(ERROR) << "Failed to request getting eol status";
+      callback.Run(update_engine::EndOfLifeStatus::kSupported);
+      return;
+    }
+    dbus::MessageReader reader(response);
+    int status;
+    if (!reader.PopInt32(&status)) {
+      LOG(ERROR) << "Incorrect response: " << response->ToString();
+      callback.Run(update_engine::EndOfLifeStatus::kSupported);
+      return;
+    }
+
+    // Validate the value of status
+    if (status > update_engine::EndOfLifeStatus::kEol ||
+        status < update_engine::EndOfLifeStatus::kSupported) {
+      LOG(ERROR) << "Incorrect status value: " << status;
+      callback.Run(update_engine::EndOfLifeStatus::kSupported);
+      return;
+    }
+
+    VLOG(1) << "Eol status received: " << status;
+    callback.Run(status);
+  }
+
   // Called when a status update signal is received.
   void StatusUpdateReceived(dbus::Signal* signal) {
     VLOG(1) << "Status update signal received: " << signal->ToString();
@@ -437,6 +476,10 @@
       callback.Run(target_channel_);
   }
 
+  void GetEolStatus(const GetEolStatusCallback& callback) override {
+    callback.Run(update_engine::EndOfLifeStatus::kSupported);
+  }
+
   std::string current_channel_;
   std::string target_channel_;
 };
diff --git a/chromeos/dbus/update_engine_client.h b/chromeos/dbus/update_engine_client.h
index 9950e213..721aaa3 100644
--- a/chromeos/dbus/update_engine_client.h
+++ b/chromeos/dbus/update_engine_client.h
@@ -15,6 +15,7 @@
 #include "chromeos/chromeos_export.h"
 #include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
+#include "third_party/cros_system_api/dbus/update_engine/dbus-constants.h"
 
 namespace chromeos {
 
@@ -133,6 +134,13 @@
   virtual void GetChannel(bool get_current_channel,
                           const GetChannelCallback& callback) = 0;
 
+  // Called once GetEolStatus() is complete. Takes one parameter;
+  // - EolStatus: the eol status of the device.
+  typedef base::Callback<void(int status)> GetEolStatusCallback;
+
+  // Get Eol status of the device and calls |callback| when completed.
+  virtual void GetEolStatus(const GetEolStatusCallback& callback) = 0;
+
   // Returns an empty UpdateCheckCallback that does nothing.
   static UpdateCheckCallback EmptyUpdateCheckCallback();
 
diff --git a/content/browser/android/synchronous_compositor_host.cc b/content/browser/android/synchronous_compositor_host.cc
index f5d9e58..a8a4de0 100644
--- a/content/browser/android/synchronous_compositor_host.cc
+++ b/content/browser/android/synchronous_compositor_host.cc
@@ -334,7 +334,7 @@
 
 void SynchronousCompositorHost::DidOverscroll(
     const DidOverscrollParams& over_scroll_params) {
-  client_->DidOverscroll(over_scroll_params.accumulated_overscroll,
+  client_->DidOverscroll(this, over_scroll_params.accumulated_overscroll,
                          over_scroll_params.latest_overscroll_delta,
                          over_scroll_params.current_fling_velocity);
 }
@@ -368,13 +368,13 @@
 
   if (need_invalidate_count_ != params.need_invalidate_count) {
     need_invalidate_count_ = params.need_invalidate_count;
-    client_->PostInvalidate();
+    client_->PostInvalidate(this);
   }
 
   if (did_activate_pending_tree_count_ !=
       params.did_activate_pending_tree_count) {
     did_activate_pending_tree_count_ = params.did_activate_pending_tree_count;
-    client_->DidUpdateContent();
+    client_->DidUpdateContent(this);
   }
 
   // Ensure only valid values from compositor are sent to client.
@@ -382,7 +382,7 @@
   // for that case here.
   if (params.page_scale_factor) {
     client_->UpdateRootLayerState(
-        gfx::ScrollOffsetToVector2dF(params.total_scroll_offset),
+        this, gfx::ScrollOffsetToVector2dF(params.total_scroll_offset),
         gfx::ScrollOffsetToVector2dF(params.max_scroll_offset),
         params.scrollable_size, params.page_scale_factor,
         params.min_page_scale_factor, params.max_page_scale_factor);
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index ffa1cd1..1ecd4bd 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -57,7 +57,6 @@
 #include "content/common/input_messages.h"
 #include "content/common/inter_process_time_ticks_converter.h"
 #include "content/common/navigation_params.h"
-#include "content/common/render_frame_setup.mojom.h"
 #include "content/common/site_isolation_policy.h"
 #include "content/common/swapped_out_messages.h"
 #include "content/public/browser/ax_event_notification_details.h"
@@ -246,6 +245,7 @@
       pending_web_ui_type_(WebUI::kNoWebUI),
       should_reuse_web_ui_(false),
       last_navigation_lofi_state_(LOFI_UNSPECIFIED),
+      frame_host_binding_(this),
       weak_ptr_factory_(this) {
   frame_tree_->AddRenderViewHostRef(render_view_host_);
   GetProcess()->AddRoute(routing_id_, this);
@@ -2384,17 +2384,13 @@
     return;
 
   RegisterMojoServices();
-  mojom::RenderFrameSetupPtr setup;
+  mojom::FrameFactoryPtr frame_factory;
   GetProcess()->GetServiceRegistry()->ConnectToRemoteService(
-      mojo::GetProxy(&setup));
+      mojo::GetProxy(&frame_factory));
 
-  shell::mojom::InterfaceProviderPtr exposed_services;
-  service_registry_->Bind(GetProxy(&exposed_services));
-
-  shell::mojom::InterfaceProviderPtr services;
-  setup->ExchangeInterfaceProviders(routing_id_, GetProxy(&services),
-                                    std::move(exposed_services));
-  service_registry_->BindRemoteServiceProvider(std::move(services));
+  frame_factory->CreateFrame(routing_id_, GetProxy(&frame_),
+                             frame_host_binding_.CreateInterfacePtrAndBind());
+  frame_->GetInterfaceProvider(service_registry_->TakeRemoteRequest());
 
 #if defined(OS_ANDROID)
   service_registry_android_ =
@@ -2412,6 +2408,8 @@
 #endif
 
   service_registry_.reset();
+  frame_.reset();
+  frame_host_binding_.Close();
 
   // Disconnect with ImageDownloader Mojo service in RenderFrame.
   mojo_image_downloader_.reset();
@@ -2674,6 +2672,11 @@
   Send(new FrameMsg_RunFileChooserResponse(routing_id_, files));
 }
 
+void RenderFrameHostImpl::GetInterfaceProvider(
+    shell::mojom::InterfaceProviderRequest interfaces) {
+  service_registry_->Bind(std::move(interfaces));
+}
+
 #if defined(USE_EXTERNAL_POPUP_MENU)
 #if defined(OS_MACOSX)
 
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 990d305..dc561b18 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -29,6 +29,7 @@
 #include "content/common/accessibility_mode_enums.h"
 #include "content/common/ax_content_node_data.h"
 #include "content/common/content_export.h"
+#include "content/common/frame.mojom.h"
 #include "content/common/frame_message_enums.h"
 #include "content/common/frame_replication_state.h"
 #include "content/common/image_downloader/image_downloader.mojom.h"
@@ -99,9 +100,11 @@
 struct Referrer;
 struct ResourceResponse;
 
-class CONTENT_EXPORT RenderFrameHostImpl : public RenderFrameHost,
-                                           public BrowserAccessibilityDelegate,
-                                           public SiteInstanceImpl::Observer {
+class CONTENT_EXPORT RenderFrameHostImpl
+    : public RenderFrameHost,
+      NON_EXPORTED_BASE(public mojom::FrameHost),
+      public BrowserAccessibilityDelegate,
+      public SiteInstanceImpl::Observer {
  public:
   using AXTreeSnapshotCallback =
       base::Callback<void(
@@ -156,6 +159,10 @@
   void FilesSelectedInChooser(const std::vector<FileChooserFileInfo>& files,
                               FileChooserParams::Mode permissions) override;
 
+  // mojom::FrameHost
+  void GetInterfaceProvider(
+      shell::mojom::InterfaceProviderRequest interfaces) override;
+
   // IPC::Sender
   bool Send(IPC::Message* msg) override;
 
@@ -1019,6 +1026,9 @@
   // same LoFi status as the top-level frame.
   LoFiState last_navigation_lofi_state_;
 
+  mojo::Binding<mojom::FrameHost> frame_host_binding_;
+  mojom::FramePtr frame_;
+
   // NOTE: This must be the last member.
   base::WeakPtrFactory<RenderFrameHostImpl> weak_ptr_factory_;
 
diff --git a/content/browser/mojo/mojo_application_host.cc b/content/browser/mojo/mojo_application_host.cc
index 2a70626..5bbe45a 100644
--- a/content/browser/mojo/mojo_application_host.cc
+++ b/content/browser/mojo/mojo_application_host.cc
@@ -13,6 +13,8 @@
 namespace content {
 namespace {
 
+// TODO(beng): remove this in favor of just using shell APIs directly.
+//             http://crbug.com/2072603002
 class ApplicationSetupImpl : public mojom::ApplicationSetup {
  public:
   ApplicationSetupImpl(ServiceRegistryImpl* service_registry,
@@ -29,7 +31,8 @@
       shell::mojom::InterfaceProviderRequest services,
       shell::mojom::InterfaceProviderPtr exposed_services) override {
     service_registry_->Bind(std::move(services));
-    service_registry_->BindRemoteServiceProvider(std::move(exposed_services));
+    mojo::FuseInterface(service_registry_->TakeRemoteRequest(),
+                        exposed_services.PassInterface());
   }
 
   mojo::Binding<mojom::ApplicationSetup> binding_;
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index 6e26164..0dba1ed 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -647,14 +647,13 @@
 
   shell::mojom::InterfaceProviderPtr exposed_services;
   service_registry_->Bind(GetProxy(&exposed_services));
-  shell::mojom::InterfaceProviderPtr services;
-  shell::mojom::InterfaceProviderRequest services_request = GetProxy(&services);
+  shell::mojom::InterfaceProviderRequest request =
+      service_registry_->TakeRemoteRequest();
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
       base::Bind(SetupMojoOnUIThread, process_id(), thread_id_,
-                 base::Passed(&services_request),
+                 base::Passed(&request),
                  base::Passed(exposed_services.PassInterface())));
-  service_registry_->BindRemoteServiceProvider(std::move(services));
 }
 
 void EmbeddedWorkerInstance::OnScriptLoadFailed() {
diff --git a/content/browser/service_worker/embedded_worker_test_helper.cc b/content/browser/service_worker/embedded_worker_test_helper.cc
index 0febd11..6e22d4c 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.cc
+++ b/content/browser/service_worker/embedded_worker_test_helper.cc
@@ -24,6 +24,7 @@
 #include "content/public/common/push_event_payload.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_browser_context.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -104,8 +105,8 @@
   render_process_service_registry_.ServiceRegistry::AddService(
       base::Bind(&MockEmbeddedWorkerSetup::Create, weak_factory_.GetWeakPtr()));
   shell::mojom::InterfaceProviderPtr services;
-  render_process_service_registry_.Bind(mojo::GetProxy(&services));
-  host_service_registry->BindRemoteServiceProvider(std::move(services));
+  render_process_service_registry_.Bind(
+      host_service_registry->TakeRemoteRequest());
   render_process_host_->SetServiceRegistry(std::move(host_service_registry));
 }
 
@@ -426,7 +427,11 @@
     shell::mojom::InterfaceProviderPtr exposed_services) {
   std::unique_ptr<ServiceRegistryImpl> new_registry(new ServiceRegistryImpl);
   new_registry->Bind(std::move(services));
-  new_registry->BindRemoteServiceProvider(std::move(exposed_services));
+  // TODO(beng): this shouldn't be necessary once we adjust this API to look
+  //             more like the one we've created for Frame.
+  //             http://crbug.com/621187
+  mojo::FuseInterface(new_registry->TakeRemoteRequest(),
+                      exposed_services.PassInterface());
   OnSetupMojo(new_registry.get());
   thread_id_service_registry_map_.add(thread_id, std::move(new_registry));
 }
diff --git a/content/child/mojo/mojo_application.cc b/content/child/mojo/mojo_application.cc
index 7aa4597..ffa285f 100644
--- a/content/child/mojo/mojo_application.cc
+++ b/content/child/mojo/mojo_application.cc
@@ -30,9 +30,9 @@
   shell::mojom::InterfaceProviderPtr services;
   shell::mojom::InterfaceProviderPtr exposed_services;
   service_registry_.Bind(GetProxy(&exposed_services));
-  application_setup->ExchangeInterfaceProviders(GetProxy(&services),
-                                                std::move(exposed_services));
-  service_registry_.BindRemoteServiceProvider(std::move(services));
+  application_setup->ExchangeInterfaceProviders(
+      service_registry_.TakeRemoteRequest(),
+      std::move(exposed_services));
 }
 
 }  // namespace content
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index bf26441..b459439c 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -230,10 +230,10 @@
 
   sources = [
     "application_setup.mojom",
+    "frame.mojom",
     "image_downloader/image_downloader.mojom",
     "leveldb_wrapper.mojom",
     "process_control.mojom",
-    "render_frame_setup.mojom",
     "render_widget_window_tree_client_factory.mojom",
     "service_worker/embedded_worker_setup.mojom",
     "storage_partition_service.mojom",
diff --git a/content/common/frame.mojom b/content/common/frame.mojom
new file mode 100644
index 0000000..9ec9644
--- /dev/null
+++ b/content/common/frame.mojom
@@ -0,0 +1,23 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module content.mojom;
+
+import "services/shell/public/interfaces/interface_provider.mojom";
+
+// Implemented by the frame provider (e.g. renderer processes).
+interface Frame {
+  GetInterfaceProvider(shell.mojom.InterfaceProvider& interfaces);
+};
+
+// Implemented by the frame server (i.e. the browser process).
+interface FrameHost {
+  GetInterfaceProvider(shell.mojom.InterfaceProvider& interfaces);
+};
+
+// Implemented by a service that provides implementations of the Frame
+// interface. (e.g. renderer processes).
+interface FrameFactory {
+  CreateFrame(int32 frame_routing_id, Frame& frame, FrameHost host);
+};
diff --git a/content/common/mojo/service_registry_impl.cc b/content/common/mojo/service_registry_impl.cc
index 39e575a..7a16b4c9 100644
--- a/content/common/mojo/service_registry_impl.cc
+++ b/content/common/mojo/service_registry_impl.cc
@@ -15,13 +15,12 @@
 }
 
 ServiceRegistryImpl::ServiceRegistryImpl()
-    : binding_(this), weak_factory_(this) {}
+    : binding_(this),
+      weak_factory_(this) {
+  remote_provider_request_ = GetProxy(&remote_provider_);
+}
 
 ServiceRegistryImpl::~ServiceRegistryImpl() {
-  while (!pending_connects_.empty()) {
-    mojo::CloseRaw(pending_connects_.front().second);
-    pending_connects_.pop();
-  }
 }
 
 void ServiceRegistryImpl::Bind(shell::mojom::InterfaceProviderRequest request) {
@@ -30,16 +29,9 @@
       &ServiceRegistryImpl::OnConnectionError, base::Unretained(this)));
 }
 
-void ServiceRegistryImpl::BindRemoteServiceProvider(
-    shell::mojom::InterfaceProviderPtr service_provider) {
-  CHECK(!remote_provider_);
-  remote_provider_ = std::move(service_provider);
-  while (!pending_connects_.empty()) {
-    remote_provider_->GetInterface(
-        mojo::String::From(pending_connects_.front().first),
-        mojo::ScopedMessagePipeHandle(pending_connects_.front().second));
-    pending_connects_.pop();
-  }
+shell::mojom::InterfaceProviderRequest
+ServiceRegistryImpl::TakeRemoteRequest() {
+  return std::move(remote_provider_request_);
 }
 
 void ServiceRegistryImpl::AddService(
@@ -62,12 +54,6 @@
     override_it->second.Run(std::move(handle));
     return;
   }
-
-  if (!remote_provider_) {
-    pending_connects_.push(
-        std::make_pair(service_name.as_string(), handle.release()));
-    return;
-  }
   remote_provider_->GetInterface(
       mojo::String::From(service_name.as_string()), std::move(handle));
 }
diff --git a/content/common/mojo/service_registry_impl.h b/content/common/mojo/service_registry_impl.h
index 923414e9..bf9352ba 100644
--- a/content/common/mojo/service_registry_impl.h
+++ b/content/common/mojo/service_registry_impl.h
@@ -30,8 +30,7 @@
 
   // ServiceRegistry overrides.
   void Bind(shell::mojom::InterfaceProviderRequest request) override;
-  void BindRemoteServiceProvider(
-      shell::mojom::InterfaceProviderPtr service_provider) override;
+  shell::mojom::InterfaceProviderRequest TakeRemoteRequest() override;
   void AddService(
       const std::string& service_name,
       const ServiceFactory& service_factory,
@@ -61,13 +60,12 @@
 
   mojo::Binding<shell::mojom::InterfaceProvider> binding_;
   shell::mojom::InterfaceProviderPtr remote_provider_;
+  shell::mojom::InterfaceProviderRequest remote_provider_request_;
 
   std::map<
       std::string,
       std::pair<ServiceFactory, scoped_refptr<base::SingleThreadTaskRunner>>>
       service_factories_;
-  std::queue<std::pair<std::string, mojo::MessagePipeHandle> >
-      pending_connects_;
 
   std::map<std::string, ServiceFactory> service_overrides_;
 
diff --git a/content/common/render_frame_setup.mojom b/content/common/render_frame_setup.mojom
deleted file mode 100644
index b020f42..0000000
--- a/content/common/render_frame_setup.mojom
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module content.mojom;
-
-import "services/shell/public/interfaces/interface_provider.mojom";
-
-interface RenderFrameSetup {
-  ExchangeInterfaceProviders(
-      int32 frame_routing_id,
-      shell.mojom.InterfaceProvider& remote_interfaces,
-      shell.mojom.InterfaceProvider local_interfaces);
-};
diff --git a/content/content_common_mojo_bindings.gyp b/content/content_common_mojo_bindings.gyp
index 7708406..417cfc6 100644
--- a/content/content_common_mojo_bindings.gyp
+++ b/content/content_common_mojo_bindings.gyp
@@ -12,10 +12,10 @@
         'mojom_files': [
           # NOTE: Sources duplicated in //content/common/BUILD.gn:mojo_bindings.
           'common/application_setup.mojom',
+          'common/frame.mojom',
           'common/image_downloader/image_downloader.mojom',
           'common/leveldb_wrapper.mojom',
           'common/process_control.mojom',
-          'common/render_frame_setup.mojom',
           'common/service_worker/embedded_worker_setup.mojom',
           'common/storage_partition_service.mojom',
         ],
diff --git a/content/public/browser/android/synchronous_compositor_client.h b/content/public/browser/android/synchronous_compositor_client.h
index f4343a5c..fa28023 100644
--- a/content/public/browser/android/synchronous_compositor_client.h
+++ b/content/public/browser/android/synchronous_compositor_client.h
@@ -33,20 +33,22 @@
                                     int routing_id) = 0;
 
   // See LayerScrollOffsetDelegate for details.
-  virtual void UpdateRootLayerState(const gfx::Vector2dF& total_scroll_offset,
+  virtual void UpdateRootLayerState(SynchronousCompositor* compositor,
+                                    const gfx::Vector2dF& total_scroll_offset,
                                     const gfx::Vector2dF& max_scroll_offset,
                                     const gfx::SizeF& scrollable_size,
                                     float page_scale_factor,
                                     float min_page_scale_factor,
                                     float max_page_scale_factor) = 0;
 
-  virtual void DidOverscroll(const gfx::Vector2dF& accumulated_overscroll,
+  virtual void DidOverscroll(SynchronousCompositor* compositor,
+                             const gfx::Vector2dF& accumulated_overscroll,
                              const gfx::Vector2dF& latest_overscroll_delta,
                              const gfx::Vector2dF& current_fling_velocity) = 0;
 
-  virtual void PostInvalidate() = 0;
+  virtual void PostInvalidate(SynchronousCompositor* compositor) = 0;
 
-  virtual void DidUpdateContent() = 0;
+  virtual void DidUpdateContent(SynchronousCompositor* compositor) = 0;
 
  protected:
   SynchronousCompositorClient() {}
diff --git a/content/public/common/service_registry.h b/content/public/common/service_registry.h
index 03f9f5a..c3852702 100644
--- a/content/public/common/service_registry.h
+++ b/content/public/common/service_registry.h
@@ -31,12 +31,10 @@
   // Binds this ServiceProvider implementation to a message pipe endpoint.
   virtual void Bind(shell::mojom::InterfaceProviderRequest request) = 0;
 
-  // Binds to a remote ServiceProvider. This will expose added services to the
-  // remote ServiceProvider with the corresponding handle and enable
-  // ConnectToRemoteService to provide access to services exposed by the remote
-  // ServiceProvider.
-  virtual void BindRemoteServiceProvider(
-      shell::mojom::InterfaceProviderPtr service_provider) = 0;
+  // ServiceRegistry is created with a bound InterfaceProviderPtr for remote
+  // interfaces, this is the server end of that pipe which should be passed
+  // to the remote end.
+  virtual shell::mojom::InterfaceProviderRequest TakeRemoteRequest() = 0;
 
   // Make the service created by |service_factory| available to the remote
   // ServiceProvider. In response to each request for a service,
diff --git a/content/public/test/mock_render_thread.cc b/content/public/test/mock_render_thread.cc
index e6e6530..a3522a31 100644
--- a/content/public/test/mock_render_thread.cc
+++ b/content/public/test/mock_render_thread.cc
@@ -27,8 +27,7 @@
       new_window_routing_id_(0),
       new_window_main_frame_routing_id_(0),
       new_window_main_frame_widget_routing_id_(0),
-      new_frame_routing_id_(0),
-      service_registry_(new ServiceRegistryImpl) {}
+      new_frame_routing_id_(0) {}
 
 MockRenderThread::~MockRenderThread() {
   while (!filters_.empty()) {
@@ -185,7 +184,8 @@
 #endif  // OS_WIN
 
 ServiceRegistry* MockRenderThread::GetServiceRegistry() {
-  DCHECK(service_registry_);
+  if (!service_registry_)
+    service_registry_.reset(new ServiceRegistryImpl);
   return service_registry_.get();
 }
 
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index b57c63c..f3efae1 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -1106,6 +1106,7 @@
       focused_pepper_plugin_(nullptr),
       pepper_last_mouse_event_target_(nullptr),
 #endif
+      frame_binding_(this),
       weak_factory_(this) {
   std::pair<RoutingIDFrameMap::iterator, bool> result =
       g_routing_id_frame_map.Get().insert(std::make_pair(routing_id_, this));
@@ -1599,11 +1600,11 @@
                    std::unique_ptr<StreamOverrideParameters>());
 }
 
-void RenderFrameImpl::BindServiceRegistry(
-    shell::mojom::InterfaceProviderRequest services,
-    shell::mojom::InterfaceProviderPtr exposed_services) {
-  service_registry_.Bind(std::move(services));
-  service_registry_.BindRemoteServiceProvider(std::move(exposed_services));
+void RenderFrameImpl::Bind(mojom::FrameRequest request,
+                           mojom::FrameHostPtr host) {
+  frame_binding_.Bind(std::move(request));
+  frame_host_ = std::move(host);
+  frame_host_->GetInterfaceProvider(service_registry_.TakeRemoteRequest());
 }
 
 ManifestManager* RenderFrameImpl::manifest_manager() {
@@ -2510,6 +2511,13 @@
   return is_pasting_;
 }
 
+// mojom::Frame implementation -------------------------------------------------
+
+void RenderFrameImpl::GetInterfaceProvider(
+    shell::mojom::InterfaceProviderRequest request) {
+  service_registry_.Bind(std::move(request));
+}
+
 // blink::WebFrameClient implementation ----------------------------------------
 
 blink::WebPlugin* RenderFrameImpl::createPlugin(
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 8c2b6010..b70731504 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -23,6 +23,7 @@
 #include "base/process/process_handle.h"
 #include "build/build_config.h"
 #include "content/common/accessibility_mode_enums.h"
+#include "content/common/frame.mojom.h"
 #include "content/common/frame_message_enums.h"
 #include "content/common/mojo/service_registry_impl.h"
 #include "content/public/common/console_message_level.h"
@@ -164,6 +165,7 @@
 
 class CONTENT_EXPORT RenderFrameImpl
     : public RenderFrame,
+      NON_EXPORTED_BASE(mojom::Frame),
       NON_EXPORTED_BASE(public blink::WebFrameClient),
       NON_EXPORTED_BASE(public blink::WebFrameSerializerClient) {
  public:
@@ -423,6 +425,10 @@
   bool IsUsingLoFi() const override;
   bool IsPasting() const override;
 
+  // mojom::Frame implementation:
+  void GetInterfaceProvider(
+      shell::mojom::InterfaceProviderRequest request) override;
+
   // blink::WebFrameClient implementation:
   blink::WebPlugin* createPlugin(blink::WebLocalFrame* frame,
                                  const blink::WebPluginParams& params) override;
@@ -633,9 +639,8 @@
       blink::WebFrameSerializerClient::FrameSerializationStatus status)
       override;
 
-  // Binds this render frame's service registry.
-  void BindServiceRegistry(shell::mojom::InterfaceProviderRequest services,
-                           shell::mojom::InterfaceProviderPtr exposed_services);
+  // Binds to the FrameHost in the browser.
+  void Bind(mojom::FrameRequest frame, mojom::FrameHostPtr frame_host);
 
   ManifestManager* manifest_manager();
 
@@ -1278,6 +1283,9 @@
   PepperPluginInstanceImpl* pepper_last_mouse_event_target_;
 #endif
 
+  mojo::Binding<mojom::Frame> frame_binding_;
+  mojom::FrameHostPtr frame_host_;
+
   base::WeakPtrFactory<RenderFrameImpl> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(RenderFrameImpl);
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index defc118..c521f601 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -74,7 +74,6 @@
 #include "content/common/frame_messages.h"
 #include "content/common/gpu/client/context_provider_command_buffer.h"
 #include "content/common/gpu_process_launch_causes.h"
-#include "content/common/render_frame_setup.mojom.h"
 #include "content/common/render_process_messages.h"
 #include "content/common/resource_messages.h"
 #include "content/common/service_worker/embedded_worker_setup.mojom.h"
@@ -316,16 +315,16 @@
   v8::Date::DateTimeConfigurationChangeNotification(isolate);
 }
 
-class RenderFrameSetupImpl : public mojom::RenderFrameSetup {
+class FrameFactoryImpl : public mojom::FrameFactory {
  public:
-  explicit RenderFrameSetupImpl(
-      mojo::InterfaceRequest<mojom::RenderFrameSetup> request)
+  explicit FrameFactoryImpl(mojom::FrameFactoryRequest request)
       : routing_id_highmark_(-1), binding_(this, std::move(request)) {}
 
-  void ExchangeInterfaceProviders(
-      int32_t frame_routing_id,
-      shell::mojom::InterfaceProviderRequest services,
-      shell::mojom::InterfaceProviderPtr exposed_services) override {
+ private:
+  // mojom::FrameFactory:
+  void CreateFrame(int32_t frame_routing_id,
+                   mojom::FrameRequest frame_request,
+                   mojom::FrameHostPtr frame_host) override {
     // TODO(morrita): This is for investigating http://crbug.com/415059 and
     // should be removed once it is fixed.
     CHECK_LT(routing_id_highmark_, frame_routing_id);
@@ -336,23 +335,21 @@
     // created due to a race between the message and a ViewMsg_New IPC that
     // triggers creation of the RenderFrame we want.
     if (!frame) {
-      RenderThreadImpl::current()->RegisterPendingRenderFrameConnect(
-          frame_routing_id, std::move(services), std::move(exposed_services));
+      RenderThreadImpl::current()->RegisterPendingFrameCreate(
+          frame_routing_id, std::move(frame_request), std::move(frame_host));
       return;
     }
 
-    frame->BindServiceRegistry(std::move(services),
-                               std::move(exposed_services));
+    frame->Bind(std::move(frame_request), std::move(frame_host));
   }
 
  private:
   int32_t routing_id_highmark_;
-  mojo::StrongBinding<mojom::RenderFrameSetup> binding_;
+  mojo::StrongBinding<mojom::FrameFactory> binding_;
 };
 
-void CreateRenderFrameSetup(
-    mojo::InterfaceRequest<mojom::RenderFrameSetup> request) {
-  new RenderFrameSetupImpl(std::move(request));
+void CreateFrameFactory(mojom::FrameFactoryRequest request) {
+  new FrameFactoryImpl(std::move(request));
 }
 
 void SetupEmbeddedWorkerOnWorkerThread(
@@ -823,7 +820,7 @@
   GetContentClient()->renderer()->RegisterProcessMojoServices(
       service_registry());
 
-  service_registry()->AddService(base::Bind(CreateRenderFrameSetup));
+  service_registry()->AddService(base::Bind(CreateFrameFactory));
   service_registry()->AddService(base::Bind(CreateEmbeddedWorkerSetup));
 
 #if defined(MOJO_SHELL_CLIENT)
@@ -1024,24 +1021,17 @@
 
 void RenderThreadImpl::AddRoute(int32_t routing_id, IPC::Listener* listener) {
   ChildThreadImpl::GetRouter()->AddRoute(routing_id, listener);
-  PendingRenderFrameConnectMap::iterator it =
-      pending_render_frame_connects_.find(routing_id);
-  if (it == pending_render_frame_connects_.end())
+  auto it = pending_frame_creates_.find(routing_id);
+  if (it == pending_frame_creates_.end())
     return;
 
   RenderFrameImpl* frame = RenderFrameImpl::FromRoutingID(routing_id);
   if (!frame)
     return;
 
-  scoped_refptr<PendingRenderFrameConnect> connection(it->second);
-  shell::mojom::InterfaceProviderRequest services(
-      std::move(connection->services()));
-  shell::mojom::InterfaceProviderPtr exposed_services(
-      std::move(connection->exposed_services()));
-  exposed_services.set_connection_error_handler(mojo::Closure());
-  pending_render_frame_connects_.erase(it);
-
-  frame->BindServiceRegistry(std::move(services), std::move(exposed_services));
+  scoped_refptr<PendingFrameCreate> create(it->second);
+  frame->Bind(it->second->TakeFrameRequest(), it->second->TakeFrameHost());
+  pending_frame_creates_.erase(it);
 }
 
 void RenderThreadImpl::RemoveRoute(int32_t routing_id) {
@@ -1065,15 +1055,15 @@
   }
 }
 
-void RenderThreadImpl::RegisterPendingRenderFrameConnect(
+void RenderThreadImpl::RegisterPendingFrameCreate(
     int routing_id,
-    shell::mojom::InterfaceProviderRequest services,
-    shell::mojom::InterfaceProviderPtr exposed_services) {
-  std::pair<PendingRenderFrameConnectMap::iterator, bool> result =
-      pending_render_frame_connects_.insert(std::make_pair(
+    mojom::FrameRequest frame_request,
+    mojom::FrameHostPtr frame_host) {
+  std::pair<PendingFrameCreateMap::iterator, bool> result =
+      pending_frame_creates_.insert(std::make_pair(
           routing_id,
-          make_scoped_refptr(new PendingRenderFrameConnect(
-              routing_id, std::move(services), std::move(exposed_services)))));
+          make_scoped_refptr(new PendingFrameCreate(
+              routing_id, std::move(frame_request), std::move(frame_host)))));
   CHECK(result.second) << "Inserting a duplicate item.";
 }
 
@@ -2059,29 +2049,27 @@
     blink::decommitFreeableMemory();
 }
 
-RenderThreadImpl::PendingRenderFrameConnect::PendingRenderFrameConnect(
+RenderThreadImpl::PendingFrameCreate::PendingFrameCreate(
     int routing_id,
-    shell::mojom::InterfaceProviderRequest services,
-    shell::mojom::InterfaceProviderPtr exposed_services)
+    mojom::FrameRequest frame_request,
+    mojom::FrameHostPtr frame_host)
     : routing_id_(routing_id),
-      services_(std::move(services)),
-      exposed_services_(std::move(exposed_services)) {
-  // The RenderFrame may be deleted before the ExchangeInterfaceProviders
-  // message is received. In that case, the RenderFrameHost should close the
-  // connection, which is detected by setting an error handler on
-  // |exposed_services_|.
-  exposed_services_.set_connection_error_handler(base::Bind(
-      &RenderThreadImpl::PendingRenderFrameConnect::OnConnectionError,
+      frame_request_(std::move(frame_request)),
+      frame_host_(std::move(frame_host)) {
+  // The RenderFrame may be deleted before the CreateFrame message is received.
+  // In that case, the RenderFrameHost should cancel the create, which is
+  // detected by setting an error handler on |frame_host_|.
+  frame_host_.set_connection_error_handler(base::Bind(
+      &RenderThreadImpl::PendingFrameCreate::OnConnectionError,
       base::Unretained(this)));
 }
 
-RenderThreadImpl::PendingRenderFrameConnect::~PendingRenderFrameConnect() {
+RenderThreadImpl::PendingFrameCreate::~PendingFrameCreate() {
 }
 
-void RenderThreadImpl::PendingRenderFrameConnect::OnConnectionError() {
+void RenderThreadImpl::PendingFrameCreate::OnConnectionError() {
   size_t erased =
-      RenderThreadImpl::current()->pending_render_frame_connects_.erase(
-          routing_id_);
+      RenderThreadImpl::current()->pending_frame_creates_.erase(routing_id_);
   DCHECK_EQ(1u, erased);
 }
 
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index 3fbf1d4..3c9cd7f9 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -24,6 +24,7 @@
 #include "build/build_config.h"
 #include "content/child/child_thread_impl.h"
 #include "content/common/content_export.h"
+#include "content/common/frame.mojom.h"
 #include "content/common/frame_replication_state.h"
 #include "content/common/gpu_process_launch_causes.h"
 #include "content/common/storage_partition_service.mojom.h"
@@ -436,10 +437,9 @@
   void AddEmbeddedWorkerRoute(int32_t routing_id, IPC::Listener* listener);
   void RemoveEmbeddedWorkerRoute(int32_t routing_id);
 
-  void RegisterPendingRenderFrameConnect(
-      int routing_id,
-      shell::mojom::InterfaceProviderRequest services,
-      shell::mojom::InterfaceProviderPtr exposed_services);
+  void RegisterPendingFrameCreate(int routing_id,
+                                  mojom::FrameRequest frame,
+                                  mojom::FrameHostPtr host);
 
   mojom::StoragePartitionService* GetStoragePartitionService();
 
@@ -663,36 +663,34 @@
   bool are_image_decode_tasks_enabled_;
   bool is_threaded_animation_enabled_;
 
-  class PendingRenderFrameConnect
-      : public base::RefCounted<PendingRenderFrameConnect> {
+  class PendingFrameCreate : public base::RefCounted<PendingFrameCreate> {
    public:
-    PendingRenderFrameConnect(
-        int routing_id,
-        shell::mojom::InterfaceProviderRequest services,
-        shell::mojom::InterfaceProviderPtr exposed_services);
+     PendingFrameCreate(int routing_id,
+                        mojom::FrameRequest frame_request,
+                        mojom::FrameHostPtr frame_host);
 
-    shell::mojom::InterfaceProviderRequest& services() { return services_; }
-
-    shell::mojom::InterfaceProviderPtr& exposed_services() {
-      return exposed_services_;
+    mojom::FrameRequest TakeFrameRequest() { return std::move(frame_request_); }
+    mojom::FrameHostPtr TakeFrameHost() {
+      frame_host_.set_connection_error_handler(mojo::Closure());
+      return std::move(frame_host_);
     }
 
    private:
-    friend class base::RefCounted<PendingRenderFrameConnect>;
+    friend class base::RefCounted<PendingFrameCreate>;
 
-    ~PendingRenderFrameConnect();
+    ~PendingFrameCreate();
 
     // Mojo error handler.
     void OnConnectionError();
 
     int routing_id_;
-    shell::mojom::InterfaceProviderRequest services_;
-    shell::mojom::InterfaceProviderPtr exposed_services_;
+    mojom::FrameRequest frame_request_;
+    mojom::FrameHostPtr frame_host_;
   };
 
-  typedef std::map<int, scoped_refptr<PendingRenderFrameConnect>>
-      PendingRenderFrameConnectMap;
-  PendingRenderFrameConnectMap pending_render_frame_connects_;
+  using PendingFrameCreateMap =
+      std::map<int, scoped_refptr<PendingFrameCreate>>;
+  PendingFrameCreateMap pending_frame_creates_;
 
   mojom::StoragePartitionServicePtr storage_partition_service_;
 
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index d802fbae..ae08701 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -43,6 +43,7 @@
 #include "content/renderer/service_worker/service_worker_type_util.h"
 #include "ipc/ipc_message.h"
 #include "ipc/ipc_message_macros.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
 #include "third_party/WebKit/public/platform/URLConversion.h"
 #include "third_party/WebKit/public/platform/WebMessagePortChannel.h"
 #include "third_party/WebKit/public/platform/WebReferrerPolicy.h"
@@ -276,8 +277,8 @@
     shell::mojom::InterfaceProviderRequest services,
     shell::mojom::InterfaceProviderPtr exposed_services) {
   context_->service_registry.Bind(std::move(services));
-  context_->service_registry.BindRemoteServiceProvider(
-      std::move(exposed_services));
+  mojo::FuseInterface(context_->service_registry.TakeRemoteRequest(),
+                      exposed_services.PassInterface());
 }
 
 blink::WebURL ServiceWorkerContextClient::scope() const {
diff --git a/content/shell/browser/shell_mojo_test_utils_android.cc b/content/shell/browser/shell_mojo_test_utils_android.cc
index 1cafe13..e51edf52 100644
--- a/content/shell/browser/shell_mojo_test_utils_android.cc
+++ b/content/shell/browser/shell_mojo_test_utils_android.cc
@@ -53,11 +53,13 @@
 
   shell::mojom::InterfaceProviderPtr exposed_services_a;
   registry_a->Bind(GetProxy(&exposed_services_a));
-  registry_b->BindRemoteServiceProvider(std::move(exposed_services_a));
+  mojo::FuseInterface(registry_b->TakeRemoteRequest(),
+                      exposed_services_a.PassInterface());
 
   shell::mojom::InterfaceProviderPtr exposed_services_b;
   registry_b->Bind(GetProxy(&exposed_services_b));
-  registry_a->BindRemoteServiceProvider(std::move(exposed_services_b));
+  mojo::FuseInterface(registry_a->TakeRemoteRequest(),
+                      exposed_services_b.PassInterface());
 
   content::ServiceRegistryAndroid* wrapper_a =
       ServiceRegistryAndroid::Create(registry_a).release();
diff --git a/docs/useful_urls.md b/docs/useful_urls.md
index b49606cb..9366ddc 100644
--- a/docs/useful_urls.md
+++ b/docs/useful_urls.md
@@ -5,45 +5,40 @@
 
 ## Build Status
 
-|:---------------------------------------------|:------------------------|
-| http://build.chromium.org/p/chromium/console | Main buildbot waterfall |
-| http://chromium-status.appspot.com/lkgr      | Last Known Good Revision. Trybots pull this revision from trunk. |
-| http://chromium-status.appspot.com/revisions | List of the last 100 potential LKGRs |
-| http://build.chromium.org/p/chromium/lkgr-status/ | Status dashboard for LKGR |
-| http://build.chromium.org/p/tryserver.chromium/waterfall?committer=developer@chromium.org | Trybot runs, by developer |
-| http://chromium-status.appspot.com/status_viewer | Tree uptime stats       |
-| http://chromium-cq-status.appspot.com        | Commit queue status     |
-| http://codereview.chromium.org/search?closed=3&commit=2&limit=50 | Pending commit queue jobs |
-| http://chromium-build-logs.appspot.com/      | Search for historical test failures by test name |
-| http://chromium-build-logs.appspot.com/list  | Filterable list of most recent build logs |
+* [Main buildbot waterfall](http://build.chromium.org/p/chromium/console)
+* [Last Known Good Revision](http://chromium-status.appspot.com/lkgr) : Trybots pull this revision from trunk
+* [List of the last 100 potential LKGRs](http://chromium-status.appspot.com/revisions)
+* [Status dashboard for LKGR](http://build.chromium.org/p/chromium/lkgr-status/)
+* http://build.chromium.org/p/tryserver.chromium/waterfall?committer=developer@chromium.org : Trybot runs, by developer
+* [Tree uptime stats](http://chromium-status.appspot.com/status_viewer)
+* [Commit queue status](http://chromium-cq-status.appspot.com)
+* [Pending commit queue jobs](http://codereview.chromium.org/search?closed=3&commit=2&limit=50)
+* [Search for historical test failures by test name](http://chromium-build-logs.appspot.com/)
+* [Filterable list of most recent build logs](http://chromium-build-logs.appspot.com/list)
 
 ## For Sheriffs
 
-|:---------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------|
-| http://build.chromium.org/p/chromium.chromiumos/waterfall?show_events=true&reload=120&failures_only=true | List of failing bots for a waterfall (chromium.chromiumos as an example) |
-| http://build.chromium.org/p/chromium.linux/waterfall?show_events=true&reload=120&builder=Linux%20Builder%20x64&builder=Linux%20Builder%20(dbg) | Monitor one or multiple bots (Linux Builder x64 and Linux Builder (dbg) on chromium.linux as an example) |
-| http://build.chromium.org/p/chromium.win/waterfall/help                                                  | Customize the waterfall view for a waterfall (using chromium.win as an example) |
-| http://chromium-sheriffing.appspot.com                                                                   | Alternate waterfall view that helps with test failure triage             |
-| http://test-results.appspot.com/dashboards/flakiness_dashboard.html                                      | Lists historical test results for the bots                               |
+* http://build.chromium.org/p/chromium.chromiumos/waterfall?show_events=true&reload=120&failures_only=true : List of failing bots for a waterfall(chromium.chromiumos as an example)
+* http://build.chromium.org/p/chromium.linux/waterfall?show_events=true&reload=120&builder=Linux%20Builder%20x64&builder=Linux%20Builder%20(dbg) : Monitor one or multiple bots(Linux Builder x64 and Linux Builder (dbg) on chromium.linux as an example)
+* http://build.chromium.org/p/chromium.win/waterfall/help : Customize the waterfall view for a waterfall(using chromium.win as an example)
+* [Alternate waterfall view that helps with test failure triage](http://chromium-sheriffing.appspot.com)
+* [Lists historical test results for the bots](http://test-results.appspot.com/dashboards/flakiness_dashboard.html)
 
 ## Release Information
 
-|:--------------------------------------|:---------------------------------------------------|
-| https://omahaproxy.appspot.com/viewer | Current release versions of Chrome on all channels |
-| https://omahaproxy.appspot.com/       | Looks up the revision of a build/release version   |
+* [Current release versions of Chrome on all channels](https://omahaproxy.appspot.com/viewer)
+* [Looks up the revision of a build/release version](https://omahaproxy.appspot.com/)
 
 ## Source Information
 
-|:------------------------|:------------|
-| http://cs.chromium.org/ | Code Search |
-| http://cs.chromium.org/SEARCH_TERM | Code Search for a specific SEARCH\_TERM |
-| https://chromium.googlesource.com/chromium/src/ | Gitiles Source Code Browser |
-| https://chromium.googlesource.com/chromium/src/+log/b6cfa6a..9a2e0a8?pretty=fuller | Git changes in revision range (also works for build numbers) |
-| http://build.chromium.org/f/chromium/perf/dashboard/ui/changelog.html?url=/trunk/src&mode=html&range=SUCCESS_REV:FAILURE_REV | SVN changes in revision range |
-| http://build.chromium.org/f/chromium/perf/dashboard/ui/changelog_blink.html?url=/trunk&mode=html&range=SUCCESS_REV:FAILURE_REV | Blink changes in revision range |
+* [Code Search](http://cs.chromium.org/)
+* http://cs.chromium.org/SEARCH_TERM : Code Search for a specific SEARCH\_TERM 
+* [Gitiles Source Code Browser](https://chromium.googlesource.com/chromium/src/)
+* https://chromium.googlesource.com/chromium/src/+log/b6cfa6a..9a2e0a8?pretty=fuller : Git changes in revision range(also works for build numbers)
+* http://build.chromium.org/f/chromium/perf/dashboard/ui/changelog.html?url=/trunk/src&mode=html&range=SUCCESS_REV:FAILURE_REV : SVN changes in revision range 
+* http://build.chromium.org/f/chromium/perf/dashboard/ui/changelog_blink.html?url=/trunk&mode=html&range=SUCCESS_REV:FAILURE_REV : Blink changes in revision range
 
 ## Communication
 
-|:------------------------------------------------------------------|:-------------------------|
-| http://groups.google.com/a/chromium.org/group/chromium-dev/topics | Chromium Developers List |
-| http://groups.google.com/a/chromium.org/group/chromium-discuss/topics | Chromium Users List      |
+* [Chromium Developers List](http://groups.google.com/a/chromium.org/group/chromium-dev/topics)
+* [Chromium Users List](http://groups.google.com/a/chromium.org/group/chromium-discuss/topics)
diff --git a/media/base/cdm_context.cc b/media/base/cdm_context.cc
index 5bba924..4ad61e0 100644
--- a/media/base/cdm_context.cc
+++ b/media/base/cdm_context.cc
@@ -6,6 +6,8 @@
 
 namespace media {
 
+const int CdmContext::kInvalidCdmId = 0;
+
 CdmContext::CdmContext() {}
 
 CdmContext::~CdmContext() {}
diff --git a/media/base/cdm_context.h b/media/base/cdm_context.h
index d41281e..d046927 100644
--- a/media/base/cdm_context.h
+++ b/media/base/cdm_context.h
@@ -19,7 +19,7 @@
 class MEDIA_EXPORT CdmContext {
  public:
   // Indicates an invalid CDM ID. See GetCdmId() for details.
-  static const int kInvalidCdmId = 0;
+  static const int kInvalidCdmId;
 
   virtual ~CdmContext();
 
diff --git a/media/base/mock_filters.cc b/media/base/mock_filters.cc
index cff598ab..0a7536b 100644
--- a/media/base/mock_filters.cc
+++ b/media/base/mock_filters.cc
@@ -135,7 +135,11 @@
 MockCdmContext::~MockCdmContext() {}
 
 int MockCdmContext::GetCdmId() const {
-  return CdmContext::kInvalidCdmId;
+  return cdm_id_;
+}
+
+void MockCdmContext::set_cdm_id(int cdm_id) {
+  cdm_id_ = cdm_id;
 }
 
 }  // namespace media
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index 78d18a5..03f020a 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -352,7 +352,11 @@
   MOCK_METHOD0(GetDecryptor, Decryptor*());
   int GetCdmId() const override;
 
+  void set_cdm_id(int cdm_id);
+
  private:
+  int cdm_id_ = CdmContext::kInvalidCdmId;
+
   DISALLOW_COPY_AND_ASSIGN(MockCdmContext);
 };
 
diff --git a/media/mojo/BUILD.gn b/media/mojo/BUILD.gn
index 5323d73..3a867fd 100644
--- a/media/mojo/BUILD.gn
+++ b/media/mojo/BUILD.gn
@@ -6,6 +6,7 @@
 
 test("media_mojo_unittests") {
   sources = [
+    "clients/mojo_renderer_unittest.cc",
     "common/media_type_converters_unittest.cc",
     "common/mojo_shared_buffer_video_frame_unittest.cc",
     "services/mojo_cdm_allocator_unittest.cc",
@@ -17,6 +18,7 @@
     "//media",
     "//media:cdm_api",
     "//media/base:test_support",
+    "//media/mojo/clients",
     "//media/mojo/common",
     "//media/mojo/interfaces",
     "//media/mojo/services",
diff --git a/media/mojo/clients/BUILD.gn b/media/mojo/clients/BUILD.gn
index 9fd09ff..a6b264c 100644
--- a/media/mojo/clients/BUILD.gn
+++ b/media/mojo/clients/BUILD.gn
@@ -7,8 +7,12 @@
   visibility = [
     "//content/renderer",
 
+    # TODO(xhwang): Only allow //media/mojo:media_mojo_unittests
+    "//media/mojo:*",
+
     # TODO(xhwang): Only allow //media/mojo/services:media_mojo_shell_unittests
     "//media/mojo/services:*",
+
     "//media/test/*",
   ]
 
diff --git a/media/mojo/clients/mojo_renderer_impl.cc b/media/mojo/clients/mojo_renderer_impl.cc
index f407b89..c0e2891 100644
--- a/media/mojo/clients/mojo_renderer_impl.cc
+++ b/media/mojo/clients/mojo_renderer_impl.cc
@@ -34,6 +34,8 @@
 MojoRendererImpl::~MojoRendererImpl() {
   DVLOG(1) << __FUNCTION__;
   DCHECK(task_runner_->BelongsToCurrentThread());
+
+  CancelPendingCallbacks();
 }
 
 void MojoRendererImpl::Initialize(
@@ -44,8 +46,13 @@
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(demuxer_stream_provider);
 
+  if (encountered_error_) {
+    task_runner_->PostTask(
+        FROM_HERE, base::Bind(init_cb, PIPELINE_ERROR_INITIALIZATION_FAILED));
+    return;
+  }
+
   demuxer_stream_provider_ = demuxer_stream_provider;
-  client_ = client;
   init_cb_ = init_cb;
 
   // Create audio and video mojom::DemuxerStream and bind its lifetime to
@@ -68,10 +75,10 @@
   // Using base::Unretained(this) is safe because |this| owns
   // |remote_renderer_|, and the callback won't be dispatched if
   // |remote_renderer_| is destroyed.
-  remote_renderer_->Initialize(
-      binding_.CreateInterfacePtrAndBind(), std::move(audio_stream),
-      std::move(video_stream),
-      base::Bind(&MojoRendererImpl::OnInitialized, base::Unretained(this)));
+  remote_renderer_->Initialize(binding_.CreateInterfacePtrAndBind(),
+                               std::move(audio_stream), std::move(video_stream),
+                               base::Bind(&MojoRendererImpl::OnInitialized,
+                                          base::Unretained(this), client));
 }
 
 void MojoRendererImpl::SetCdm(CdmContext* cdm_context,
@@ -79,24 +86,44 @@
   DVLOG(1) << __FUNCTION__;
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(cdm_context);
+  DCHECK(!cdm_attached_cb.is_null());
+  DCHECK(cdm_attached_cb_.is_null());
+
+  if (encountered_error_) {
+    task_runner_->PostTask(FROM_HERE, base::Bind(cdm_attached_cb, false));
+    return;
+  }
 
   int32_t cdm_id = cdm_context->GetCdmId();
   if (cdm_id == CdmContext::kInvalidCdmId) {
     DVLOG(2) << "MojoRendererImpl only works with remote CDMs but the CDM ID "
                 "is invalid.";
-    cdm_attached_cb.Run(false);
+    task_runner_->PostTask(FROM_HERE, base::Bind(cdm_attached_cb, false));
     return;
   }
 
   BindRemoteRendererIfNeeded();
-  remote_renderer_->SetCdm(cdm_id, cdm_attached_cb);
+
+  cdm_attached_cb_ = cdm_attached_cb;
+  remote_renderer_->SetCdm(cdm_id, base::Bind(&MojoRendererImpl::OnCdmAttached,
+                                              base::Unretained(this)));
 }
 
 void MojoRendererImpl::Flush(const base::Closure& flush_cb) {
   DVLOG(2) << __FUNCTION__;
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(remote_renderer_.is_bound());
-  remote_renderer_->Flush(flush_cb);
+  DCHECK(!flush_cb.is_null());
+  DCHECK(flush_cb_.is_null());
+
+  if (encountered_error_) {
+    task_runner_->PostTask(FROM_HERE, flush_cb);
+    return;
+  }
+
+  flush_cb_ = flush_cb;
+  remote_renderer_->Flush(
+      base::Bind(&MojoRendererImpl::OnFlushed, base::Unretained(this)));
 }
 
 void MojoRendererImpl::StartPlayingFrom(base::TimeDelta time) {
@@ -116,6 +143,7 @@
   DVLOG(2) << __FUNCTION__;
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(remote_renderer_.is_bound());
+
   remote_renderer_->SetPlaybackRate(playback_rate);
 }
 
@@ -123,6 +151,7 @@
   DVLOG(2) << __FUNCTION__;
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(remote_renderer_.is_bound());
+
   remote_renderer_->SetVolume(volume);
 }
 
@@ -135,13 +164,16 @@
 bool MojoRendererImpl::HasAudio() {
   DVLOG(1) << __FUNCTION__;
   DCHECK(task_runner_->BelongsToCurrentThread());
-  DCHECK(remote_renderer_.get());  // We always bind the renderer.
+  DCHECK(remote_renderer_.is_bound());
+
   return !!demuxer_stream_provider_->GetStream(DemuxerStream::AUDIO);
 }
 
 bool MojoRendererImpl::HasVideo() {
   DVLOG(1) << __FUNCTION__;
   DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK(remote_renderer_.is_bound());
+
   return !!demuxer_stream_provider_->GetStream(DemuxerStream::VIDEO);
 }
 
@@ -170,6 +202,8 @@
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(init_cb_.is_null());
 
+  encountered_error_ = true;
+
   // TODO(tim): Should we plumb error code from remote renderer?
   // http://crbug.com/410451.
   client_->OnError(PIPELINE_ERROR_DECODE);
@@ -194,12 +228,11 @@
   DVLOG(1) << __FUNCTION__;
   DCHECK(task_runner_->BelongsToCurrentThread());
 
-  if (!init_cb_.is_null()) {
-    base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_INITIALIZATION_FAILED);
-    return;
-  }
+  encountered_error_ = true;
+  CancelPendingCallbacks();
 
-  client_->OnError(PIPELINE_ERROR_DECODE);
+  if (client_)
+    client_->OnError(PIPELINE_ERROR_DECODE);
 }
 
 void MojoRendererImpl::BindRemoteRendererIfNeeded() {
@@ -207,6 +240,8 @@
   DCHECK(task_runner_->BelongsToCurrentThread());
 
   // If |remote_renderer_| has already been bound, do nothing.
+  // Note that after Bind() is called, |remote_renderer_| is always bound even
+  // after connection error.
   if (remote_renderer_.is_bound())
     return;
 
@@ -221,13 +256,49 @@
       base::Bind(&MojoRendererImpl::OnConnectionError, base::Unretained(this)));
 }
 
-void MojoRendererImpl::OnInitialized(bool success) {
+void MojoRendererImpl::OnInitialized(media::RendererClient* client,
+                                     bool success) {
   DVLOG(1) << __FUNCTION__;
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(!init_cb_.is_null());
 
+  // Only set |client_| after initialization succeeded. No client methods should
+  // be called before this.
+  if (success)
+    client_ = client;
+
   base::ResetAndReturn(&init_cb_).Run(
       success ? PIPELINE_OK : PIPELINE_ERROR_INITIALIZATION_FAILED);
 }
 
+void MojoRendererImpl::OnFlushed() {
+  DVLOG(1) << __FUNCTION__;
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK(!flush_cb_.is_null());
+
+  base::ResetAndReturn(&flush_cb_).Run();
+}
+
+void MojoRendererImpl::OnCdmAttached(bool success) {
+  DVLOG(1) << __FUNCTION__;
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK(!cdm_attached_cb_.is_null());
+
+  base::ResetAndReturn(&cdm_attached_cb_).Run(success);
+}
+
+void MojoRendererImpl::CancelPendingCallbacks() {
+  DVLOG(1) << __FUNCTION__;
+  DCHECK(task_runner_->BelongsToCurrentThread());
+
+  if (!init_cb_.is_null())
+    base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_INITIALIZATION_FAILED);
+
+  if (!flush_cb_.is_null())
+    base::ResetAndReturn(&flush_cb_).Run();
+
+  if (!cdm_attached_cb_.is_null())
+    base::ResetAndReturn(&cdm_attached_cb_).Run(false);
+}
+
 }  // namespace media
diff --git a/media/mojo/clients/mojo_renderer_impl.h b/media/mojo/clients/mojo_renderer_impl.h
index 9b60afe5..7ad7b11 100644
--- a/media/mojo/clients/mojo_renderer_impl.h
+++ b/media/mojo/clients/mojo_renderer_impl.h
@@ -73,8 +73,12 @@
   // Callback for connection error on |remote_renderer_|.
   void OnConnectionError();
 
-  // Called when |remote_renderer_| has finished initializing.
-  void OnInitialized(bool success);
+  // Callbacks for |remote_renderer_| methods.
+  void OnInitialized(media::RendererClient* client, bool success);
+  void OnFlushed();
+  void OnCdmAttached(bool success);
+
+  void CancelPendingCallbacks();
 
   // |task_runner| on which all methods are invoked, except for GetMediaTime(),
   // which can be called on any thread.
@@ -86,14 +90,14 @@
 
   // Video frame overlays are rendered onto this sink.
   // Rendering of a new overlay is only needed when video natural size changes.
-  VideoRendererSink* video_renderer_sink_;
+  VideoRendererSink* video_renderer_sink_ = nullptr;
 
   // Provider of audio/video DemuxerStreams. Must be valid throughout the
   // lifetime of |this|.
-  DemuxerStreamProvider* demuxer_stream_provider_;
+  DemuxerStreamProvider* demuxer_stream_provider_ = nullptr;
 
   // Client of |this| renderer passed in Initialize.
-  media::RendererClient* client_;
+  media::RendererClient* client_ = nullptr;
 
   // This class is constructed on one thread and used exclusively on another
   // thread. This member is used to safely pass the RendererPtr from one thread
@@ -106,9 +110,11 @@
   // Binding for RendererClient, bound to the |task_runner_|.
   mojo::Binding<RendererClient> binding_;
 
-  // Callbacks passed to Initialize() that we forward messages from
-  // |remote_renderer_| through.
+  bool encountered_error_ = false;
+
   PipelineStatusCB init_cb_;
+  base::Closure flush_cb_;
+  CdmAttachedCB cdm_attached_cb_;
 
   // Lock used to serialize access for |time_|.
   mutable base::Lock lock_;
diff --git a/media/mojo/clients/mojo_renderer_unittest.cc b/media/mojo/clients/mojo_renderer_unittest.cc
new file mode 100644
index 0000000..8f90d33
--- /dev/null
+++ b/media/mojo/clients/mojo_renderer_unittest.cc
@@ -0,0 +1,382 @@
+// 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 <stdint.h>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/platform_thread.h"
+#include "media/base/cdm_config.h"
+#include "media/base/cdm_context.h"
+#include "media/base/gmock_callback_support.h"
+#include "media/base/mock_filters.h"
+#include "media/base/test_helpers.h"
+#include "media/cdm/default_cdm_factory.h"
+#include "media/mojo/clients/mojo_renderer_impl.h"
+#include "media/mojo/common/media_type_converters.h"
+#include "media/mojo/interfaces/content_decryption_module.mojom.h"
+#include "media/mojo/interfaces/renderer.mojom.h"
+#include "media/mojo/services/mojo_cdm_service.h"
+#include "media/mojo/services/mojo_cdm_service_context.h"
+#include "media/mojo/services/mojo_renderer_service.h"
+#include "media/renderers/video_overlay_factory.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::StrictMock;
+
+namespace media {
+
+const int64_t kStartPlayingTimeInMs = 100;
+const char kClearKeyKeySystem[] = "org.w3.clearkey";
+
+ACTION_P2(SetError, renderer_client, error) {
+  renderer_client->OnError(error);
+}
+
+class MojoRendererTest : public ::testing::Test {
+ public:
+  MojoRendererTest() {
+    std::unique_ptr<StrictMock<MockRenderer>> mock_renderer(
+        new StrictMock<MockRenderer>());
+    mock_renderer_ = mock_renderer.get();
+
+    mojom::RendererPtr remote_renderer;
+
+    mojo_renderer_service_ = new MojoRendererService(
+        mojo_cdm_service_context_.GetWeakPtr(), std::move(mock_renderer),
+        mojo::GetProxy(&remote_renderer));
+
+    mojo_renderer_.reset(
+        new MojoRendererImpl(message_loop_.task_runner(),
+                             std::unique_ptr<VideoOverlayFactory>(nullptr),
+                             nullptr, std::move(remote_renderer)));
+
+    // CreateAudioStream() and CreateVideoStream() overrides expectations for
+    // expected non-NULL streams.
+    EXPECT_CALL(demuxer_, GetStream(_)).WillRepeatedly(Return(nullptr));
+
+    EXPECT_CALL(*mock_renderer_, GetMediaTime())
+        .WillRepeatedly(Return(base::TimeDelta()));
+  }
+
+  virtual ~MojoRendererTest() {}
+
+  void Destroy() {
+    mojo_renderer_.reset();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  // Completion callbacks.
+  MOCK_METHOD1(OnInitialized, void(PipelineStatus));
+  MOCK_METHOD0(OnFlushed, void());
+  MOCK_METHOD1(OnCdmAttached, void(bool));
+
+  std::unique_ptr<StrictMock<MockDemuxerStream>> CreateStream(
+      DemuxerStream::Type type) {
+    std::unique_ptr<StrictMock<MockDemuxerStream>> stream(
+        new StrictMock<MockDemuxerStream>(type));
+    return stream;
+  }
+
+  void CreateAudioStream() {
+    audio_stream_ = CreateStream(DemuxerStream::AUDIO);
+    EXPECT_CALL(demuxer_, GetStream(DemuxerStream::AUDIO))
+        .WillRepeatedly(Return(audio_stream_.get()));
+  }
+
+  void CreateVideoStream(bool is_encrypted = false) {
+    video_stream_ = CreateStream(DemuxerStream::VIDEO);
+    video_stream_->set_video_decoder_config(
+        is_encrypted ? TestVideoConfig::NormalEncrypted()
+                     : TestVideoConfig::Normal());
+    EXPECT_CALL(demuxer_, GetStream(DemuxerStream::VIDEO))
+        .WillRepeatedly(Return(video_stream_.get()));
+  }
+
+  void InitializeAndExpect(PipelineStatus status) {
+    DVLOG(1) << __FUNCTION__ << ": " << status;
+    EXPECT_CALL(*this, OnInitialized(status));
+    mojo_renderer_->Initialize(
+        &demuxer_, &renderer_client_,
+        base::Bind(&MojoRendererTest::OnInitialized, base::Unretained(this)));
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void Initialize() {
+    CreateAudioStream();
+    EXPECT_CALL(*mock_renderer_, Initialize(_, _, _))
+        .WillOnce(DoAll(SaveArg<1>(&remote_renderer_client_),
+                        RunCallback<2>(PIPELINE_OK)));
+    InitializeAndExpect(PIPELINE_OK);
+  }
+
+  void Flush() {
+    DVLOG(1) << __FUNCTION__;
+    // Flush callback should always be fired.
+    EXPECT_CALL(*this, OnFlushed());
+    mojo_renderer_->Flush(
+        base::Bind(&MojoRendererTest::OnFlushed, base::Unretained(this)));
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void SetCdmAndExpect(bool success) {
+    DVLOG(1) << __FUNCTION__;
+    // Set CDM callback should always be fired.
+    EXPECT_CALL(*this, OnCdmAttached(success));
+    mojo_renderer_->SetCdm(
+        &cdm_context_,
+        base::Bind(&MojoRendererTest::OnCdmAttached, base::Unretained(this)));
+    base::RunLoop().RunUntilIdle();
+  }
+
+  // Simulates a connection error at the client side by killing the service.
+  // Note that |mock_renderer_| will also be destroyed, do NOT expect anything
+  // on it. Otherwise the test will crash.
+  void ConnectionError() {
+    DVLOG(1) << __FUNCTION__;
+    delete mojo_renderer_service_;
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void OnCdmCreated(mojom::CdmPromiseResultPtr result,
+                    int cdm_id,
+                    mojom::DecryptorPtr decryptor) {
+    EXPECT_TRUE(result->success);
+    EXPECT_NE(CdmContext::kInvalidCdmId, cdm_id);
+    cdm_context_.set_cdm_id(cdm_id);
+  }
+
+  void CreateCdm() {
+    new MojoCdmService(mojo_cdm_service_context_.GetWeakPtr(), &cdm_factory_,
+                       mojo::GetProxy(&remote_cdm_));
+    remote_cdm_->Initialize(
+        kClearKeyKeySystem, "https://www.test.com",
+        mojom::CdmConfig::From(CdmConfig()),
+        base::Bind(&MojoRendererTest::OnCdmCreated, base::Unretained(this)));
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void StartPlayingFrom(base::TimeDelta start_time) {
+    EXPECT_CALL(*mock_renderer_, StartPlayingFrom(start_time));
+    mojo_renderer_->StartPlayingFrom(start_time);
+    EXPECT_EQ(start_time, mojo_renderer_->GetMediaTime());
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void Play() {
+    StartPlayingFrom(base::TimeDelta::FromMilliseconds(kStartPlayingTimeInMs));
+  }
+
+  // Fixture members.
+  base::MessageLoop message_loop_;
+
+  // The MojoRendererImpl that we are testing.
+  std::unique_ptr<MojoRendererImpl> mojo_renderer_;
+
+  // Client side mocks and helpers.
+  StrictMock<MockRendererClient> renderer_client_;
+  StrictMock<MockCdmContext> cdm_context_;
+  mojom::ContentDecryptionModulePtr remote_cdm_;
+
+  // Client side mock demuxer and demuxer streams.
+  StrictMock<MockDemuxer> demuxer_;
+  std::unique_ptr<StrictMock<MockDemuxerStream>> audio_stream_;
+  std::unique_ptr<StrictMock<MockDemuxerStream>> video_stream_;
+
+  // Service side mocks and helpers.
+  StrictMock<MockRenderer>* mock_renderer_;
+  MojoCdmServiceContext mojo_cdm_service_context_;
+  RendererClient* remote_renderer_client_;
+  DefaultCdmFactory cdm_factory_;
+
+  // Owned by the connection. But we can delete it manually to trigger a
+  // connection error at the client side. See ConnectionError();
+  MojoRendererService* mojo_renderer_service_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MojoRendererTest);
+};
+
+TEST_F(MojoRendererTest, Initialize_Success) {
+  Initialize();
+}
+
+TEST_F(MojoRendererTest, Initialize_Failure) {
+  CreateAudioStream();
+  // Mojo Renderer only expects a boolean result, which will be translated
+  // to PIPELINE_OK or PIPELINE_ERROR_INITIALIZATION_FAILED.
+  EXPECT_CALL(*mock_renderer_, Initialize(_, _, _))
+      .WillOnce(RunCallback<2>(PIPELINE_ERROR_ABORT));
+  InitializeAndExpect(PIPELINE_ERROR_INITIALIZATION_FAILED);
+}
+
+TEST_F(MojoRendererTest, Initialize_BeforeConnectionError) {
+  CreateAudioStream();
+  EXPECT_CALL(*mock_renderer_, Initialize(_, _, _))
+      .WillOnce(InvokeWithoutArgs(this, &MojoRendererTest::ConnectionError));
+  InitializeAndExpect(PIPELINE_ERROR_INITIALIZATION_FAILED);
+}
+
+TEST_F(MojoRendererTest, Initialize_AfterConnectionError) {
+  ConnectionError();
+  CreateAudioStream();
+  InitializeAndExpect(PIPELINE_ERROR_INITIALIZATION_FAILED);
+}
+
+TEST_F(MojoRendererTest, Flush_Success) {
+  Initialize();
+
+  EXPECT_CALL(*mock_renderer_, Flush(_)).WillOnce(RunClosure<0>());
+  Flush();
+}
+
+TEST_F(MojoRendererTest, Flush_ConnectionError) {
+  Initialize();
+
+  // Upon connection error, OnError() should be called once and only once.
+  EXPECT_CALL(renderer_client_, OnError(PIPELINE_ERROR_DECODE)).Times(1);
+  EXPECT_CALL(*mock_renderer_, Flush(_))
+      .WillOnce(InvokeWithoutArgs(this, &MojoRendererTest::ConnectionError));
+  Flush();
+}
+
+TEST_F(MojoRendererTest, Flush_AfterConnectionError) {
+  Initialize();
+
+  // Upon connection error, OnError() should be called once and only once.
+  EXPECT_CALL(renderer_client_, OnError(PIPELINE_ERROR_DECODE)).Times(1);
+  ConnectionError();
+
+  Flush();
+}
+
+TEST_F(MojoRendererTest, SetCdm_Success) {
+  Initialize();
+  CreateCdm();
+  EXPECT_CALL(*mock_renderer_, SetCdm(_, _)).WillOnce(RunCallback<1>(true));
+  SetCdmAndExpect(true);
+}
+
+TEST_F(MojoRendererTest, SetCdm_Failure) {
+  Initialize();
+  CreateCdm();
+  EXPECT_CALL(*mock_renderer_, SetCdm(_, _)).WillOnce(RunCallback<1>(false));
+  SetCdmAndExpect(false);
+}
+
+TEST_F(MojoRendererTest, SetCdm_InvalidCdmId) {
+  Initialize();
+  SetCdmAndExpect(false);
+}
+
+TEST_F(MojoRendererTest, SetCdm_NonExistCdmId) {
+  Initialize();
+  cdm_context_.set_cdm_id(1);
+  SetCdmAndExpect(false);
+}
+
+TEST_F(MojoRendererTest, SetCdm_BeforeInitialize) {
+  CreateCdm();
+  EXPECT_CALL(*mock_renderer_, SetCdm(_, _)).WillOnce(RunCallback<1>(true));
+  SetCdmAndExpect(true);
+}
+
+TEST_F(MojoRendererTest, SetCdm_AfterInitializeAndConnectionError) {
+  CreateCdm();
+  Initialize();
+  EXPECT_CALL(renderer_client_, OnError(PIPELINE_ERROR_DECODE)).Times(1);
+  ConnectionError();
+  SetCdmAndExpect(false);
+}
+
+TEST_F(MojoRendererTest, SetCdm_AfterConnectionErrorAndBeforeInitialize) {
+  CreateCdm();
+  // Initialize() is not called so RendererClient::OnError() is not expected.
+  ConnectionError();
+  SetCdmAndExpect(false);
+  InitializeAndExpect(PIPELINE_ERROR_INITIALIZATION_FAILED);
+}
+
+TEST_F(MojoRendererTest, SetCdm_BeforeInitializeAndConnectionError) {
+  CreateCdm();
+  EXPECT_CALL(*mock_renderer_, SetCdm(_, _)).WillOnce(RunCallback<1>(true));
+  SetCdmAndExpect(true);
+  // Initialize() is not called so RendererClient::OnError() is not expected.
+  ConnectionError();
+  CreateAudioStream();
+  InitializeAndExpect(PIPELINE_ERROR_INITIALIZATION_FAILED);
+}
+
+TEST_F(MojoRendererTest, StartPlayingFrom) {
+  Initialize();
+  Play();
+}
+
+TEST_F(MojoRendererTest, GetMediaTime) {
+  Initialize();
+  EXPECT_EQ(base::TimeDelta(), mojo_renderer_->GetMediaTime());
+
+  Play();
+
+  // Time is updated periodically with a short delay.
+  const base::TimeDelta kUpdatedTime = base::TimeDelta::FromMilliseconds(500);
+  EXPECT_CALL(*mock_renderer_, GetMediaTime())
+      .WillRepeatedly(Return(kUpdatedTime));
+  base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(kUpdatedTime, mojo_renderer_->GetMediaTime());
+}
+
+TEST_F(MojoRendererTest, OnEnded) {
+  Initialize();
+  Play();
+
+  EXPECT_CALL(renderer_client_, OnEnded()).Times(1);
+  remote_renderer_client_->OnEnded();
+  base::RunLoop().RunUntilIdle();
+}
+
+// TODO(xhwang): Add tests for all RendererClient methods.
+
+TEST_F(MojoRendererTest, Destroy_PendingInitialize) {
+  CreateAudioStream();
+  EXPECT_CALL(*mock_renderer_, Initialize(_, _, _))
+      .WillRepeatedly(RunCallback<2>(PIPELINE_ERROR_ABORT));
+  EXPECT_CALL(*this, OnInitialized(PIPELINE_ERROR_INITIALIZATION_FAILED));
+  mojo_renderer_->Initialize(
+      &demuxer_, &renderer_client_,
+      base::Bind(&MojoRendererTest::OnInitialized, base::Unretained(this)));
+  Destroy();
+}
+
+TEST_F(MojoRendererTest, Destroy_PendingFlush) {
+  EXPECT_CALL(*mock_renderer_, SetCdm(_, _))
+      .WillRepeatedly(RunCallback<1>(true));
+  EXPECT_CALL(*this, OnCdmAttached(false));
+  mojo_renderer_->SetCdm(
+      &cdm_context_,
+      base::Bind(&MojoRendererTest::OnCdmAttached, base::Unretained(this)));
+  Destroy();
+}
+
+TEST_F(MojoRendererTest, Destroy_PendingSetCdm) {
+  Initialize();
+  EXPECT_CALL(*mock_renderer_, Flush(_)).WillRepeatedly(RunClosure<0>());
+  EXPECT_CALL(*this, OnFlushed());
+  mojo_renderer_->Flush(
+      base::Bind(&MojoRendererTest::OnFlushed, base::Unretained(this)));
+  Destroy();
+}
+
+// TODO(xhwang): Add more tests on OnError. For example, ErrorDuringFlush,
+// ErrorAfterFlush etc.
+
+}  // namespace media
diff --git a/media/mojo/services/mojo_cdm_service.h b/media/mojo/services/mojo_cdm_service.h
index 04a6f4d..2e1aa782 100644
--- a/media/mojo/services/mojo_cdm_service.h
+++ b/media/mojo/services/mojo_cdm_service.h
@@ -10,6 +10,7 @@
 #include <memory>
 
 #include "base/callback.h"
+#include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
@@ -27,7 +28,8 @@
 
 // A mojom::ContentDecryptionModule implementation backed by a
 // media::MediaKeys.
-class MojoCdmService : public mojom::ContentDecryptionModule {
+class MEDIA_MOJO_EXPORT MojoCdmService
+    : NON_EXPORTED_BASE(public mojom::ContentDecryptionModule) {
  public:
   // Get the CDM associated with |cdm_id|, which is unique per process.
   // Can be called on any thread. The returned CDM is not guaranteed to be
@@ -37,7 +39,7 @@
   // render frame the caller is associated with. In the future, we should move
   // all out-of-process media players into the MojoMediaApplicaiton so that we
   // can use MojoCdmServiceContext (per render frame) to get the CDM.
-  static scoped_refptr<MediaKeys> MEDIA_MOJO_EXPORT LegacyGetCdm(int cdm_id);
+  static scoped_refptr<MediaKeys> LegacyGetCdm(int cdm_id);
 
   // Constructs a MojoCdmService and strongly binds it to the |request|.
   MojoCdmService(
diff --git a/media/mojo/services/mojo_cdm_service_context.h b/media/mojo/services/mojo_cdm_service_context.h
index 518d8a5..5938e9fc 100644
--- a/media/mojo/services/mojo_cdm_service_context.h
+++ b/media/mojo/services/mojo_cdm_service_context.h
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "media/mojo/services/media_mojo_export.h"
 
 namespace media {
 
@@ -19,7 +20,7 @@
 class MojoCdmService;
 
 // A class that creates, owns and manages all MojoCdmService instances.
-class MojoCdmServiceContext {
+class MEDIA_MOJO_EXPORT MojoCdmServiceContext {
  public:
   MojoCdmServiceContext();
   ~MojoCdmServiceContext();
diff --git a/media/mojo/services/mojo_renderer_service.h b/media/mojo/services/mojo_renderer_service.h
index ed63e58..58b1ce7 100644
--- a/media/mojo/services/mojo_renderer_service.h
+++ b/media/mojo/services/mojo_renderer_service.h
@@ -17,6 +17,7 @@
 #include "media/base/pipeline_status.h"
 #include "media/base/renderer_client.h"
 #include "media/mojo/interfaces/renderer.mojom.h"
+#include "media/mojo/services/media_mojo_export.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 
 namespace media {
@@ -28,7 +29,8 @@
 
 // A mojom::Renderer implementation that use a media::Renderer to render
 // media streams.
-class MojoRendererService : public mojom::Renderer, public RendererClient {
+class MEDIA_MOJO_EXPORT MojoRendererService : public mojom::Renderer,
+                                              public RendererClient {
  public:
   // |mojo_cdm_service_context| can be used to find the CDM to support
   // encrypted media. If null, encrypted media is not supported.
diff --git a/media/renderers/renderer_impl_unittest.cc b/media/renderers/renderer_impl_unittest.cc
index 4c901f7..9790055 100644
--- a/media/renderers/renderer_impl_unittest.cc
+++ b/media/renderers/renderer_impl_unittest.cc
@@ -68,8 +68,8 @@
         video_renderer_client_(nullptr),
         audio_renderer_client_(nullptr),
         initialization_status_(PIPELINE_OK) {
-    // SetDemuxerExpectations() adds overriding expectations for expected
-    // non-NULL streams.
+    // CreateAudioStream() and CreateVideoStream() overrides expectations for
+    // expected non-NULL streams.
     DemuxerStream* null_pointer = NULL;
     EXPECT_CALL(*demuxer_, GetStream(_))
         .WillRepeatedly(Return(null_pointer));
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index bac56e3..c05e163 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -42,8 +42,9 @@
 crbug.com/504613 crbug.com/524248 virtual/spv2/paint/images/image-backgrounds-not-antialiased.html [ Skip ]
 crbug.com/502531 fast/borders/border-antialiasing.html [ Failure ]
 
-crbug.com/600008 [ Win ] fast/repaint/window-resize-centered-inline-under-fixed-pos.html [ Failure Pass ]
-crbug.com/600008 [ Win ] fast/repaint/window-resize-vertical-writing-mode.html [ Failure Pass ]
+# TODO(wangxianzhu): Restore these after rebaseline.
+# crbug.com/600008 [ Win ] fast/repaint/window-resize-centered-inline-under-fixed-pos.html [ Failure Pass ]
+# crbug.com/600008 [ Win ] fast/repaint/window-resize-vertical-writing-mode.html [ Failure Pass ]
 crbug.com/600008 [ Mac Win ] fast/repaint/window-resize-background-image-non-fixed.html [ Failure Pass ] 
 
 crbug.com/538697 [ Win7 Debug ] virtual/threaded/printing/webgl-oversized-printing.html [ Crash ]
@@ -337,7 +338,8 @@
 crbug.com/552433 [ Linux Mac Win10 Win7 ] svg/dom/length-list-parser.html [ Failure Pass ]
 crbug.com/552433 [ Linux Mac Win10 Win7 ] svg/transforms/text-with-pattern-with-svg-transform.svg [ Failure Pass ]
 crbug.com/552433 [ Linux Mac Win10 Win7 ] svg/hixie/perf/006.xml [ Failure Pass ]
-crbug.com/605024 [ Mac ] svg/transforms/animated-path-inside-transformed-html.xhtml [ Failure ]
+# TODO(wangxianzhu): Restore this after rebaseline.
+# crbug.com/605024 [ Mac ] svg/transforms/animated-path-inside-transformed-html.xhtml [ Failure ]
 
 # This directly has manual tests that don't have to run with run-webkit-tests
 crbug.com/359838 http/tests/ManualTests/ [ Skip ]
@@ -1218,7 +1220,8 @@
 crbug.com/619103 fast/table/border-collapsing/cached-change-tbody-border-color.html [ Failure ]
 crbug.com/619103 paint/invalidation/invalidate-after-composited-scroll-of-window.html [ Failure ]
 crbug.com/619103 paint/invalidation/animated-gif-background-offscreen.html [ Pass Failure ]
-crbug.com/619103 [ Win ] svg/text/text-viewbox-rescale.html [ Pass Failure ]
+# TODO(wangxianzhu): Restore this after rebaseline.
+# crbug.com/619103 [ Win ] svg/text/text-viewbox-rescale.html [ Pass Failure ]
 
 crbug.com/443596 media/sources-fallback-codecs.html [ Pass Failure ]
 
@@ -1380,6 +1383,128 @@
 
 crbug.com/594595 [ Linux ] http/tests/security/mixedContent/websocket/insecure-websocket-in-secure-page-worker-allowed.html [ Timeout Pass ]
 
+crbug.com/619630 compositing/squashing/iframe-inside-squashed-layer.html [ NeedsRebaseline ]
+crbug.com/619630 compositing/squashing/remove-squashed-layer-plus-move.html [ NeedsRebaseline ]
+crbug.com/619630 css3/flexbox/repaint-on-layout.html [ NeedsRebaseline ]
+crbug.com/619630 css3/flexbox/repaint.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/block-no-inflow-children.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/box-inline-resize.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/bugzilla-3509.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/bugzilla-5699.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/bugzilla-6278.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/bugzilla-7235.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/change-text-content-and-background-color.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/clip-with-layout-delta.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/clipped-relative.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/control-clip.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/crbug-371640-4.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/crbug-371640.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/delete-into-nested-block.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/gradients-em-stops-repaint.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/inline-block-resize.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/inline-focus.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/inline-outline-repaint.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/inline-reflow.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/inline-relative-positioned.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/invisible-objects.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/layout-state-relative.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/layout-state-scrolloffset.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/layout-state-scrolloffset2.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/layout-state-scrolloffset3.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/layoutstate-invalid-invalidation-inline-relative-positioned.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/line-flow-with-floats-1.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/line-flow-with-floats-10.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/line-flow-with-floats-2.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/line-flow-with-floats-3.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/line-flow-with-floats-4.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/line-flow-with-floats-5.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/line-flow-with-floats-6.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/line-flow-with-floats-7.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/line-flow-with-floats-8.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/line-flow-with-floats-9.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/line-in-scrolled-clipped-block.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/line-overflow.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/lines-with-layout-delta.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/list-marker-2.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/make-children-non-inline.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/multi-layout-one-frame.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/multicol-repaint.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/multicol-with-text.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/outline-continuations.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/overflow-delete-line.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/overflow-scroll-body-appear.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/overflow-scroll-delete.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/paint-invalidation-with-reparent-across-frame-boundaries.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/positioned-document-element.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/positioned-list-offset-change-repaint.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/quotes.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/reflection-repaint-test.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/relative-inline-positioned-movement-repaint.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/remove-block-after-layout.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/remove-inline-after-layout.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/remove-inline-block-descendant-of-flex.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/remove-inline-layer-after-layout.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/resize-scrollable-iframe.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/selection-after-delete.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/selection-after-remove.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/selection-clear.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/shift-relative-positioned-container-with-image-removal.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/stacked-diacritics.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/subtree-root-skipped.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/table-collapsed-border.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/text-append-dirty-lines.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/text-in-relative-positioned-inline.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/text-match-document-change.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/transform-layout-repaint.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/vertical-align-length1.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/vertical-align-length2.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/vertical-align1.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/vertical-align2.html [ NeedsRebaseline ]
+crbug.com/619630 fast/repaint/window-resize-centered-inline-under-fixed-pos.html [ NeedsManualRebaseline ]
+crbug.com/619630 fast/repaint/window-resize-vertical-writing-mode.html [ NeedsManualRebaseline ]
+crbug.com/619630 fast/table/border-collapsing/cached-change-cell-sl-border-color.html [ NeedsRebaseline ]
+crbug.com/619630 fast/table/resize-table-repaint-percent-size-cell.html [ NeedsRebaseline ]
+crbug.com/619630 fast/table/resize-table-repaint-vertical-align-cell.html [ NeedsRebaseline ]
+crbug.com/619630 fast/table/resize-table-row-repaint.html [ NeedsRebaseline ]
+crbug.com/619630 svg/as-image/svg-image-change-content-size.xhtml [ NeedsRebaseline ]
+crbug.com/619630 svg/as-object/embedded-svg-size-changes-no-layout-triggers.html [ NeedsRebaseline ]
+crbug.com/619630 svg/as-object/nested-embedded-svg-size-changes-no-layout-triggers-1.html [ NeedsRebaseline ]
+crbug.com/619630 svg/as-object/nested-embedded-svg-size-changes-no-layout-triggers-2.html [ NeedsRebaseline ]
+crbug.com/619630 svg/carto.net/tabgroup.svg [ NeedsRebaseline ]
+crbug.com/619630 svg/custom/absolute-sized-content-with-resources.xhtml [ NeedsRebaseline ]
+crbug.com/619630 svg/custom/js-late-gradient-creation.svg [ NeedsRebaseline ]
+crbug.com/619630 svg/custom/js-late-pattern-creation.svg [ NeedsRebaseline ]
+crbug.com/619630 svg/custom/object-sizing-no-width-height-change-content-box-size.xhtml [ NeedsRebaseline ]
+crbug.com/619630 svg/custom/relative-sized-content-with-resources.xhtml [ NeedsRebaseline ]
+crbug.com/619630 svg/custom/relative-sized-inner-svg.xhtml [ NeedsRebaseline ]
+crbug.com/619630 svg/custom/relative-sized-shadow-tree-content-with-symbol.xhtml [ NeedsRebaseline ]
+crbug.com/619630 svg/custom/relative-sized-use-on-symbol.xhtml [ NeedsRebaseline ]
+crbug.com/619630 svg/custom/relative-sized-use-without-attributes-on-symbol.xhtml [ NeedsRebaseline ]
+crbug.com/619630 svg/custom/scrolling-embedded-svg-file-image-repaint-problem.html [ NeedsRebaseline ]
+crbug.com/619630 svg/custom/text-dom-removal.svg [ NeedsRebaseline ]
+crbug.com/619630 svg/custom/text-repaint-including-stroke.svg [ NeedsRebaseline ]
+crbug.com/619630 svg/custom/text-xy-updates-SVGList.xhtml [ NeedsRebaseline ]
+crbug.com/619630 svg/custom/use-clipped-hit.svg [ NeedsRebaseline ]
+crbug.com/619630 svg/custom/use-detach.svg [ NeedsRebaseline ]
+crbug.com/619630 svg/custom/use-setAttribute-crash.svg [ NeedsRebaseline ]
+crbug.com/619630 svg/repaint/add-outline-property-on-root.html [ NeedsRebaseline ]
+crbug.com/619630 svg/repaint/inner-svg-change-viewPort-relative.svg [ NeedsRebaseline ]
+crbug.com/619630 svg/repaint/modify-transferred-listitem-different-attr.html [ NeedsRebaseline ]
+crbug.com/619630 svg/repaint/outline-offset-text.html [ NeedsRebaseline ]
+crbug.com/619630 svg/repaint/remove-outline-property-on-root.html [ NeedsRebaseline ]
+crbug.com/619630 svg/repaint/repaint-non-scaling-stroke-text-decoration.html [ NeedsRebaseline ]
+crbug.com/619630 svg/repaint/repaint-non-scaling-stroke-text.html [ NeedsRebaseline ]
+crbug.com/619630 svg/repaint/text-mask-update.svg [ NeedsRebaseline ]
+crbug.com/619630 svg/repaint/transform-text-element.html [ NeedsRebaseline ]
+crbug.com/619630 svg/text/append-text-node-to-tspan.html [ NeedsRebaseline ]
+crbug.com/619630 svg/text/modify-text-node-in-tspan.html [ NeedsRebaseline ]
+crbug.com/619630 svg/text/remove-text-node-from-tspan.html [ NeedsRebaseline ]
+crbug.com/619630 svg/text/remove-tspan-from-text.html [ NeedsRebaseline ]
+crbug.com/619630 svg/text/text-rescale.html [ NeedsRebaseline ]
+crbug.com/619630 svg/text/text-viewbox-rescale.html [ NeedsManualRebaseline ]
+crbug.com/619630 svg/text/tspan-dynamic-positioning.svg [ NeedsRebaseline ]
+crbug.com/619630 svg/transforms/animated-path-inside-transformed-html.xhtml [ NeedsManualRebaseline ]
+
 crbug.com/453002 [ Win ] fast/text/international/text-combine-image-test.html [ Failure Pass ]
 crbug.com/453002 [ Win ] fast/text/international/vertical-text-glyph-test.html [ Failure Pass ]
 crbug.com/453002 [ Win ] fast/text/justify-ideograph-vertical.html [ Failure Pass ]
diff --git a/third_party/WebKit/Source/core/layout/line/InlineBox.cpp b/third_party/WebKit/Source/core/layout/line/InlineBox.cpp
index f9e3fe6..87dc822 100644
--- a/third_party/WebKit/Source/core/layout/line/InlineBox.cpp
+++ b/third_party/WebKit/Source/core/layout/line/InlineBox.cpp
@@ -63,10 +63,6 @@
 
 void InlineBox::destroy()
 {
-    // We do not need to issue invalidations if the page is being destroyed
-    // since these objects will never be repainted.
-    if (!m_lineLayoutItem.documentBeingDestroyed())
-        m_lineLayoutItem.invalidateDisplayItemClient(*this);
     delete this;
 }
 
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 6f72e36..020a698 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -80129,6 +80129,7 @@
   <int value="546710806" label="disable-easy-signin"/>
   <int value="550378029" label="reset-app-list-install-state"/>
   <int value="567368307" label="enable-experimental-canvas-features"/>
+  <int value="581118445" label="enable-eol-notification"/>
   <int value="592050831" label="disable-slimming-paint"/>
   <int value="593707592" label="disable-network-portal-notification"/>
   <int value="598827460" label="enable-roboto-font-ui"/>
diff --git a/ui/file_manager/file_manager/foreground/elements/files_metadata_box.html b/ui/file_manager/file_manager/foreground/elements/files_metadata_box.html
index f93d7e5b..311eb7d 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_metadata_box.html
+++ b/ui/file_manager/file_manager/foreground/elements/files_metadata_box.html
@@ -11,17 +11,30 @@
       display: block;
     }
     #box {
-      background: white;
       overflow: auto;
+      width: 200px;
+    }
+    .category {
+      color: gray;
+      font-weight: bold;
+      margin-bottom: 10px;
+    }
+    .type {
+      color: white;
+    }
+    .value {
+      --webkit-margin-start: 100px;
+      color: white;
     }
   </style>
   <template>
     <div id="box">
       <!-- TODO(oka): update appearance to match mocks. -->
-      <div>
-        size: [[size]]<br>
-        modificationTime: [[modificationTime]]
-      </div>
+      <div class="category" i18n-content="METADATA_BOX_GENERAL_INFO"></div>
+      <div class="type" i18n-content="METADATA_BOX_FILE_SIZE"></div>
+      <div class="value">[[size]]</div>
+      <div class="type" i18n-content="METADATA_BOX_MODIFICATION_TIME"></div>
+      <div class="value">[[modificationTime]]</div>
     </div>
   </template>
 </dom-module>
diff --git a/ui/file_manager/file_manager/foreground/elements/files_metadata_box.js b/ui/file_manager/file_manager/foreground/elements/files_metadata_box.js
index 73f131f..e74e7b5 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_metadata_box.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_metadata_box.js
@@ -9,7 +9,7 @@
     size: String,
     modiifcationTime: String,
     /*
-     * TODO(oka): Add the follwoing fields.
+     * TODO(oka): Add the following fields.
      * filePath: String,
      * imageWidth: Number,
      * imageHeight: Number,
diff --git a/ui/file_manager/file_manager/foreground/elements/files_quick_view.css b/ui/file_manager/file_manager/foreground/elements/files_quick_view.css
new file mode 100644
index 0000000..fe2e81c
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/elements/files_quick_view.css
@@ -0,0 +1,90 @@
+/* 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. */
+
+:host {
+  display: block;
+}
+
+paper-dialog {
+  background: black;
+  height: 70%;
+  width: 70%;
+}
+
+.content {
+  bottom: 0;
+  left: 0;
+  margin: auto;
+  max-height: 100%;
+  max-width: 100%;
+  position: absolute;
+  right: 0;
+  top: 0;
+}
+
+#audio-control {
+  bottom: 0;
+  left: 0;
+  margin: 0;
+  position: absolute;
+  width: 100%;
+}
+
+#toolbar {
+  --paper-toolbar-background: rgba(127, 127, 127, 0.5);
+  --paper-toolbar-height: 32px;
+  color: white;
+  margin: 0;
+  z-index: 1;
+}
+
+#buttons {
+  position: absolute;
+  right: 0px;
+}
+
+paper-button {
+  padding: 4px;
+}
+
+#metadata-box {
+  background: rgba(0, 0, 0, 0.5);
+  bottom: 0;
+  margin: 0;
+  position: absolute;
+  right: 0;
+  top: 32px;
+  z-index: 1;
+}
+
+#metadata-box[hidden] {
+  display: none;
+}
+
+/* Large generic thumbnails, used when a file does not have a thumbnail. */
+[generic-thumbnail] {
+  background-image: -webkit-image-set(
+      url(../images/files/ui/filetype_placeholder_generic.png) 1x,
+      url(../images/files/ui/2x/filetype_placeholder_generic.png) 2x);
+  background-position: center 50px;
+  background-repeat: no-repeat;
+}
+
+[generic-thumbnail='audio'] {
+  background-image: -webkit-image-set(
+      url(../images/files/ui/filetype_placeholder_audio.png) 1x,
+      url(../images/files/ui/2x/filetype_placeholder_audio.png) 2x);
+}
+
+[generic-thumbnail='image'] {
+  background-image: -webkit-image-set(
+      url(../images/files/ui/filetype_placeholder_image.png) 1x,
+      url(../images/files/ui/2x/filetype_placeholder_image.png) 2x);
+}
+
+[generic-thumbnail='video'] {
+  background-image: -webkit-image-set(
+      url(../images/files/ui/filetype_placeholder_video.png) 1x,
+      url(../images/files/ui/2x/filetype_placeholder_video.png) 2x);
+}
diff --git a/ui/file_manager/file_manager/foreground/elements/files_quick_view.html b/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
index 2e86630..dfaf825 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
+++ b/ui/file_manager/file_manager/foreground/elements/files_quick_view.html
@@ -12,46 +12,9 @@
 <link rel="import" href="icons.html">
 
 <dom-module id="files-quick-view">
-  <style>
-    :host {
-      display: block;
-    }
-    paper-dialog {
-      background: black;
-      height: 100%;
-      margin: 10px;
-      width: 100%;
-    }
-    img, video, audio {
-      bottom: 0;
-      left: 0;
-      margin: auto;
-      max-height: 100%;
-      max-width: 100%;
-      position: absolute;
-      right: 0;
-      top: 0;
-    }
-    #toolbar {
-      --paper-toolbar-background: rgba(0, 0, 0, 0.5);
-      color: white;
-      margin: 0;
-      z-index: 1;
-    }
-    #buttons {
-      position: absolute;
-      right: 0px;
-    }
-    #metadata-box {
-      position: absolute;
-      right: 0px;
-    }
-    #metadata-box[hidden] {
-      display: none;
-    }
-  </style>
+  <link rel="import" type="css" href="files_quick_view.css">
   <template>
-    <paper-dialog id="dialog">
+    <paper-dialog id="dialog" modal>
       <paper-toolbar id="toolbar">
         <div>[[filePath]]</div>
         <div id="buttons">
@@ -66,21 +29,34 @@
            </paper-button>
         </div>
       </paper-toolbar>
-      <template class="content" is="dom-if" if="[[image]]">
-        <div>
-          <img src="[[image]]"/>
-        </div>
-      </template>
-      <template class="content" is="dom-if" if="[[video]]">
-        <div>
-          <video controls autoplay src="[[video]]" poster="[[videoPoster]]"/>
-        </div>
-      </template>
-      <template class="content" is="dom-if" if="[[audio]]">
-        <div>
-          <audio autoplay controls src="[[audio]]"/>
-        </div>
-      </template>
+      <!-- TOOD(oka): Show default icon if image, video or audio was broken -->
+      <div>
+        <template is="dom-if" if="[[image]]">
+          <div generic-thumbnail="image">
+            <img class="content" src="[[image]]">
+          </div>
+        </template>
+        <template is="dom-if" if="[[video]]">
+          <div generic-thumbnail="video">
+            <!-- TODO(oka): Stop to autoplay if video is in Drive. -->
+            <video class="content" controls autoplay src="[[video]]" poster="[[videoPoster]]"></video>
+          </div>
+        </template>
+        <template is="dom-if" if="[[audio]]">
+          <template is="dom-if" if="[[contentThumbnailUrl]]">
+            <img class="content" src="[[contentThumbnailUrl]]">
+          </template>
+          <template is="dom-if" if="[[!contentThumbnailUrl]]">
+            <div class="content" generic-thumbnail="audio"></div>
+          </template>
+          <!-- TODO(oka): Stop to autoplay if video is in Drive. -->
+          <audio id="audio-control" autoplay controls src="[[audio]]"></audio>
+        </template>
+        <!-- TODO(oka): Support folder icon -->
+        <template is="dom-if" if="[[unsupported]]">
+          <div class="content" generic-thumbnail><div>
+        </template>
+      </div>
       <files-metadata-box id="metadata-box" hidden="{{!metadataBoxActive}}"></file-metadata-box>
     </paper-dialog>
   </template>
diff --git a/ui/file_manager/file_manager/foreground/elements/files_quick_view.js b/ui/file_manager/file_manager/foreground/elements/files_quick_view.js
index 675c86d7..30c11e3 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_quick_view.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_quick_view.js
@@ -11,6 +11,8 @@
     video: String,
     videoPoster: String,
     audio: String,
+    contentThumbnailUrl: String,
+    unsupported: Boolean,
 
     // metadata-box-active-changed event is fired on attribute change.
     metadataBoxActive: {
@@ -21,28 +23,16 @@
   },
 
   listeners: {
-    'iron-overlay-closed': 'clear_',
+    'iron-overlay-closed': 'clear',
   },
 
-  setImageURL: function(url) {
-    this.clear_();
-    this.image = url;
-  },
-
-  setVideoURL: function(url) {
-    this.clear_();
-    this.video = url;
-  },
-
-  setAudioURL: function(url) {
-    this.clear_();
-    this.audio = url;
-  },
-
-  clear_: function() {
+  clear: function() {
+    this.filePath = '';
     this.image = '';
     this.video = '';
     this.audio = '';
+    this.contentThumbnailUrl = '';
+    this.unsupported = false;
   },
 
   // Open the dialog.
diff --git a/ui/file_manager/file_manager/foreground/js/quick_view_controller.js b/ui/file_manager/file_manager/foreground/js/quick_view_controller.js
index f2031bc..5c2fbb45 100644
--- a/ui/file_manager/file_manager/foreground/js/quick_view_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/quick_view_controller.js
@@ -168,8 +168,7 @@
   var entry =
       (/** @type {!FileEntry} */ (this.quickViewModel_.getSelectedEntry()));
   assert(entry);
-  this.quickView_.filePath = entry.name;
-  return this.metadataModel_.get([entry], ['contentThumbnailUrl'])
+  return this.metadataModel_.get([entry], ['thumbnailUrl', 'externalFileUrl'])
       .then(this.onMetadataLoaded_.bind(this, entry));
 };
 
@@ -181,15 +180,17 @@
  * @private
  */
 QuickViewController.prototype.onMetadataLoaded_ = function(entry, items) {
+  this.quickView_.clear();
+  this.quickView_.filePath = entry.name;
   var item = items[0];
   var type = FileType.getType(entry);
-  var thumbnailUrl = item.thumbnailUrl || item.croppedThumbnailUrl;
+  var thumbnailUrl = item.thumbnailUrl;
   if (type.type === 'image') {
     if (item.externalFileUrl) {
       // TODO(oka): Support Drive.
     } else {
       var url = thumbnailUrl || entry.toURL();
-      this.quickView_.setImageURL(url);
+      this.quickView_.image = url;
     }
   } else if (type.type === 'video') {
     // TODO(oka): Set thumbnail.
@@ -197,17 +198,23 @@
       // TODO(oka): Support Drive.
     } else {
       var url = entry.toURL();
-      this.quickView_.setVideoURL(url);
+      this.quickView_.video = url;
     }
-    this.quickView_.setVideoURL(entry.toURL());
   } else if (type.type === 'audio') {
-    this.quickView_.setAudioURL(entry.toURL());
-    // TODO(oka): Set thumbnail.
     if (item.externalFileUrl) {
       // TODO(oka): Support Drive.
     } else {
-      this.quickView_.setAudioURL(url);
+      var url = entry.toURL();
+      this.quickView_.audio = url;
+      this.metadataModel_.get([entry], ['contentThumbnailUrl'])
+          .then(function(entry, items) {
+            var item = items[0];
+            if (item.contentThumbnailUrl)
+              this.quickView_.contentThumbnailUrl =
+                  item.contentThumbnailUrl;
+          }.bind(this, entry));
     }
-    this.quickView_.setAudioURL(entry.toURL());
+  } else {
+    this.quickView_.unsupported = true;
   }
 };
diff --git a/ui/file_manager/file_manager_resources.grd b/ui/file_manager/file_manager_resources.grd
index 455352c1..753e2349 100644
--- a/ui/file_manager/file_manager_resources.grd
+++ b/ui/file_manager/file_manager_resources.grd
@@ -56,6 +56,7 @@
       <include name="IDR_FILE_MANAGER_ELEMENTS_FILES_ICON_BUTTON_JS" file="file_manager/foreground/elements/files_icon_button.js" type="BINDATA" />
       <include name="IDR_FILE_MANAGER_ELEMENTS_FILES_METADATA_BOX_HTML" file="file_manager/foreground/elements/files_metadata_box.html" type="BINDATA" />
       <include name="IDR_FILE_MANAGER_ELEMENTS_FILES_METADATA_BOX_JS" file="file_manager/foreground/elements/files_metadata_box.js" type="BINDATA" />
+      <include name="IDR_FILE_MANAGER_ELEMENTS_FILES_QUICK_PREVIEW_CSS" file="file_manager/foreground/elements/files_quick_view.css" flattenhtml="true" type="BINDATA" />
       <include name="IDR_FILE_MANAGER_ELEMENTS_FILES_QUICK_PREVIEW_HTML" file="file_manager/foreground/elements/files_quick_view.html" type="BINDATA" />
       <include name="IDR_FILE_MANAGER_ELEMENTS_FILES_QUICK_PREVIEW_JS" file="file_manager/foreground/elements/files_quick_view.js" type="BINDATA" />
       <include name="IDR_FILE_MANAGER_ELEMENTS_FILES_RIPPLE_HTML" file="file_manager/foreground/elements/files_ripple.html" type="BINDATA" />