diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentApp.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentApp.java
index 84e3bfa..2fad899c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentApp.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentApp.java
@@ -6,6 +6,7 @@
 
 import android.os.Handler;
 
+import org.chromium.content_public.browser.WebContents;
 import org.chromium.payments.mojom.PaymentMethodData;
 
 import java.util.ArrayList;
@@ -24,6 +25,7 @@
  * @see https://w3c.github.io/webpayments-payment-apps-api/
  */
 public class ServiceWorkerPaymentApp implements PaymentApp {
+    private final WebContents mWebContents;
     private final ServiceWorkerPaymentAppBridge.Manifest mManifest;
     private final Set<String> mMethodNames;
 
@@ -32,9 +34,12 @@
      *
      * @see https://w3c.github.io/webpayments-payment-apps-api/#payment-app-manifest
      *
+     * @param webContents The web contents where PaymentRequest was invoked.
      * @param manifest    A manifest that describes this payment app.
      */
-    public ServiceWorkerPaymentApp(ServiceWorkerPaymentAppBridge.Manifest manifest) {
+    public ServiceWorkerPaymentApp(
+            WebContents webContents, ServiceWorkerPaymentAppBridge.Manifest manifest) {
+        mWebContents = webContents;
         mManifest = manifest;
 
         mMethodNames = new HashSet<>();
@@ -51,7 +56,8 @@
                 new ArrayList<PaymentInstrument>();
 
         for (ServiceWorkerPaymentAppBridge.Option option : mManifest.options) {
-            instruments.add(new ServiceWorkerPaymentInstrument(mManifest.scopeUrl, option));
+            instruments.add(new ServiceWorkerPaymentInstrument(
+                    mWebContents, mManifest.registrationId, option));
         }
 
         new Handler().post(new Runnable() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
index 46cf672f..a67cd751 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentAppBridge.java
@@ -7,7 +7,6 @@
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 
-import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.SuppressFBWarnings;
 import org.chromium.content_public.browser.WebContents;
@@ -15,7 +14,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
+import java.util.Set;
 
 /**
  * Native bridge for interacting with service worker based payment apps.
@@ -33,11 +32,11 @@
      */
     public static class Manifest {
         /**
-         * The scope url of the service worker.
+         * The registration ID of the service worker.
          *
          * This can be used to identify a service worker based payment app.
          */
-        public String scopeUrl;
+        public long registrationId;
         public String label;
         public Drawable icon;
         public List<Option> options = new ArrayList<>();
@@ -56,50 +55,33 @@
         public List<String> enabledMethods = new ArrayList<>();
     }
 
-    /**
-     * Fetch all the installed service worker app manifests.
-     *
-     * This method is protected so that it can be overridden by tests.
-     *
-     * @return The installed service worker app manifests.
-     */
-    @VisibleForTesting
-    protected List<Manifest> getAllAppManifests() {
-        return nativeGetAllAppManifests();
-    }
-
     @Override
     public void create(Context context, WebContents webContents,
             PaymentAppFactory.PaymentAppCreatedCallback callback) {
-        List<Manifest> manifests = getAllAppManifests();
-        for (int i = 0; i < manifests.size(); i++) {
-            callback.onPaymentAppCreated(new ServiceWorkerPaymentApp(manifests.get(i)));
-        }
-        callback.onAllPaymentAppsCreated();
+        nativeGetAllAppManifests(webContents, callback);
     }
 
     /**
      * Invoke a payment app with a given option and matching method data.
+     *
+     * @param webContents    The web contents that invoked PaymentRequest.
+     * @param registrationId The service worker registration ID of the Payment App.
+     * @param optionId       The ID of the PaymentOption that was selected by the user.
+     * @param methodData     The PaymentMethodData objects that are relevant for this payment app.
      */
-    public void invokePaymentapp(
-            String scopeUrl, String optionId, Map<String, PaymentMethodData> methodData) {
-        nativeInvokePaymentApp(scopeUrl, optionId, methodData);
+    public static void invokePaymentApp(WebContents webContents, long registrationId,
+            String optionId, Set<PaymentMethodData> methodData) {
+        nativeInvokePaymentApp(webContents, registrationId, optionId,
+                methodData.toArray(new PaymentMethodData[0]));
     }
 
     @CalledByNative
-    private static List<Manifest> createManifestList() {
-        return new ArrayList<Manifest>();
-    }
-
-    @CalledByNative
-    private static Manifest createAndAddManifest(
-            List<Manifest> manifestList, String scopeUrl, String label, String icon) {
+    private static Manifest createManifest(long registrationId, String label, String icon) {
         Manifest manifest = new Manifest();
-        manifest.scopeUrl = scopeUrl;
+        manifest.registrationId = registrationId;
         manifest.label = label;
         // TODO(tommyt): crbug.com/669876. Handle icons.
         manifest.icon = null;
-        manifestList.add(manifest);
         return manifest;
     }
 
@@ -120,7 +102,26 @@
         option.enabledMethods.add(enabledMethod);
     }
 
-    private static native List<Manifest> nativeGetAllAppManifests();
-    private static native void nativeInvokePaymentApp(
-            String scopeUrl, String optionId, Object methodDataMap);
+    @CalledByNative
+    private static void onGotManifest(Manifest manifest, WebContents webContents, Object callback) {
+        assert callback instanceof PaymentAppFactory.PaymentAppCreatedCallback;
+        ((PaymentAppFactory.PaymentAppCreatedCallback) callback)
+                .onPaymentAppCreated(new ServiceWorkerPaymentApp(webContents, manifest));
+    }
+
+    @CalledByNative
+    private static void onGotAllManifests(Object callback) {
+        assert callback instanceof PaymentAppFactory.PaymentAppCreatedCallback;
+        ((PaymentAppFactory.PaymentAppCreatedCallback) callback).onAllPaymentAppsCreated();
+    }
+
+    /*
+     * TODO(tommyt): crbug.com/505554. Change the |callback| parameter below to
+     * be of type PaymentAppFactory.PaymentAppCreatedCallback, once this JNI bug
+     * has been resolved.
+     */
+    private static native void nativeGetAllAppManifests(WebContents webContents, Object callback);
+
+    private static native void nativeInvokePaymentApp(WebContents webContents, long registrationId,
+            String optionId, PaymentMethodData[] methodData);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentInstrument.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentInstrument.java
index 5dd4af9d..f0f9761 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentInstrument.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ServiceWorkerPaymentInstrument.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.payments;
 
+import org.chromium.content_public.browser.WebContents;
 import org.chromium.payments.mojom.PaymentItem;
 import org.chromium.payments.mojom.PaymentMethodData;
 
@@ -22,6 +23,8 @@
  * @see https://w3c.github.io/webpayments-payment-apps-api/
  */
 public class ServiceWorkerPaymentInstrument extends PaymentInstrument {
+    private final WebContents mWebContents;
+    private final long mAppRegistrationId;
     private final ServiceWorkerPaymentAppBridge.Option mOption;
     private final Set<String> mMethodNames;
 
@@ -31,12 +34,16 @@
      *
      * @see https://w3c.github.io/webpayments-payment-apps-api/#payment-app-options
      *
-     * @param scopeUrl    The scope url of the corresponding service worker payment app.
-     * @param option      A payment app option from the payment app.
+     * @param webContents       The web contents where PaymentRequest was invoked.
+     * @param appRegistrationId The registration id of the corresponding service worker payment app.
+     * @param option            A payment app option from the payment app.
      */
-    public ServiceWorkerPaymentInstrument(String scopeUrl,
+    public ServiceWorkerPaymentInstrument(WebContents webContents, long appRegistrationId,
             ServiceWorkerPaymentAppBridge.Option option) {
-        super(scopeUrl + "#" + option.id, option.label, null /* icon */, option.icon);
+        super(Long.toString(appRegistrationId) + "#" + option.id, option.label, null /* icon */,
+                option.icon);
+        mWebContents = webContents;
+        mAppRegistrationId = appRegistrationId;
         mOption = option;
 
         mMethodNames = new HashSet<String>(option.enabledMethods);
@@ -51,8 +58,8 @@
     public void invokePaymentApp(String merchantName, String origin, PaymentItem total,
             List<PaymentItem> cart, Map<String, PaymentMethodData> methodData,
             InstrumentDetailsCallback callback) {
-        // TODO(tommyt): crbug.com/669876. Implement this for use with Service Worker Payment Apps.
-        callback.onInstrumentDetailsError();
+        ServiceWorkerPaymentAppBridge.invokePaymentApp(
+                mWebContents, mAppRegistrationId, mOption.id, new HashSet<>(methodData.values()));
     }
 
     @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServiceWorkerPaymentAppTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServiceWorkerPaymentAppTest.java
index c1657df3..e2efac1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServiceWorkerPaymentAppTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServiceWorkerPaymentAppTest.java
@@ -4,12 +4,13 @@
 
 package org.chromium.chrome.browser.payments;
 
+import android.content.Context;
 import android.support.test.filters.MediumTest;
 
 import org.chromium.base.test.util.Feature;
+import org.chromium.content_public.browser.WebContents;
 
 import java.util.Arrays;
-import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
@@ -31,19 +32,20 @@
     }
 
     /**
-     * Installs a service worker based payment app for testing.
+     * Installs a mock service worker based payment app for testing.
      *
      * @param optionPresence Whether the manifest has any payment options. Either NO_OPTIONS
      *                       ONE_OPTION or TWO_OPTIONS
      */
-    private void installServiceWorkerPaymentApp(final int instrumentPresence) {
+    private void installMockServiceWorkerPaymentApp(final int instrumentPresence) {
         PaymentAppFactory.getInstance().addAdditionalFactory(
-                new ServiceWorkerPaymentAppBridge() {
+                new PaymentAppFactory.PaymentAppFactoryAddition() {
                     @Override
-                    public List<Manifest> getAllAppManifests() {
+                    public void create(Context context, WebContents webContents,
+                            PaymentAppFactory.PaymentAppCreatedCallback callback) {
                         ServiceWorkerPaymentAppBridge.Manifest testManifest =
                                 new ServiceWorkerPaymentAppBridge.Manifest();
-                        testManifest.scopeUrl = "https://bobpay.com/app";
+                        testManifest.registrationId = 0;
                         testManifest.label = "BobPay";
 
                         if (instrumentPresence != NO_OPTIONS) {
@@ -66,7 +68,9 @@
                             testManifest.options.add(testOption);
                         }
 
-                        return Arrays.asList(testManifest);
+                        callback.onPaymentAppCreated(
+                                new ServiceWorkerPaymentApp(webContents, testManifest));
+                        callback.onAllPaymentAppsCreated();
                     }
                 });
     }
@@ -79,7 +83,7 @@
     @Feature({"Payments"})
     public void testNoOptions() throws InterruptedException, ExecutionException,
             TimeoutException {
-        installServiceWorkerPaymentApp(NO_OPTIONS);
+        installMockServiceWorkerPaymentApp(NO_OPTIONS);
         openPageAndClickBuyAndWait(mShowFailed);
         expectResultContains(
                 new String[]{"show() rejected", "The payment method is not supported"});
@@ -89,7 +93,7 @@
     @Feature({"Payments"})
     public void testOneOption() throws InterruptedException, ExecutionException,
             TimeoutException {
-        installServiceWorkerPaymentApp(ONE_OPTION);
+        installMockServiceWorkerPaymentApp(ONE_OPTION);
         triggerUIAndWait(mReadyForInput);
         // TODO(tommyt): crbug.com/669876. Expand this test as we implement more
         // service worker based payment app functionality.
@@ -99,7 +103,7 @@
     @Feature({"Payments"})
     public void testTwoOptions() throws InterruptedException, ExecutionException,
             TimeoutException {
-        installServiceWorkerPaymentApp(TWO_OPTIONS);
+        installMockServiceWorkerPaymentApp(TWO_OPTIONS);
         triggerUIAndWait(mReadyForInput);
         // TODO(tommyt): crbug.com/669876. Expand this test as we implement more
         // service worker based payment app functionality.
diff --git a/chrome/browser/android/payments/service_worker_payment_app_bridge.cc b/chrome/browser/android/payments/service_worker_payment_app_bridge.cc
index 3421925..9ab20a3 100644
--- a/chrome/browser/android/payments/service_worker_payment_app_bridge.cc
+++ b/chrome/browser/android/payments/service_worker_payment_app_bridge.cc
@@ -8,34 +8,35 @@
 #include "base/android/scoped_java_ref.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "components/payments/payment_app.mojom.h"
-#include "content/public/browser/service_worker_context.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/payment_app_context.h"
 #include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_contents.h"
 #include "jni/ServiceWorkerPaymentAppBridge_jni.h"
 
+using base::android::AttachCurrentThread;
 using base::android::ConvertUTF8ToJavaString;
 using base::android::JavaParamRef;
+using base::android::JavaRef;
+using base::android::ScopedJavaGlobalRef;
 using base::android::ScopedJavaLocalRef;
 
-static ScopedJavaLocalRef<jobject> GetAllAppManifests(
-    JNIEnv* env,
-    const JavaParamRef<jclass>& jcaller) {
-  // TODO(tommyt): crbug.com/669876. Initialise the following two variables.
-  // At the moment, they are empty, so this function will return an empty
-  // list of manifests. We need to hook this function up to the service worker
-  // payment apps.
-  std::string scope_url;
-  std::vector<payments::mojom::PaymentAppManifestPtr> manifests;
+namespace {
 
-  ScopedJavaLocalRef<jobject> java_manifests =
-      Java_ServiceWorkerPaymentAppBridge_createManifestList(env);
-  for (const auto& manifest : manifests) {
+void OnGotAllManifests(const JavaRef<jobject>& jweb_contents,
+                       const JavaRef<jobject>& jcallback,
+                       content::PaymentAppContext::Manifests manifests) {
+  JNIEnv* env = AttachCurrentThread();
+
+  for (const auto& entry : manifests) {
     ScopedJavaLocalRef<jobject> java_manifest =
-        Java_ServiceWorkerPaymentAppBridge_createAndAddManifest(
-            env, java_manifests, ConvertUTF8ToJavaString(env, scope_url),
-            ConvertUTF8ToJavaString(env, manifest->name),
-            manifest->icon ? ConvertUTF8ToJavaString(env, *manifest->icon)
-                           : nullptr);
-    for (const auto& option : manifest->options) {
+        Java_ServiceWorkerPaymentAppBridge_createManifest(
+            env, entry.first, ConvertUTF8ToJavaString(env, entry.second->name),
+            entry.second->icon
+                ? ConvertUTF8ToJavaString(env, *entry.second->icon)
+                : nullptr);
+    for (const auto& option : entry.second->options) {
       ScopedJavaLocalRef<jobject> java_option =
           Java_ServiceWorkerPaymentAppBridge_createAndAddOption(
               env, java_manifest, ConvertUTF8ToJavaString(env, option->id),
@@ -47,16 +48,45 @@
             env, java_option, ConvertUTF8ToJavaString(env, enabled_method));
       }
     }
+
+    Java_ServiceWorkerPaymentAppBridge_onGotManifest(env, java_manifest,
+                                                     jweb_contents, jcallback);
   }
 
-  return java_manifests;
+  Java_ServiceWorkerPaymentAppBridge_onGotAllManifests(env, jcallback);
+}
+
+}  // namespace
+
+static void GetAllAppManifests(JNIEnv* env,
+                               const JavaParamRef<jclass>& jcaller,
+                               const JavaParamRef<jobject>& jweb_contents,
+                               const JavaParamRef<jobject>& jcallback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  content::WebContents* web_contents =
+      content::WebContents::FromJavaWebContents(jweb_contents);
+
+  content::BrowserContext* browser_context = web_contents->GetBrowserContext();
+
+  content::StoragePartition* storage_partition =
+      content::BrowserContext::GetDefaultStoragePartition(browser_context);
+
+  content::PaymentAppContext* payment_app_context =
+      storage_partition->GetPaymentAppContext();
+
+  payment_app_context->GetAllManifests(base::Bind(
+      &OnGotAllManifests, ScopedJavaGlobalRef<jobject>(env, jweb_contents),
+      ScopedJavaGlobalRef<jobject>(env, jcallback)));
 }
 
 static void InvokePaymentApp(JNIEnv* env,
                              const JavaParamRef<jclass>& jcaller,
-                             const JavaParamRef<jstring>& scopeUrl,
-                             const JavaParamRef<jstring>& optionId,
-                             const JavaParamRef<jobject>& methodDataMap) {
+                             const JavaParamRef<jobject>& jweb_contents,
+                             jlong registration_id,
+                             const JavaParamRef<jstring>& joption_id,
+                             const JavaParamRef<jobjectArray>& jmethod_data) {
+  // TODO(tommyt): crbug.com/669876. Implement this
   NOTIMPLEMENTED();
 }
 
diff --git a/chrome/browser/browsing_data/browsing_data_remover_unittest.cc b/chrome/browser/browsing_data/browsing_data_remover_unittest.cc
index fdd1961..b2820437 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_unittest.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_unittest.cc
@@ -240,6 +240,9 @@
       override {
     return nullptr;
   }
+  content::PaymentAppContext* GetPaymentAppContext() override {
+    return nullptr;
+  }
   content::HostZoomMap* GetHostZoomMap() override { return nullptr; }
   content::HostZoomLevelContext* GetHostZoomLevelContext() override {
     return nullptr;
diff --git a/chrome/browser/chromeos/arc/arc_service_launcher.cc b/chrome/browser/chromeos/arc/arc_service_launcher.cc
index 758e6d5..ef22e198 100644
--- a/chrome/browser/chromeos/arc/arc_service_launcher.cc
+++ b/chrome/browser/chromeos/arc/arc_service_launcher.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/bind.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_service.h"
@@ -25,6 +26,8 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "components/arc/arc_service_manager.h"
+#include "components/arc/arc_session.h"
+#include "components/arc/arc_session_runner.h"
 #include "components/arc/audio/arc_audio_bridge.h"
 #include "components/arc/bluetooth/arc_bluetooth_bridge.h"
 #include "components/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h"
@@ -78,7 +81,9 @@
 
   // Creates ArcSessionManager at first.
   arc_session_manager_ =
-      base::MakeUnique<ArcSessionManager>(arc_bridge_service);
+      base::MakeUnique<ArcSessionManager>(base::MakeUnique<ArcSessionRunner>(
+          base::Bind(ArcSession::Create, arc_bridge_service,
+                     arc_service_manager_->blocking_task_runner())));
 
   // List in lexicographical order.
   arc_service_manager_->AddService(
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.cc b/chrome/browser/chromeos/arc/arc_session_manager.cc
index 0a6204c..3544f00 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager.cc
@@ -42,6 +42,7 @@
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/session_manager_client.h"
 #include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_session_runner.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
 #include "components/sync_preferences/pref_service_syncable.h"
@@ -83,22 +84,22 @@
 
 }  // namespace
 
-ArcSessionManager::ArcSessionManager(ArcBridgeService* bridge_service)
-    : ArcService(bridge_service),
+ArcSessionManager::ArcSessionManager(
+    std::unique_ptr<ArcSessionRunner> arc_session_runner)
+    : arc_session_runner_(std::move(arc_session_runner)),
       attempt_user_exit_callback_(base::Bind(chrome::AttemptUserExit)),
       weak_ptr_factory_(this) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(!g_arc_session_manager);
   g_arc_session_manager = this;
-
-  arc_bridge_service()->AddObserver(this);
+  arc_session_runner_->AddObserver(this);
 }
 
 ArcSessionManager::~ArcSessionManager() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   Shutdown();
-  arc_bridge_service()->RemoveObserver(this);
+  arc_session_runner_->RemoveObserver(this);
 
   DCHECK_EQ(this, g_arc_session_manager);
   g_arc_session_manager = nullptr;
@@ -195,6 +196,11 @@
   return user_manager::UserManager::Get()->IsLoggedInAsArcKioskApp();
 }
 
+void ArcSessionManager::OnSessionReady() {
+  for (auto& observer : arc_session_observer_list_)
+    observer.OnSessionReady();
+}
+
 void ArcSessionManager::OnSessionStopped(StopReason reason) {
   // TODO(crbug.com/625923): Use |reason| to report more detailed errors.
   if (arc_sign_in_timer_.IsRunning())
@@ -203,7 +209,7 @@
   if (profile_->GetPrefs()->GetBoolean(prefs::kArcDataRemoveRequested)) {
     // This should be always true, but just in case as this is looked at
     // inside RemoveArcData() at first.
-    DCHECK(arc_bridge_service()->stopped());
+    DCHECK(arc_session_runner_->IsStopped());
     RemoveArcData();
   } else {
     // To support special "Stop and enable ARC" procedure for enterprise,
@@ -213,6 +219,9 @@
         FROM_HERE, base::Bind(&ArcSessionManager::MaybeReenableArc,
                               weak_ptr_factory_.GetWeakPtr()));
   }
+
+  for (auto& observer : arc_session_observer_list_)
+    observer.OnSessionStopped(reason);
 }
 
 void ArcSessionManager::RemoveArcData() {
@@ -223,8 +232,8 @@
   // OnArcDataRemoved resets this flag.
   profile_->GetPrefs()->SetBoolean(prefs::kArcDataRemoveRequested, true);
 
-  if (!arc_bridge_service()->stopped()) {
-    // Just set a flag. On bridge stopped, this will be re-called,
+  if (!arc_session_runner_->IsStopped()) {
+    // Just set a flag. On session stopped, this will be re-called,
     // then session manager should remove the data.
     return;
   }
@@ -258,7 +267,7 @@
 void ArcSessionManager::MaybeReenableArc() {
   // Here check if |reenable_arc_| is marked or not.
   // The only case this happens should be in the special case for enterprise
-  // "on managed lost" case. In that case, OnBridgeStopped() should trigger
+  // "on managed lost" case. In that case, OnSessionStopped() should trigger
   // the RemoveArcData(), then this.
   if (!reenable_arc_ || !IsArcEnabled())
     return;
@@ -273,10 +282,10 @@
 void ArcSessionManager::OnProvisioningFinished(ProvisioningResult result) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  // Due asynchronous nature of stopping Arc bridge, OnProvisioningFinished may
-  // arrive after setting the |State::STOPPED| state and |State::Active| is not
-  // guaranty set here. prefs::kArcDataRemoveRequested is also can be active
-  // for now.
+  // Due asynchronous nature of stopping the ARC instance,
+  // OnProvisioningFinished may arrive after setting the |State::STOPPED| state
+  // and |State::Active| is not guaranteed to be set here.
+  // prefs::kArcDataRemoveRequested also can be active for now.
 
   if (provisioning_reported_) {
     // We don't expect ProvisioningResult::SUCCESS is reported twice or reported
@@ -370,7 +379,7 @@
       result == ProvisioningResult::CHROME_SERVER_COMMUNICATION_ERROR) {
     if (profile_->GetPrefs()->HasPrefPath(prefs::kArcSignedIn))
       profile_->GetPrefs()->SetBoolean(prefs::kArcSignedIn, false);
-    ShutdownBridge();
+    ShutdownSession();
     if (support_host_)
       support_host_->ShowError(error, false);
     return;
@@ -387,8 +396,8 @@
     RemoveArcData();
   }
 
-  // We'll delay shutting down the bridge in this case to allow people to send
-  // feedback.
+  // We'll delay shutting down the ARC instance in this case to allow people
+  // to send feedback.
   if (support_host_)
     support_host_->ShowError(error, true /* = show send feedback button */);
 }
@@ -493,7 +502,7 @@
   if (!g_disable_ui_for_testing)
     ArcAuthNotification::Hide();
 
-  ShutdownBridge();
+  ShutdownSession();
   if (support_host_) {
     support_host_->Close();
     support_host_->RemoveObserver(this);
@@ -535,7 +544,7 @@
     profile_->GetPrefs()->SetBoolean(prefs::kArcSignedIn, false);
     profile_->GetPrefs()->SetBoolean(prefs::kArcTermsAccepted, false);
   }
-  ShutdownBridge();
+  ShutdownSession();
   if (support_host_)
     support_host_->Close();
 }
@@ -634,12 +643,15 @@
   StartTermsOfServiceNegotiation();
 }
 
-void ArcSessionManager::ShutdownBridge() {
+void ArcSessionManager::ShutdownSession() {
   arc_sign_in_timer_.Stop();
   playstore_launcher_.reset();
   terms_of_service_negotiator_.reset();
   android_management_checker_.reset();
-  arc_bridge_service()->RequestStop();
+  arc_session_runner_->RequestStop();
+  // TODO(hidehiko): The ARC instance's stopping is asynchronous, so it might
+  // still be running when we return from this function. Do not set the
+  // STOPPED state immediately here.
   if (state_ != State::NOT_INITIALIZED && state_ != State::REMOVING_DATA_DIR)
     SetState(State::STOPPED);
   for (auto& observer : observer_list_)
@@ -656,11 +668,31 @@
   observer_list_.RemoveObserver(observer);
 }
 
+void ArcSessionManager::AddSessionObserver(ArcSessionObserver* observer) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  arc_session_observer_list_.AddObserver(observer);
+}
+
+void ArcSessionManager::RemoveSessionObserver(ArcSessionObserver* observer) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  arc_session_observer_list_.RemoveObserver(observer);
+}
+
+bool ArcSessionManager::IsSessionRunning() const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  return arc_session_runner_->IsRunning();
+}
+
+bool ArcSessionManager::IsSessionStopped() const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  return arc_session_runner_->IsStopped();
+}
+
 // This is the special method to support enterprise mojo API.
 // TODO(hidehiko): Remove this.
 void ArcSessionManager::StopAndEnableArc() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(!arc_bridge_service()->stopped());
+  DCHECK(!arc_session_runner_->IsStopped());
   reenable_arc_ = true;
   StopArc();
 }
@@ -673,7 +705,7 @@
 
   provisioning_reported_ = false;
 
-  arc_bridge_service()->RequestStart();
+  arc_session_runner_->RequestStart();
   SetState(State::ACTIVE);
 }
 
@@ -760,9 +792,10 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(!terms_of_service_negotiator_);
 
-  if (!arc_bridge_service()->stopped()) {
-    // If the user attempts to re-enable ARC while the bridge is still running
-    // the user should not be able to continue until the bridge has stopped.
+  if (!arc_session_runner_->IsStopped()) {
+    // If the user attempts to re-enable ARC while the ARC instance is still
+    // running the user should not be able to continue until the ARC instance
+    // has stopped.
     if (support_host_) {
       support_host_->ShowError(
           ArcSupportHost::Error::SIGN_IN_SERVICE_UNAVAILABLE_ERROR, false);
@@ -801,7 +834,7 @@
 
 void ArcSessionManager::StartArcAndroidManagementCheck() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(arc_bridge_service()->stopped());
+  DCHECK(arc_session_runner_->IsStopped());
   DCHECK(state_ == State::SHOWING_TERMS_OF_SERVICE ||
          state_ == State::CHECKING_ANDROID_MANAGEMENT);
   SetState(State::CHECKING_ANDROID_MANAGEMENT);
@@ -830,7 +863,7 @@
       StartArc();
       break;
     case policy::AndroidManagementClient::Result::MANAGED:
-      ShutdownBridge();
+      ShutdownSession();
       if (support_host_) {
         support_host_->ShowError(
             ArcSupportHost::Error::ANDROID_MANAGEMENT_REQUIRED_ERROR, false);
@@ -838,7 +871,7 @@
       UpdateOptInCancelUMA(OptInCancelReason::ANDROID_MANAGEMENT_REQUIRED);
       break;
     case policy::AndroidManagementClient::Result::ERROR:
-      ShutdownBridge();
+      ShutdownSession();
       if (support_host_) {
         support_host_->ShowError(
             ArcSupportHost::Error::SERVER_COMMUNICATION_ERROR, false);
@@ -895,13 +928,13 @@
   } else if (!profile_->GetPrefs()->GetBoolean(prefs::kArcTermsAccepted)) {
     StartTermsOfServiceNegotiation();
   } else if (support_host_->ui_page() == ArcSupportHost::UIPage::ERROR &&
-             !arc_bridge_service()->stopped()) {
+             !arc_session_runner_->IsStopped()) {
     // ERROR_WITH_FEEDBACK is set in OnSignInFailed(). In the case, stopping
     // ARC was postponed to contain its internal state into the report.
     // Here, on retry, stop it, then restart.
     DCHECK_EQ(State::ACTIVE, state_);
     support_host_->ShowArcLoading();
-    ShutdownBridge();
+    ShutdownSession();
     reenable_arc_ = true;
   } else if (state_ == State::ACTIVE) {
     // This case is handled in ArcAuthService.
@@ -920,6 +953,16 @@
   chrome::OpenFeedbackDialog(nullptr);
 }
 
+void ArcSessionManager::SetArcSessionRunnerForTesting(
+    std::unique_ptr<ArcSessionRunner> arc_session_runner) {
+  DCHECK(arc_session_runner);
+  DCHECK(arc_session_runner_);
+  DCHECK(arc_session_runner_->IsStopped());
+  arc_session_runner_->RemoveObserver(this);
+  arc_session_runner_ = std::move(arc_session_runner);
+  arc_session_runner_->AddObserver(this);
+}
+
 void ArcSessionManager::SetAttemptUserExitCallbackForTesting(
     const base::Closure& callback) {
   DCHECK(!callback.is_null());
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.h b/chrome/browser/chromeos/arc/arc_session_manager.h
index 59678ace..20ef5ab 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager.h
+++ b/chrome/browser/chromeos/arc/arc_session_manager.h
@@ -15,12 +15,10 @@
 #include "base/timer/timer.h"
 #include "chrome/browser/chromeos/arc/arc_support_host.h"
 #include "chrome/browser/chromeos/policy/android_management_client.h"
-#include "components/arc/arc_service.h"
 #include "components/arc/arc_session_observer.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/sync_preferences/pref_service_syncable_observer.h"
 #include "components/sync_preferences/synced_pref_observer.h"
-#include "mojo/public/cpp/bindings/binding.h"
 
 class ArcAppLauncher;
 class Profile;
@@ -38,14 +36,13 @@
 class ArcAndroidManagementChecker;
 class ArcAuthCodeFetcher;
 class ArcAuthContext;
-class ArcBridgeService;
+class ArcSessionRunner;
 class ArcTermsOfServiceNegotiator;
 enum class ProvisioningResult : int;
 
 // This class proxies the request from the client to fetch an auth code from
 // LSO. It lives on the UI thread.
-class ArcSessionManager : public ArcService,
-                          public ArcSessionObserver,
+class ArcSessionManager : public ArcSessionObserver,
                           public ArcSupportHost::Observer,
                           public sync_preferences::PrefServiceSyncableObserver,
                           public sync_preferences::SyncedPrefObserver {
@@ -103,7 +100,8 @@
    public:
     virtual ~Observer() = default;
 
-    // Called to notify that ARC bridge is shut down.
+    // Called to notify that ARC session is shut down.
+    // TODO(hidehiko): Rename the observer callback to OnArcSessionShutdown().
     virtual void OnArcBridgeShutdown() {}
 
     // Called to notify that ARC enabled state has been updated.
@@ -117,7 +115,8 @@
     virtual void OnArcDataRemoved() {}
   };
 
-  explicit ArcSessionManager(ArcBridgeService* bridge_service);
+  explicit ArcSessionManager(
+      std::unique_ptr<ArcSessionRunner> arc_session_runner);
   ~ArcSessionManager() override;
 
   static ArcSessionManager* Get();
@@ -154,8 +153,16 @@
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
-  // ArcSessionObserver:
-  void OnSessionStopped(StopReason reason) override;
+  // Adds or removes ArcSessionObservers.
+  // TODO(hidehiko): The observer should be migrated into
+  // ArcSessionManager::Observer.
+  void AddSessionObserver(ArcSessionObserver* observer);
+  void RemoveSessionObserver(ArcSessionObserver* observer);
+
+  // Returns true if ARC instance is running/stopped, respectively.
+  // See ArcSessionRunner::IsRunning()/IsStopped() for details.
+  bool IsSessionRunning() const;
+  bool IsSessionStopped() const;
 
   // Called from Arc support platform app when user cancels signing.
   void CancelAuthCode();
@@ -207,6 +214,9 @@
 
   void OnProvisioningFinished(ProvisioningResult result);
 
+  // Injectors for testing.
+  void SetArcSessionRunnerForTesting(
+      std::unique_ptr<ArcSessionRunner> arc_session_runner);
   void SetAttemptUserExitCallbackForTesting(const base::Closure& callback);
 
  private:
@@ -215,7 +225,7 @@
   void OnTermsOfServiceNegotiated(bool accepted);
 
   void SetState(State state);
-  void ShutdownBridge();
+  void ShutdownSession();
   void OnOptInPreferenceChanged();
   void OnAndroidManagementPassed();
   void OnArcDataRemoved(bool success);
@@ -236,6 +246,12 @@
   void OnBackgroundAndroidManagementChecked(
       policy::AndroidManagementClient::Result result);
 
+  // ArcSessionObserver:
+  void OnSessionReady() override;
+  void OnSessionStopped(StopReason reason) override;
+
+  std::unique_ptr<ArcSessionRunner> arc_session_runner_;
+
   // Unowned pointer. Keeps current profile.
   Profile* profile_ = nullptr;
 
@@ -244,6 +260,7 @@
 
   State state_ = State::NOT_INITIALIZED;
   base::ObserverList<Observer> observer_list_;
+  base::ObserverList<ArcSessionObserver> arc_session_observer_list_;
   std::unique_ptr<ArcAppLauncher> playstore_launcher_;
   bool reenable_arc_ = false;
   bool provisioning_reported_ = false;
diff --git a/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc b/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc
index 8ca0fd5..24c24d5 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager_browsertest.cc
@@ -154,10 +154,6 @@
     chromeos::DBusThreadManager::GetSetterForTesting()->SetSessionManagerClient(
         std::unique_ptr<chromeos::SessionManagerClient>(
             fake_session_manager_client));
-
-    // Mock out ARC instance.
-    ArcServiceManager::SetArcSessionRunnerForTesting(
-        base::MakeUnique<ArcSessionRunner>(base::Bind(FakeArcSession::Create)));
   }
 
   void SetUpOnMainThread() override {
@@ -166,6 +162,8 @@
     // Init ArcSessionManager for testing.
     ArcSessionManager::DisableUIForTesting();
     ArcSessionManager::EnableCheckAndroidManagementForTesting();
+    ArcSessionManager::Get()->SetArcSessionRunnerForTesting(
+        base::MakeUnique<ArcSessionRunner>(base::Bind(FakeArcSession::Create)));
 
     EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
 
diff --git a/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc b/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
index d49c893..1af0884 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
@@ -29,7 +29,6 @@
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_session_manager_client.h"
-#include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/arc_session_runner.h"
 #include "components/arc/test/fake_arc_session.h"
@@ -74,14 +73,12 @@
     profile_ = profile_builder.Build();
     StartPreferenceSyncing();
 
-    ArcServiceManager::SetArcSessionRunnerForTesting(
-        base::MakeUnique<ArcSessionRunner>(base::Bind(FakeArcSession::Create)));
     arc_service_manager_ = base::MakeUnique<ArcServiceManager>(nullptr);
     arc_session_manager_ = base::MakeUnique<ArcSessionManager>(
-        arc_service_manager_->arc_bridge_service());
+        base::MakeUnique<ArcSessionRunner>(base::Bind(FakeArcSession::Create)));
 
     // Check initial conditions.
-    EXPECT_TRUE(bridge_service()->stopped());
+    EXPECT_TRUE(arc_session_manager_->IsSessionStopped());
 
     chromeos::WallpaperManager::Initialize();
   }
@@ -100,9 +97,7 @@
 
  protected:
   Profile* profile() { return profile_.get(); }
-  ArcBridgeService* bridge_service() {
-    return arc_service_manager_->arc_bridge_service();
-  }
+
   ArcSessionManager* arc_session_manager() {
     return arc_session_manager_.get();
   }
@@ -226,7 +221,7 @@
 }
 
 TEST_F(ArcSessionManagerTest, BaseWorkflow) {
-  ASSERT_FALSE(bridge_service()->ready());
+  ASSERT_TRUE(arc_session_manager()->IsSessionStopped());
   ASSERT_EQ(ArcSessionManager::State::NOT_INITIALIZED,
             arc_session_manager()->state());
 
@@ -247,12 +242,12 @@
   arc_session_manager()->StartArc();
 
   ASSERT_EQ(ArcSessionManager::State::ACTIVE, arc_session_manager()->state());
-  ASSERT_TRUE(bridge_service()->ready());
+  ASSERT_TRUE(arc_session_manager()->IsSessionRunning());
 
   arc_session_manager()->Shutdown();
   ASSERT_EQ(ArcSessionManager::State::NOT_INITIALIZED,
             arc_session_manager()->state());
-  ASSERT_FALSE(bridge_service()->ready());
+  ASSERT_TRUE(arc_session_manager()->IsSessionStopped());
 
   // Send profile and don't provide a code.
   arc_session_manager()->OnPrimaryUserProfilePrepared(profile());
@@ -339,22 +334,22 @@
   prefs->SetBoolean(prefs::kArcTermsAccepted, true);
   arc_session_manager()->StartArc();
   EXPECT_EQ(ArcSessionManager::State::ACTIVE, arc_session_manager()->state());
-  EXPECT_TRUE(bridge_service()->ready());
+  EXPECT_TRUE(arc_session_manager()->IsSessionRunning());
   EXPECT_FALSE(prefs->GetBoolean(prefs::kArcSignedIn));
   arc_session_manager()->OnProvisioningFinished(ProvisioningResult::SUCCESS);
   EXPECT_TRUE(prefs->GetBoolean(prefs::kArcSignedIn));
   EXPECT_EQ(ArcSessionManager::State::ACTIVE, arc_session_manager()->state());
-  EXPECT_TRUE(bridge_service()->ready());
+  EXPECT_TRUE(arc_session_manager()->IsSessionRunning());
 
   // Second start, no fetching code is expected.
   arc_session_manager()->Shutdown();
   EXPECT_EQ(ArcSessionManager::State::NOT_INITIALIZED,
             arc_session_manager()->state());
-  EXPECT_FALSE(bridge_service()->ready());
+  EXPECT_TRUE(arc_session_manager()->IsSessionStopped());
   arc_session_manager()->OnPrimaryUserProfilePrepared(profile());
   EXPECT_TRUE(prefs->GetBoolean(prefs::kArcSignedIn));
   EXPECT_EQ(ArcSessionManager::State::ACTIVE, arc_session_manager()->state());
-  EXPECT_TRUE(bridge_service()->ready());
+  EXPECT_TRUE(arc_session_manager()->IsSessionRunning());
 
   // Report failure.
   arc_session_manager()->OnProvisioningFinished(
@@ -363,7 +358,7 @@
   // the ARC is still necessary to run on background for gathering the logs.
   EXPECT_TRUE(prefs->GetBoolean(prefs::kArcSignedIn));
   EXPECT_EQ(ArcSessionManager::State::ACTIVE, arc_session_manager()->state());
-  EXPECT_TRUE(bridge_service()->ready());
+  EXPECT_TRUE(arc_session_manager()->IsSessionRunning());
 
   // Correctly stop service.
   arc_session_manager()->Shutdown();
diff --git a/chrome/browser/chromeos/arc/notification/arc_boot_error_notification.cc b/chrome/browser/chromeos/arc/notification/arc_boot_error_notification.cc
index 5d38252..61d53e1 100644
--- a/chrome/browser/chromeos/arc/notification/arc_boot_error_notification.cc
+++ b/chrome/browser/chromeos/arc/notification/arc_boot_error_notification.cc
@@ -8,6 +8,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/arc/arc_session_manager.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/chrome_pages.h"
@@ -90,11 +91,11 @@
 ArcBootErrorNotification::ArcBootErrorNotification(
     ArcBridgeService* bridge_service)
     : ArcService(bridge_service) {
-  arc_bridge_service()->AddObserver(this);
+  ArcSessionManager::Get()->AddSessionObserver(this);
 }
 
 ArcBootErrorNotification::~ArcBootErrorNotification() {
-  arc_bridge_service()->RemoveObserver(this);
+  ArcSessionManager::Get()->RemoveSessionObserver(this);
 }
 
 void ArcBootErrorNotification::OnSessionStopped(StopReason reason) {
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index 2abfffa..3de03b3 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -209,8 +209,6 @@
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_session_manager_client.h"
 #include "chromeos/dbus/session_manager_client.h"
-#include "components/arc/arc_bridge_service.h"
-#include "components/arc/arc_service_manager.h"
 #include "components/arc/arc_session_runner.h"
 #include "components/arc/test/fake_arc_session.h"
 #include "components/signin/core/account_id/account_id.h"
@@ -4052,6 +4050,9 @@
  protected:
   void SetUpTest() {
     arc::ArcSessionManager::DisableUIForTesting();
+    arc::ArcSessionManager::Get()->SetArcSessionRunnerForTesting(
+        base::MakeUnique<arc::ArcSessionRunner>(
+            base::Bind(arc::FakeArcSession::Create)));
 
     browser()->profile()->GetPrefs()->SetBoolean(prefs::kArcSignedIn, true);
     browser()->profile()->GetPrefs()->SetBoolean(prefs::kArcTermsAccepted,
@@ -4067,10 +4068,6 @@
     chromeos::DBusThreadManager::GetSetterForTesting()->SetSessionManagerClient(
         std::unique_ptr<chromeos::SessionManagerClient>(
             fake_session_manager_client_));
-
-    arc::ArcServiceManager::SetArcSessionRunnerForTesting(
-        base::MakeUnique<arc::ArcSessionRunner>(
-            base::Bind(arc::FakeArcSession::Create)));
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
@@ -4103,20 +4100,19 @@
   SetUpTest();
 
   const PrefService* const pref = browser()->profile()->GetPrefs();
-  const arc::ArcBridgeService* const arc_bridge_service =
-      arc::ArcServiceManager::Get()->arc_bridge_service();
+  const auto* const arc_session_manager = arc::ArcSessionManager::Get();
 
   // ARC is switched off by default.
-  EXPECT_TRUE(arc_bridge_service->stopped());
+  EXPECT_TRUE(arc_session_manager->IsSessionStopped());
   EXPECT_FALSE(pref->GetBoolean(prefs::kArcEnabled));
 
   // Enable ARC.
   SetArcEnabledByPolicy(true);
-  EXPECT_TRUE(arc_bridge_service->ready());
+  EXPECT_TRUE(arc_session_manager->IsSessionRunning());
 
   // Disable ARC.
   SetArcEnabledByPolicy(false);
-  EXPECT_TRUE(arc_bridge_service->stopped());
+  EXPECT_TRUE(arc_session_manager->IsSessionStopped());
 
   TearDownTest();
 }
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc b/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc
index a3b52e3f..2f581887 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc
+++ b/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc
@@ -65,17 +65,25 @@
 
 struct ResourceSummary {
   ResourceSummary()
-      : is_no_store(false),
-        version(0),
+      : version(0),
+        is_no_store(false),
         is_external(false),
-        should_be_recorded(true) {}
+        is_observable(true),
+        is_prohibited(false) {}
 
   ResourcePrefetchPredictor::URLRequestSummary request;
   std::string content;
-  bool is_no_store;
+  // Allows to update HTTP ETag.
   size_t version;
+  // True iff "Cache-control: no-store" header is present.
+  bool is_no_store;
+  // True iff a request for this resource must be ignored by the custom handler.
   bool is_external;
-  bool should_be_recorded;
+  // True iff the LearningObserver must observe this resource.
+  bool is_observable;
+  // A request with |is_prohibited| set to true makes the test that originates
+  // the request fail.
+  bool is_prohibited;
 };
 
 struct RedirectEdge {
@@ -175,6 +183,10 @@
               testing::UnorderedElementsAreArray(expected_subresources));
 }
 
+std::string CreateVersionedETag(size_t version, const std::string& path) {
+  return base::StringPrintf("'%zu%s'", version, path.c_str());
+}
+
 }  // namespace
 
 // Helper class to track and allow waiting for a single OnNavigationLearned
@@ -270,6 +282,9 @@
     embedded_test_server()->RegisterRequestHandler(
         base::Bind(&ResourcePrefetchPredictorBrowserTest::HandleResourceRequest,
                    base::Unretained(this)));
+    embedded_test_server()->RegisterRequestMonitor(base::Bind(
+        &ResourcePrefetchPredictorBrowserTest::MonitorResourceRequest,
+        base::Unretained(this)));
     ASSERT_TRUE(embedded_test_server()->Start());
     predictor_ =
         ResourcePrefetchPredictorFactory::GetForProfile(browser()->profile());
@@ -287,7 +302,17 @@
     // Prefetch all needed resources and change expectations so that all
     // cacheable resources should be served from cache next navigation.
     PrefetchURL(main_frame_url);
+    // To be sure that the browser send no requests to the server after
+    // prefetching.
+    for (auto& kv : resources_) {
+      if (kv.second.is_observable)
+        kv.second.is_prohibited = true;
+    }
     NavigateToURLAndCheckSubresources(main_frame_url);
+    for (auto& kv : resources_) {
+      if (kv.second.is_observable)
+        kv.second.is_prohibited = false;
+    }
   }
 
   void NavigateToURLAndCheckSubresources(
@@ -296,10 +321,10 @@
     GURL endpoint_url = GetRedirectEndpoint(main_frame_url);
     std::vector<URLRequestSummary> url_request_summaries;
     for (const auto& kv : resources_) {
-      if (kv.second.is_no_store || !kv.second.should_be_recorded)
-        continue;
-      url_request_summaries.push_back(
-          GetURLRequestSummaryForResource(endpoint_url, kv.second));
+      if (kv.second.is_observable) {
+        url_request_summaries.push_back(
+            GetURLRequestSummaryForResource(endpoint_url, kv.second));
+      }
     }
 
     bool match_navigation_id =
@@ -315,7 +340,7 @@
         ui_test_utils::BROWSER_TEST_NONE);
     observer.Wait();
     for (auto& kv : resources_) {
-      if (!kv.second.is_no_store && kv.second.should_be_recorded)
+      if (kv.second.is_observable)
         kv.second.request.was_cached = true;
     }
     for (const auto& nav : observer.current_navigation_ids())
@@ -327,7 +352,7 @@
     predictor_->StartPrefetching(main_frame_url, PrefetchOrigin::EXTERNAL);
     observer.Wait();
     for (auto& kv : resources_) {
-      if (!kv.second.is_no_store && kv.second.should_be_recorded)
+      if (kv.second.is_observable)
         kv.second.request.was_cached = true;
     }
   }
@@ -355,12 +380,12 @@
     return resource;
   }
 
-  void AddUnrecordedResources(const std::vector<GURL>& resource_urls) {
+  void AddUnobservableResources(const std::vector<GURL>& resource_urls) {
     for (const GURL& resource_url : resource_urls) {
       auto resource =
           AddResource(resource_url, content::RESOURCE_TYPE_SUB_RESOURCE,
                       net::DEFAULT_PRIORITY);
-      resource->should_be_recorded = false;
+      resource->is_observable = false;
     }
   }
 
@@ -407,6 +432,9 @@
     https_server()->RegisterRequestHandler(
         base::Bind(&ResourcePrefetchPredictorBrowserTest::HandleResourceRequest,
                    base::Unretained(this)));
+    https_server()->RegisterRequestMonitor(base::Bind(
+        &ResourcePrefetchPredictorBrowserTest::MonitorResourceRequest,
+        base::Unretained(this)));
     ASSERT_TRUE(https_server()->Start());
   }
 
@@ -453,7 +481,8 @@
   GURL GetRedirectEndpoint(const GURL& initial_url) const {
     GURL current = initial_url;
     while (true) {
-      auto it = redirects_.find(current);
+      std::map<GURL, RedirectEdge>::const_iterator it =
+          redirects_.find(current);
       if (it == redirects_.end())
         break;
       current = it->second.url;
@@ -461,6 +490,26 @@
     return current;
   }
 
+  void MonitorResourceRequest(
+      const net::test_server::HttpRequest& request) const {
+    std::map<GURL, ResourceSummary>::const_iterator resource_it =
+        resources_.find(request.GetURL());
+    if (resource_it == resources_.end())
+      return;
+
+    const ResourceSummary& summary = resource_it->second;
+    EXPECT_FALSE(summary.is_prohibited) << request.GetURL() << "\n"
+                                        << request.all_headers;
+  }
+
+  // The custom handler for resource requests from the browser to an
+  // EmbeddedTestServer. Runs on the EmbeddedTestServer IO thread.
+  // Finds the data to serve requests in |resources_| map keyed by a request
+  // URL.
+  // Uses also the following headers from the |request|:
+  //   - "Host" to retrieve the host that actually was issued by the browser.
+  //   - "If-None-Match" to determine if the resource is still valid. If the
+  //      ETag values match, the handler responds with a HTTP 304 status.
   std::unique_ptr<net::test_server::HttpResponse> HandleResourceRequest(
       const net::test_server::HttpRequest& request) const {
     GURL resource_url = request.GetURL();
@@ -477,7 +526,8 @@
                     << resource_url.spec();
     }
 
-    auto resource_it = resources_.find(resource_url);
+    std::map<GURL, ResourceSummary>::const_iterator resource_it =
+        resources_.find(resource_url);
     if (resource_it == resources_.end())
       return nullptr;
 
@@ -487,7 +537,15 @@
 
     auto http_response =
         base::MakeUnique<net::test_server::BasicHttpResponse>();
-    http_response->set_code(net::HTTP_OK);
+
+    if (request.headers.find("If-None-Match") != request.headers.end() &&
+        request.headers.at("If-None-Match") ==
+            CreateVersionedETag(summary.version, request.relative_url)) {
+      http_response->set_code(net::HTTP_NOT_MODIFIED);
+    } else {
+      http_response->set_code(net::HTTP_OK);
+    }
+
     if (!summary.request.mime_type.empty())
       http_response->set_content_type(summary.request.mime_type);
     if (!summary.content.empty())
@@ -496,8 +554,7 @@
       http_response->AddCustomHeader("Cache-Control", "no-store");
     if (summary.request.has_validators) {
       http_response->AddCustomHeader(
-          "ETag", base::StringPrintf("'%zu%s'", summary.version,
-                                     request.relative_url.c_str()));
+          "ETag", CreateVersionedETag(summary.version, request.relative_url));
     }
     if (summary.request.always_revalidate)
       http_response->AddCustomHeader("Cache-Control", "no-cache");
@@ -506,9 +563,14 @@
     return std::move(http_response);
   }
 
+  // The custom handler for redirect requests from the browser to an
+  // EmbeddedTestServer. Running on the EmbeddedTestServer IO thread.
+  // Finds the data to serve requests in |redirects_| map keyed by a request
+  // URL.
   std::unique_ptr<net::test_server::HttpResponse> HandleRedirectRequest(
       const net::test_server::HttpRequest& request) const {
-    auto redirect_it = redirects_.find(request.GetURL());
+    std::map<GURL, RedirectEdge>::const_iterator redirect_it =
+        redirects_.find(request.GetURL());
     if (redirect_it == redirects_.end())
       return nullptr;
 
@@ -623,7 +685,7 @@
               net::HIGHEST);
   // https://www.w3.org/TR/2014/REC-html5-20141028/scripting-1.html#the-script-element
   // Script elements don't execute when inserted using innerHTML attribute.
-  AddUnrecordedResources({GetURL(kScriptPath)});
+  AddUnobservableResources({GetURL(kScriptPath)});
   TestLearningAndPrefetching(GetURL(kHtmlInnerHtmlPath));
 }
 
@@ -653,9 +715,10 @@
   AddResource(GetURL(kStylePath2), content::RESOURCE_TYPE_STYLESHEET,
               net::HIGHEST);
   AddResource(GetURL(kScriptPath2), content::RESOURCE_TYPE_SCRIPT, net::MEDIUM);
-  // Included from <iframe src="html_subresources.html"> and not recored.
-  AddUnrecordedResources({GetURL(kImagePath), GetURL(kStylePath),
-                          GetURL(kScriptPath), GetURL(kFontPath)});
+  // Included from <iframe src="html_subresources.html"> and shouldn't be
+  // observed.
+  AddUnobservableResources({GetURL(kImagePath), GetURL(kStylePath),
+                            GetURL(kScriptPath), GetURL(kFontPath)});
   TestLearningAndPrefetching(GetURL(kHtmlIframePath));
 }
 
@@ -718,4 +781,20 @@
   EXPECT_EQ(navigation_ids_history_size(), 4U);
 }
 
+IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, AlwaysRevalidate) {
+  std::vector<ResourceSummary*> resources = {
+      AddResource(GetURL(kImagePath), content::RESOURCE_TYPE_IMAGE,
+                  net::LOWEST),
+      AddResource(GetURL(kStylePath), content::RESOURCE_TYPE_STYLESHEET,
+                  net::HIGHEST),
+      AddResource(GetURL(kScriptPath), content::RESOURCE_TYPE_SCRIPT,
+                  net::MEDIUM),
+      AddResource(GetURL(kFontPath), content::RESOURCE_TYPE_FONT_RESOURCE,
+                  net::HIGHEST),
+  };
+  for (auto& resource : resources)
+    resource->request.always_revalidate = true;
+  TestLearningAndPrefetching(GetURL(kHtmlSubresourcesPath));
+}
+
 }  // namespace predictors
diff --git a/chrome/browser/ui/app_list/arc/arc_app_test.cc b/chrome/browser/ui/app_list/arc/arc_app_test.cc
index 1a9d9581..0d21c988 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_test.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_test.cc
@@ -79,12 +79,10 @@
     ArcAppListPrefsFactory::GetInstance()->RecreateServiceInstanceForTesting(
         profile_);
   }
-  arc::ArcServiceManager::SetArcSessionRunnerForTesting(
-      base::MakeUnique<arc::ArcSessionRunner>(
-          base::Bind(arc::FakeArcSession::Create)));
   arc_service_manager_ = base::MakeUnique<arc::ArcServiceManager>(nullptr);
   arc_session_manager_ = base::MakeUnique<arc::ArcSessionManager>(
-      arc_service_manager_->arc_bridge_service());
+      base::MakeUnique<arc::ArcSessionRunner>(
+          base::Bind(arc::FakeArcSession::Create)));
   DCHECK(arc::ArcSessionManager::Get());
   arc::ArcSessionManager::DisableUIForTesting();
   arc_session_manager_->OnPrimaryUserProfilePrepared(profile_);
@@ -96,12 +94,12 @@
   run_loop.Run();
 
   arc_session_manager_->EnableArc();
+  // Check initial conditions.
+  EXPECT_FALSE(arc_session_manager_->IsSessionRunning());
+
   app_instance_.reset(new arc::FakeAppInstance(arc_app_list_pref_));
   arc_service_manager_->arc_bridge_service()->app()->SetInstance(
       app_instance_.get());
-
-  // Check initial conditions.
-  EXPECT_FALSE(arc_service_manager_->arc_bridge_service()->ready());
 }
 
 void ArcAppTest::CreateFakeAppsAndPackages() {
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_ash_unittest.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_ash_unittest.cc
index c11611a..49efa1f 100644
--- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_ash_unittest.cc
+++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_ash_unittest.cc
@@ -15,7 +15,6 @@
 #if defined(OS_CHROMEOS)
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/chromeos/arc/arc_session_manager.h"
-#include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_session_runner.h"
 #include "components/arc/test/fake_arc_session.h"
 #endif
@@ -36,12 +35,9 @@
     ash::test::AshTestBase::SetUp();
 #if defined(OS_CHROMEOS)
     arc::ArcSessionManager::DisableUIForTesting();
-    bridge_service_ = base::MakeUnique<arc::ArcBridgeService>();
-    bridge_service_->InitializeArcSessionRunner(
+    arc_session_manager_ = base::MakeUnique<arc::ArcSessionManager>(
         base::MakeUnique<arc::ArcSessionRunner>(
             base::Bind(arc::FakeArcSession::Create)));
-    arc_session_manager_ =
-        base::MakeUnique<arc::ArcSessionManager>(bridge_service_.get());
     arc_session_manager_->OnPrimaryUserProfilePrepared(
         extension_environment_.profile());
 #endif
@@ -68,7 +64,6 @@
   views::Widget* widget_ = nullptr;
   AppInfoDialog* dialog_ = nullptr;  // Owned by |widget_|'s views hierarchy.
 #if defined(OS_CHROMEOS)
-  std::unique_ptr<arc::ArcBridgeService> bridge_service_;
   std::unique_ptr<arc::ArcSessionManager> arc_session_manager_;
 #endif
 
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_views_unittest.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_views_unittest.cc
index 48028e28..73ad28d 100644
--- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_views_unittest.cc
+++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_views_unittest.cc
@@ -26,7 +26,6 @@
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/arc/arc_session_manager.h"
-#include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_session_runner.h"
 #include "components/arc/test/fake_arc_session.h"
 #endif
@@ -71,12 +70,9 @@
     BrowserWithTestWindowTest::SetUp();
 #if defined(OS_CHROMEOS)
     arc::ArcSessionManager::DisableUIForTesting();
-    bridge_service_ = base::MakeUnique<arc::ArcBridgeService>();
-    bridge_service_->InitializeArcSessionRunner(
+    arc_session_manager_ = base::MakeUnique<arc::ArcSessionManager>(
         base::MakeUnique<arc::ArcSessionRunner>(
             base::Bind(arc::FakeArcSession::Create)));
-    arc_session_manager_ =
-        base::MakeUnique<arc::ArcSessionManager>(bridge_service_.get());
     arc_session_manager_->OnPrimaryUserProfilePrepared(
         extension_environment_.profile());
 #endif
@@ -143,7 +139,6 @@
   scoped_refptr<extensions::Extension> extension_;
   extensions::TestExtensionEnvironment extension_environment_;
 #if defined(OS_CHROMEOS)
-  std::unique_ptr<arc::ArcBridgeService> bridge_service_;
   std::unique_ptr<arc::ArcSessionManager> arc_session_manager_;
 #endif
 
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 1c5cf88..0d51d3d 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-9122.0.0
\ No newline at end of file
+9125.0.0
\ No newline at end of file
diff --git a/components/arc/arc_bridge_service.cc b/components/arc/arc_bridge_service.cc
index 3044d1e..f17d3d6 100644
--- a/components/arc/arc_bridge_service.cc
+++ b/components/arc/arc_bridge_service.cc
@@ -11,7 +11,6 @@
 #include "base/sequenced_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chromeos/chromeos_switches.h"
-#include "components/arc/arc_session_runner.h"
 
 namespace arc {
 
@@ -38,45 +37,4 @@
   return command_line->HasSwitch(chromeos::switches::kArcAvailable);
 }
 
-void ArcBridgeService::InitializeArcSessionRunner(
-    std::unique_ptr<ArcSessionRunner> arc_session_runner) {
-  DCHECK(!arc_session_runner_);
-  arc_session_runner_ = std::move(arc_session_runner);
-}
-
-void ArcBridgeService::AddObserver(ArcSessionObserver* observer) {
-  DCHECK(arc_session_runner_);
-  arc_session_runner_->AddObserver(observer);
-}
-
-void ArcBridgeService::RemoveObserver(ArcSessionObserver* observer) {
-  DCHECK(arc_session_runner_);
-  arc_session_runner_->RemoveObserver(observer);
-}
-
-void ArcBridgeService::RequestStart() {
-  DCHECK(arc_session_runner_);
-  arc_session_runner_->RequestStart();
-}
-
-void ArcBridgeService::RequestStop() {
-  DCHECK(arc_session_runner_);
-  arc_session_runner_->RequestStop();
-}
-
-void ArcBridgeService::OnShutdown() {
-  DCHECK(arc_session_runner_);
-  arc_session_runner_->OnShutdown();
-}
-
-bool ArcBridgeService::ready() const {
-  DCHECK(arc_session_runner_);
-  return arc_session_runner_->IsRunning();
-}
-
-bool ArcBridgeService::stopped() const {
-  DCHECK(arc_session_runner_);
-  return arc_session_runner_->IsStopped();
-}
-
 }  // namespace arc
diff --git a/components/arc/arc_bridge_service.h b/components/arc/arc_bridge_service.h
index b74bea6a..3f4a5f5a3 100644
--- a/components/arc/arc_bridge_service.h
+++ b/components/arc/arc_bridge_service.h
@@ -48,41 +48,19 @@
 
 }  // namespace mojom
 
-class ArcSessionObserver;
-class ArcSessionRunner;
-
-// The Chrome-side service that handles ARC instances and ARC bridge creation.
-// This service handles the lifetime of ARC instances and sets up the
-// communication channel (the ARC bridge) used to send and receive messages.
+// Holds Mojo channels which proxy to ARC side implementation. The actual
+// instances are set/removed via ArcBridgeHostImpl.
 class ArcBridgeService {
  public:
   ArcBridgeService();
-  virtual ~ArcBridgeService();
+  ~ArcBridgeService();
 
-  // Return true if ARC has been enabled through a commandline
-  // switch.
+  // Returns true if ARC has been enabled through a commandline switch.
   static bool GetEnabled(const base::CommandLine* command_line);
 
-  // Return true if ARC is available on the current board.
+  // Returns true if ARC is available on the current board.
   static bool GetAvailable(const base::CommandLine* command_line);
 
-  // Initializes the ArcSessionRunner with the given instance.
-  // This must be called before following proxy methods.
-  void InitializeArcSessionRunner(
-      std::unique_ptr<ArcSessionRunner> arc_session_runner);
-
-  // Proxies the method to ArcSessionRunner. See details in the
-  // ArcSessionRunner's comment.
-  // TODO(hidehiko): Move the ownership from ArcBridgeService to
-  // ArcSessionManager, and remove these methods.
-  void AddObserver(ArcSessionObserver* observer);
-  void RemoveObserver(ArcSessionObserver* observer);
-  void RequestStart();
-  void RequestStop();
-  void OnShutdown();
-  bool ready() const;
-  bool stopped() const;
-
   InstanceHolder<mojom::AppInstance>* app() { return &app_; }
   InstanceHolder<mojom::AudioInstance>* audio() { return &audio_; }
   InstanceHolder<mojom::AuthInstance>* auth() { return &auth_; }
@@ -125,7 +103,6 @@
   InstanceHolder<mojom::WallpaperInstance>* wallpaper() { return &wallpaper_; }
 
  private:
-  // Instance holders.
   InstanceHolder<mojom::AppInstance> app_;
   InstanceHolder<mojom::AudioInstance> audio_;
   InstanceHolder<mojom::AuthInstance> auth_;
@@ -151,8 +128,6 @@
   InstanceHolder<mojom::VideoInstance> video_;
   InstanceHolder<mojom::WallpaperInstance> wallpaper_;
 
-  std::unique_ptr<ArcSessionRunner> arc_session_runner_;
-
   DISALLOW_COPY_AND_ASSIGN(ArcBridgeService);
 };
 
diff --git a/components/arc/arc_service_manager.cc b/components/arc/arc_service_manager.cc
index df5250c..9204fef 100644
--- a/components/arc/arc_service_manager.cc
+++ b/components/arc/arc_service_manager.cc
@@ -20,9 +20,6 @@
 // Weak pointer.  This class is owned by arc::ArcServiceLauncher.
 ArcServiceManager* g_arc_service_manager = nullptr;
 
-// This pointer is owned by ArcServiceManager.
-ArcSessionRunner* g_arc_session_runner_for_testing = nullptr;
-
 }  // namespace
 
 class ArcServiceManager::IntentHelperObserverImpl
@@ -52,30 +49,17 @@
     scoped_refptr<base::TaskRunner> blocking_task_runner)
     : blocking_task_runner_(blocking_task_runner),
       intent_helper_observer_(base::MakeUnique<IntentHelperObserverImpl>(this)),
+      arc_bridge_service_(base::MakeUnique<ArcBridgeService>()),
       icon_loader_(new ActivityIconLoader()),
       activity_resolver_(new LocalActivityResolver()) {
   DCHECK(!g_arc_service_manager);
   g_arc_service_manager = this;
-
-  arc_bridge_service_ = base::MakeUnique<ArcBridgeService>();
-  if (g_arc_session_runner_for_testing) {
-    arc_bridge_service_->InitializeArcSessionRunner(
-        base::WrapUnique(g_arc_session_runner_for_testing));
-    g_arc_session_runner_for_testing = nullptr;
-  } else {
-    arc_bridge_service_->InitializeArcSessionRunner(
-        base::MakeUnique<ArcSessionRunner>(base::Bind(&ArcSession::Create,
-                                                      arc_bridge_service_.get(),
-                                                      blocking_task_runner)));
-  }
 }
 
 ArcServiceManager::~ArcServiceManager() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(g_arc_service_manager == this);
+  DCHECK_EQ(g_arc_service_manager, this);
   g_arc_service_manager = nullptr;
-  if (g_arc_session_runner_for_testing)
-    delete g_arc_session_runner_for_testing;
 }
 
 // static
@@ -116,15 +100,6 @@
   icon_loader_ = nullptr;
   activity_resolver_ = nullptr;
   services_.clear();
-  arc_bridge_service_->OnShutdown();
-}
-
-// static
-void ArcServiceManager::SetArcSessionRunnerForTesting(
-    std::unique_ptr<ArcSessionRunner> arc_session_runner) {
-  if (g_arc_session_runner_for_testing)
-    delete g_arc_session_runner_for_testing;
-  g_arc_session_runner_for_testing = arc_session_runner.release();
 }
 
 }  // namespace arc
diff --git a/components/arc/arc_service_manager.h b/components/arc/arc_service_manager.h
index 68df26a..81a4ea9 100644
--- a/components/arc/arc_service_manager.h
+++ b/components/arc/arc_service_manager.h
@@ -21,7 +21,6 @@
 class ArcBridgeService;
 class ArcIntentHelperObserver;
 class ArcService;
-class ArcSessionRunner;
 
 // Manages creation and destruction of services that communicate with the ARC
 // instance via the ArcBridgeService.
@@ -66,11 +65,6 @@
     return blocking_task_runner_;
   }
 
-  // Set ArcSessionRunner instance for testing. Call before ArcServiceManager
-  // creation.
-  static void SetArcSessionRunnerForTesting(
-      std::unique_ptr<ArcSessionRunner> arc_session_runner);
-
   // Returns the icon loader owned by ArcServiceManager and shared by services.
   scoped_refptr<ActivityIconLoader> icon_loader() { return icon_loader_; }
 
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.cc b/content/browser/service_worker/service_worker_controllee_request_handler.cc
index 7a1626a..af61d9c 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.cc
@@ -420,10 +420,18 @@
   DCHECK(job_.get());
   DCHECK(context_);
 
+  // When this request handler was created, the provider host had a controller
+  // and hence an active version, but by the time MaybeCreateJob() is called
+  // the active version may have been lost. This happens when
+  // ServiceWorkerRegistration::DeleteVersion() was called to delete the worker
+  // because a permanent failure occurred when trying to start it.
+  //
+  // As this is an exceptional case, just error out.
+  // TODO(falken): Figure out if |active_version| can change to
+  // |controlling_version| and do it or document the findings.
   if (!provider_host_->active_version()) {
-    // TODO(falken): For debugging. Remove once https://crbug.com/655910 is
-    // resolved.
-    CHECK(provider_host_->controller_was_deleted());
+    job_->FailDueToLostController();
+    return;
   }
 
   MaybeForwardToServiceWorker(job_.get(), provider_host_->active_version());
@@ -462,7 +470,7 @@
 void ServiceWorkerControlleeRequestHandler::MainResourceLoadFailed() {
   DCHECK(provider_host_);
   // Detach the controller so subresource requests also skip the worker.
-  provider_host_->NotifyControllerLost(false /* was_deleted */);
+  provider_host_->NotifyControllerLost();
 }
 
 void ServiceWorkerControlleeRequestHandler::ClearJob() {
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc b/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
index 17d580e1..8132f4d9 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
@@ -301,6 +301,48 @@
   EXPECT_FALSE(sw_job->ShouldForwardToServiceWorker());
 }
 
+// Tests the scenario where a controllee request handler was created
+// for a subresource request, but before MaybeCreateJob() is run, the
+// controller/active version becomes null.
+TEST_P(ServiceWorkerControlleeRequestHandlerTestP, LostActiveVersion) {
+  // Store an activated worker.
+  version_->set_fetch_handler_existence(
+      ServiceWorkerVersion::FetchHandlerExistence::EXISTS);
+  version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
+  registration_->SetActiveVersion(version_);
+  context()->storage()->StoreRegistration(
+      registration_.get(), version_.get(),
+      base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
+  base::RunLoop().RunUntilIdle();
+
+  // Conduct a main resource load to set the controller.
+  ServiceWorkerRequestTestResources main_test_resources(
+      this, GURL("https://host/scope/doc"), RESOURCE_TYPE_MAIN_FRAME);
+  main_test_resources.MaybeCreateJob();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(version_->HasControllee());
+  EXPECT_EQ(version_, provider_host_->controlling_version());
+  EXPECT_EQ(version_, provider_host_->active_version());
+
+  // Unset the active version.
+  provider_host_->NotifyControllerLost();
+  registration_->SetActiveVersion(nullptr);
+  EXPECT_FALSE(version_->HasControllee());
+  EXPECT_FALSE(provider_host_->controlling_version());
+  EXPECT_FALSE(provider_host_->active_version());
+
+  // Conduct a subresource load.
+  ServiceWorkerRequestTestResources sub_test_resources(
+      this, GURL("https://host/scope/doc"), RESOURCE_TYPE_IMAGE);
+  ServiceWorkerURLRequestJob* sub_job = sub_test_resources.MaybeCreateJob();
+  base::RunLoop().RunUntilIdle();
+
+  // Verify that the job errored.
+  EXPECT_EQ(
+      ServiceWorkerURLRequestJob::ResponseType::FAIL_DUE_TO_LOST_CONTROLLER,
+      sub_job->response_type_);
+}
+
 TEST_P(ServiceWorkerControlleeRequestHandlerTestP, FallbackWithNoFetchHandler) {
   version_->set_fetch_handler_existence(
       ServiceWorkerVersion::FetchHandlerExistence::DOES_NOT_EXIST);
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index 92c46ff4e..a32fdd3 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -118,8 +118,7 @@
       parent_frame_security_level_(parent_frame_security_level),
       context_(context),
       dispatcher_host_(dispatcher_host),
-      allow_association_(true),
-      controller_was_deleted_(false) {
+      allow_association_(true) {
   DCHECK_NE(SERVICE_WORKER_PROVIDER_UNKNOWN, provider_type_);
 
   // PlzNavigate
@@ -211,9 +210,7 @@
   if (!controlling_version_)
     return;
   ServiceWorkerVersion* active_version = registration->active_version();
-  // TODO(falken): Change to DCHECK once https://crbug.com/655910 is
-  // resolved.
-  CHECK(active_version);
+  DCHECK(active_version);
   DCHECK_EQ(active_version->status(), ServiceWorkerVersion::ACTIVATING);
   SetControllerVersionAttribute(active_version,
                                 true /* notify_controllerchange */);
@@ -360,9 +357,7 @@
   return nullptr;
 }
 
-void ServiceWorkerProviderHost::NotifyControllerLost(bool was_deleted) {
-  if (was_deleted)
-    controller_was_deleted_ = true;
+void ServiceWorkerProviderHost::NotifyControllerLost() {
   SetControllerVersionAttribute(nullptr, true /* notify_controllerchange */);
 }
 
@@ -472,8 +467,7 @@
 
 void ServiceWorkerProviderHost::ClaimedByRegistration(
     ServiceWorkerRegistration* registration) {
-  // TODO(falken): Change to DCHECK once https://crbug.com/655910 is resolved.
-  CHECK(registration->active_version());
+  DCHECK(registration->active_version());
   if (registration == associated_registration_) {
     SetControllerVersionAttribute(registration->active_version(),
                                   true /* notify_controllerchange */);
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index 35ed99d..4879e4f 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -122,29 +122,53 @@
     return running_hosted_version_.get() != NULL;
   }
 
+  // Returns this provider's controller. The controller is typically the same as
+  // active_version() but can differ in the following cases:
+  // (1) The client was created before the registration existed or had an active
+  // version (in spec language, it is not "using" the registration).
+  // (2) The client had a controller but NotifyControllerLost() was called due
+  // to an exceptional circumstance (here also it is not "using" the
+  // registration).
+  // (3) During algorithms such as the update, skipWaiting(), and claim() steps,
+  // the active_version and controlling_version may temporarily differ. For
+  // example, to perform skipWaiting(), the registration's active version is
+  // updated first and then the provider host's controlling version is updated
+  // to match it.
   ServiceWorkerVersion* controlling_version() const {
+    // Only clients can have controllers.
+    DCHECK(!controlling_version_ || IsProviderForClient());
     return controlling_version_.get();
   }
+
   ServiceWorkerVersion* active_version() const {
     return associated_registration_.get() ?
         associated_registration_->active_version() : NULL;
   }
+
   ServiceWorkerVersion* waiting_version() const {
     return associated_registration_.get() ?
         associated_registration_->waiting_version() : NULL;
   }
+
   ServiceWorkerVersion* installing_version() const {
     return associated_registration_.get() ?
         associated_registration_->installing_version() : NULL;
   }
 
+  // Returns the associated registration. The provider host listens to this
+  // registration to resolve the .ready promise and set its controller.
   ServiceWorkerRegistration* associated_registration() const {
+    // Only clients can have an associated registration.
+    DCHECK(!associated_registration_ || IsProviderForClient());
     return associated_registration_.get();
   }
 
   // The running version, if any, that this provider is providing resource
   // loads for.
   ServiceWorkerVersion* running_hosted_version() const {
+    // Only providers for controllers can host a running version.
+    DCHECK(!running_hosted_version_ ||
+           provider_type_ == SERVICE_WORKER_PROVIDER_FOR_CONTROLLER);
     return running_hosted_version_.get();
   }
 
@@ -268,8 +292,7 @@
   // Called when our controller has been terminated and doomed due to an
   // exceptional condition like it could no longer be read from the script
   // cache.
-  void NotifyControllerLost(bool was_deleted);
-  bool controller_was_deleted() { return controller_was_deleted_; }
+  void NotifyControllerLost();
 
  private:
   friend class ForeignFetchRequestHandlerTest;
@@ -390,8 +413,6 @@
   base::WeakPtr<ServiceWorkerContextCore> context_;
   ServiceWorkerDispatcherHost* dispatcher_host_;
   bool allow_association_;
-  // TODO(falken): Remove once https://crbug.com/655910 is resolved.
-  bool controller_was_deleted_;
 
   std::vector<base::Closure> queued_events_;
 
diff --git a/content/browser/service_worker/service_worker_registration.cc b/content/browser/service_worker/service_worker_registration.cc
index e993a27..de9f481 100644
--- a/content/browser/service_worker/service_worker_registration.cc
+++ b/content/browser/service_worker/service_worker_registration.cc
@@ -358,7 +358,7 @@
        !it->IsAtEnd(); it->Advance()) {
     ServiceWorkerProviderHost* host = it->GetProviderHost();
     if (host->controlling_version() == version)
-      host->NotifyControllerLost(true /* was_deleted */);
+      host->NotifyControllerLost();
   }
 
   version->Doom();
diff --git a/content/browser/service_worker/service_worker_url_request_job.cc b/content/browser/service_worker/service_worker_url_request_job.cc
index 05b69ed..da9a6f9 100644
--- a/content/browser/service_worker/service_worker_url_request_job.cc
+++ b/content/browser/service_worker/service_worker_url_request_job.cc
@@ -297,6 +297,12 @@
   MaybeStartRequest();
 }
 
+void ServiceWorkerURLRequestJob::FailDueToLostController() {
+  DCHECK_EQ(NOT_DETERMINED, response_type_);
+  response_type_ = FAIL_DUE_TO_LOST_CONTROLLER;
+  MaybeStartRequest();
+}
+
 void ServiceWorkerURLRequestJob::Start() {
   is_started_ = true;
   MaybeStartRequest();
@@ -427,6 +433,12 @@
       NOTREACHED();
       return;
 
+    case FAIL_DUE_TO_LOST_CONTROLLER:
+      request()->net_log().AddEvent(
+          net::NetLogEventType::SERVICE_WORKER_ERROR_NO_ACTIVE_VERSION);
+      NotifyStartError(net::URLRequestStatus::FromError(net::ERR_FAILED));
+      return;
+
     case FALLBACK_TO_NETWORK:
       FinalizeFallbackToNetwork();
       return;
@@ -792,30 +804,41 @@
 }
 
 void ServiceWorkerURLRequestJob::OnStartCompleted() const {
-  if (response_type_ != FORWARD_TO_SERVICE_WORKER &&
-      response_type_ != FALLBACK_TO_RENDERER) {
-    ServiceWorkerResponseInfo::ForRequest(request_, true)
-        ->OnStartCompleted(
-            false /* was_fetched_via_service_worker */,
-            false /* was_fetched_via_foreign_fetch */,
-            false /* was_fallback_required */,
-            std::vector<GURL>() /* url_list_via_service_worker */,
-            blink::WebServiceWorkerResponseTypeDefault,
-            base::TimeTicks() /* service_worker_start_time */,
-            base::TimeTicks() /* service_worker_ready_time */,
-            false /* respons_is_in_cache_storage */,
-            std::string() /* response_cache_storage_cache_name */,
-            ServiceWorkerHeaderList() /* cors_exposed_header_names */);
-    return;
+  switch (response_type_) {
+    case NOT_DETERMINED:
+      NOTREACHED();
+      return;
+    case FAIL_DUE_TO_LOST_CONTROLLER:
+    case FALLBACK_TO_NETWORK:
+      // Indicate that the service worker did not respond to the request.
+      ServiceWorkerResponseInfo::ForRequest(request_, true)
+          ->OnStartCompleted(
+              false /* was_fetched_via_service_worker */,
+              false /* was_fetched_via_foreign_fetch */,
+              false /* was_fallback_required */,
+              std::vector<GURL>() /* url_list_via_service_worker */,
+              blink::WebServiceWorkerResponseTypeDefault,
+              base::TimeTicks() /* service_worker_start_time */,
+              base::TimeTicks() /* service_worker_ready_time */,
+              false /* response_is_in_cache_storage */,
+              std::string() /* response_cache_storage_cache_name */,
+              ServiceWorkerHeaderList() /* cors_exposed_header_names */);
+      break;
+    case FALLBACK_TO_RENDERER:
+    case FORWARD_TO_SERVICE_WORKER:
+      // Indicate that the service worker responded to the request, which is
+      // considered true if "fallback to renderer" was required since the
+      // renderer expects that.
+      ServiceWorkerResponseInfo::ForRequest(request_, true)
+          ->OnStartCompleted(
+              true /* was_fetched_via_service_worker */,
+              fetch_type_ == ServiceWorkerFetchType::FOREIGN_FETCH,
+              fall_back_required_, response_url_list_,
+              service_worker_response_type_, worker_start_time_,
+              worker_ready_time_, response_is_in_cache_storage_,
+              response_cache_storage_cache_name_, cors_exposed_header_names_);
+      break;
   }
-  ServiceWorkerResponseInfo::ForRequest(request_, true)
-      ->OnStartCompleted(true /* was_fetched_via_service_worker */,
-                         fetch_type_ == ServiceWorkerFetchType::FOREIGN_FETCH,
-                         fall_back_required_, response_url_list_,
-                         service_worker_response_type_, worker_start_time_,
-                         worker_ready_time_, response_is_in_cache_storage_,
-                         response_cache_storage_cache_name_,
-                         cors_exposed_header_names_);
 }
 
 bool ServiceWorkerURLRequestJob::IsMainResourceLoad() const {
diff --git a/content/browser/service_worker/service_worker_url_request_job.h b/content/browser/service_worker/service_worker_url_request_job.h
index 5eb8f00..89703fe 100644
--- a/content/browser/service_worker/service_worker_url_request_job.h
+++ b/content/browser/service_worker/service_worker_url_request_job.h
@@ -114,6 +114,10 @@
   void FallbackToNetwork();
   void FallbackToNetworkOrRenderer();
   void ForwardToServiceWorker();
+  // Tells the job to abort with a start error. Currently this is only called
+  // because the controller was lost. This function could be made more generic
+  // if needed later.
+  void FailDueToLostController();
 
   bool ShouldFallbackToNetwork() const {
     return response_type_ == FALLBACK_TO_NETWORK;
@@ -145,12 +149,15 @@
 
  private:
   class FileSizeResolver;
+  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerControlleeRequestHandlerTestP,
+                           LostActiveVersion);
 
   enum ResponseType {
     NOT_DETERMINED,
+    FAIL_DUE_TO_LOST_CONTROLLER,
     FALLBACK_TO_NETWORK,
     FALLBACK_TO_RENDERER,  // Use this when falling back with CORS check
-    FORWARD_TO_SERVICE_WORKER,
+    FORWARD_TO_SERVICE_WORKER
   };
 
   enum ResponseBodyType {
diff --git a/content/browser/service_worker/service_worker_url_request_job_unittest.cc b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
index 6e518ae..0ce78e4 100644
--- a/content/browser/service_worker/service_worker_url_request_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
@@ -291,7 +291,7 @@
   void MainResourceLoadFailed() override {
     CHECK(provider_host_);
     // Detach the controller so subresource requests also skip the worker.
-    provider_host_->NotifyControllerLost(false /* was_deleted */);
+    provider_host_->NotifyControllerLost();
   }
 
   // Runs a request where the active worker starts a request in ACTIVATING state
diff --git a/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc b/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
index 9215ade..d1e2328f2 100644
--- a/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
@@ -293,7 +293,7 @@
     std::unique_ptr<ServiceWorkerProviderHost> host(
         new ServiceWorkerProviderHost(
             process_id, MSG_ROUTING_NONE, provider_id,
-            SERVICE_WORKER_PROVIDER_FOR_WORKER,
+            SERVICE_WORKER_PROVIDER_FOR_CONTROLLER,
             ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
             context()->AsWeakPtr(), nullptr));
     base::WeakPtr<ServiceWorkerProviderHost> provider_host = host->AsWeakPtr();
diff --git a/content/browser/storage_partition_impl.h b/content/browser/storage_partition_impl.h
index 50100a9..8e9e914 100644
--- a/content/browser/storage_partition_impl.h
+++ b/content/browser/storage_partition_impl.h
@@ -73,9 +73,9 @@
   HostZoomLevelContext* GetHostZoomLevelContext() override;
   ZoomLevelDelegate* GetZoomLevelDelegate() override;
   PlatformNotificationContextImpl* GetPlatformNotificationContext() override;
+  PaymentAppContextImpl* GetPaymentAppContext() override;
 
   BackgroundSyncContext* GetBackgroundSyncContext();
-  PaymentAppContextImpl* GetPaymentAppContext();
   BroadcastChannelProvider* GetBroadcastChannelProvider();
 
   // mojom::StoragePartitionService interface.
diff --git a/content/child/resource_dispatcher.cc b/content/child/resource_dispatcher.cc
index 3cbc9ff..24afd8a 100644
--- a/content/child/resource_dispatcher.cc
+++ b/content/child/resource_dispatcher.cc
@@ -483,9 +483,14 @@
   }
   if (value) {
     request_info->is_deferred = value;
+    if (request_info->url_loader_client)
+      request_info->url_loader_client->SetDefersLoading();
   } else if (request_info->is_deferred) {
     request_info->is_deferred = false;
 
+    if (request_info->url_loader_client)
+      request_info->url_loader_client->UnsetDefersLoading();
+
     FollowPendingRedirect(request_id, request_info);
 
     main_thread_task_runner_->PostTask(
@@ -553,6 +558,13 @@
   PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
   if (!request_info || request_info->is_deferred)
     return;
+
+  if (request_info->url_loader) {
+    DCHECK(request_info->deferred_message_queue.empty());
+    request_info->url_loader_client->FlushDeferredMessages();
+    return;
+  }
+
   // Because message handlers could result in request_info being destroyed,
   // we need to work with a stack reference to the deferred queue.
   MessageQueue q;
diff --git a/content/child/resource_dispatcher.h b/content/child/resource_dispatcher.h
index 39557b2..8e4c212 100644
--- a/content/child/resource_dispatcher.h
+++ b/content/child/resource_dispatcher.h
@@ -153,6 +153,7 @@
   void OnTransferSizeUpdated(int request_id, int32_t transfer_size_diff);
 
  private:
+  friend class URLLoaderClientImpl;
   friend class URLResponseBodyConsumer;
   friend class ResourceDispatcherTest;
 
diff --git a/content/child/test_request_peer.cc b/content/child/test_request_peer.cc
index 25200d53..47add960 100644
--- a/content/child/test_request_peer.cc
+++ b/content/child/test_request_peer.cc
@@ -68,6 +68,8 @@
   if (context_->cancelled)
     return;
   context_->total_encoded_data_length += transfer_size_diff;
+  if (context_->defer_on_transfer_size_updated)
+    dispatcher_->SetDefersLoading(context_->request_id, true);
 }
 
 void TestRequestPeer::OnCompletedRequest(int error_code,
diff --git a/content/child/test_request_peer.h b/content/child/test_request_peer.h
index 81162ac..e076b0d 100644
--- a/content/child/test_request_peer.h
+++ b/content/child/test_request_peer.h
@@ -65,6 +65,8 @@
     // Total encoded data length, regardless of whether downloading to a file or
     // not.
     int total_encoded_data_length = 0;
+    bool defer_on_transfer_size_updated = false;
+
     // Total length when downloading to a file.
     int total_downloaded_data_length = 0;
 
diff --git a/content/child/url_loader_client_impl.cc b/content/child/url_loader_client_impl.cc
index b9305043..04aee61 100644
--- a/content/child/url_loader_client_impl.cc
+++ b/content/child/url_loader_client_impl.cc
@@ -21,7 +21,8 @@
     : binding_(this),
       request_id_(request_id),
       resource_dispatcher_(resource_dispatcher),
-      task_runner_(std::move(task_runner)) {}
+      task_runner_(std::move(task_runner)),
+      weak_factory_(this) {}
 
 URLLoaderClientImpl::~URLLoaderClientImpl() {
   if (body_consumer_)
@@ -34,6 +35,81 @@
   binding_.Bind(client_ptr_info, associated_group);
 }
 
+void URLLoaderClientImpl::SetDefersLoading() {
+  is_deferred_ = true;
+  if (body_consumer_)
+    body_consumer_->SetDefersLoading();
+}
+
+void URLLoaderClientImpl::UnsetDefersLoading() {
+  is_deferred_ = false;
+}
+
+void URLLoaderClientImpl::FlushDeferredMessages() {
+  DCHECK(!is_deferred_);
+  std::vector<IPC::Message> messages;
+  messages.swap(deferred_messages_);
+  bool has_completion_message = false;
+  base::WeakPtr<URLLoaderClientImpl> weak_this = weak_factory_.GetWeakPtr();
+  // First, dispatch all messages excluding the followings:
+  //  - response body (dispatched by |body_consumer_|)
+  //  - transfer size change (dispatched later)
+  //  - completion (dispatched by |body_consumer_| or dispatched later)
+  for (size_t index = 0; index < messages.size(); ++index) {
+    if (messages[index].type() == ResourceMsg_RequestComplete::ID) {
+      // The completion message arrives at the end of the message queue.
+      DCHECK(!has_completion_message);
+      DCHECK_EQ(index, messages.size() - 1);
+      has_completion_message = true;
+      break;
+    }
+    Dispatch(messages[index]);
+    if (!weak_this)
+      return;
+    if (is_deferred_) {
+      deferred_messages_.insert(deferred_messages_.begin(),
+                                messages.begin() + index + 1, messages.end());
+      return;
+    }
+  }
+
+  // Dispatch the transfer size update.
+  if (accumulated_transfer_size_diff_during_deferred_ > 0) {
+    auto transfer_size_diff = accumulated_transfer_size_diff_during_deferred_;
+    accumulated_transfer_size_diff_during_deferred_ = 0;
+    resource_dispatcher_->OnTransferSizeUpdated(request_id_,
+                                                transfer_size_diff);
+    if (!weak_this)
+      return;
+    if (is_deferred_) {
+      if (has_completion_message) {
+        DCHECK_GT(messages.size(), 0u);
+        DCHECK_EQ(messages.back().type(),
+                  static_cast<uint32_t>(ResourceMsg_RequestComplete::ID));
+        deferred_messages_.emplace_back(std::move(messages.back()));
+      }
+      return;
+    }
+  }
+
+  if (body_consumer_) {
+    // When we have |body_consumer_|, the completion message is dispatched by
+    // it, not by this object.
+    DCHECK(!has_completion_message);
+    // Dispatch the response body.
+    body_consumer_->UnsetDefersLoading();
+    return;
+  }
+
+  // Dispatch the completion message.
+  if (has_completion_message) {
+    DCHECK_GT(messages.size(), 0u);
+    DCHECK_EQ(messages.back().type(),
+              static_cast<uint32_t>(ResourceMsg_RequestComplete::ID));
+    Dispatch(messages.back());
+  }
+}
+
 void URLLoaderClientImpl::OnReceiveResponse(
     const ResourceResponseHead& response_head,
     mojom::DownloadedTempFilePtr downloaded_file) {
@@ -59,8 +135,12 @@
 }
 
 void URLLoaderClientImpl::OnTransferSizeUpdated(int32_t transfer_size_diff) {
-  resource_dispatcher_->OnTransferSizeUpdated(request_id_,
-                                              transfer_size_diff);
+  if (is_deferred_) {
+    accumulated_transfer_size_diff_during_deferred_ += transfer_size_diff;
+  } else {
+    resource_dispatcher_->OnTransferSizeUpdated(request_id_,
+                                                transfer_size_diff);
+  }
 }
 
 void URLLoaderClientImpl::OnStartLoadingResponseBody(
@@ -70,6 +150,8 @@
       request_id_, resource_dispatcher_, std::move(body), task_runner_);
   if (has_received_response_)
     body_consumer_->Start();
+  if (is_deferred_)
+    body_consumer_->SetDefersLoading();
 }
 
 void URLLoaderClientImpl::OnComplete(
@@ -82,7 +164,14 @@
 }
 
 void URLLoaderClientImpl::Dispatch(const IPC::Message& message) {
-  resource_dispatcher_->OnMessageReceived(message);
+  if (is_deferred_) {
+    deferred_messages_.push_back(message);
+  } else if (deferred_messages_.size() > 0) {
+    deferred_messages_.push_back(message);
+    FlushDeferredMessages();
+  } else {
+    resource_dispatcher_->DispatchMessage(message);
+  }
 }
 
 }  // namespace content
diff --git a/content/child/url_loader_client_impl.h b/content/child/url_loader_client_impl.h
index 3710c07..ada64af 100644
--- a/content/child/url_loader_client_impl.h
+++ b/content/child/url_loader_client_impl.h
@@ -6,8 +6,10 @@
 #define CONTENT_CHILD_URL_LOADER_CLIENT_IMPL_H_
 
 #include <stdint.h>
+#include <vector>
 #include "base/callback_forward.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "content/common/content_export.h"
 #include "content/common/url_loader.mojom.h"
 #include "ipc/ipc_message.h"
@@ -42,6 +44,16 @@
   void Bind(mojom::URLLoaderClientAssociatedPtrInfo* client_ptr_info,
             mojo::AssociatedGroup* associated_group);
 
+  // Sets |is_deferred_|. From now, the received messages are not dispatched
+  // to clients until UnsetDefersLoading is called.
+  void SetDefersLoading();
+
+  // Unsets |is_deferred_|.
+  void UnsetDefersLoading();
+
+  // Disaptches the messages received after SetDefersLoading is called.
+  void FlushDeferredMessages();
+
   // mojom::URLLoaderClient implementation
   void OnReceiveResponse(const ResourceResponseHead& response_head,
                          mojom::DownloadedTempFilePtr downloaded_file) override;
@@ -53,16 +65,20 @@
       mojo::ScopedDataPipeConsumerHandle body) override;
   void OnComplete(const ResourceRequestCompletionStatus& status) override;
 
-private:
+ private:
   void Dispatch(const IPC::Message& message);
 
   mojo::AssociatedBinding<mojom::URLLoaderClient> binding_;
   scoped_refptr<URLResponseBodyConsumer> body_consumer_;
   mojom::DownloadedTempFilePtr downloaded_file_;
+  std::vector<IPC::Message> deferred_messages_;
   const int request_id_;
   bool has_received_response_ = false;
+  bool is_deferred_ = false;
+  int32_t accumulated_transfer_size_diff_during_deferred_ = 0;
   ResourceDispatcher* const resource_dispatcher_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  base::WeakPtrFactory<URLLoaderClientImpl> weak_factory_;
 };
 
 }  // namespace content
diff --git a/content/child/url_loader_client_impl_unittest.cc b/content/child/url_loader_client_impl_unittest.cc
index cec32a7..a26febf 100644
--- a/content/child/url_loader_client_impl_unittest.cc
+++ b/content/child/url_loader_client_impl_unittest.cc
@@ -351,4 +351,221 @@
   EXPECT_TRUE(request_peer_context_.cancelled);
 }
 
+TEST_F(URLLoaderClientImplTest, Defer) {
+  ResourceResponseHead response_head;
+  ResourceRequestCompletionStatus completion_status;
+
+  url_loader_client_->OnReceiveResponse(response_head, nullptr);
+  url_loader_client_->OnComplete(completion_status);
+
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+
+  dispatcher_->SetDefersLoading(request_id_, true);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+
+  dispatcher_->SetDefersLoading(request_id_, false);
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_peer_context_.received_response);
+  EXPECT_TRUE(request_peer_context_.complete);
+}
+
+TEST_F(URLLoaderClientImplTest, DeferWithResponseBody) {
+  ResourceResponseHead response_head;
+  ResourceRequestCompletionStatus completion_status;
+
+  url_loader_client_->OnReceiveResponse(response_head, nullptr);
+  mojo::DataPipe data_pipe(DataPipeOptions());
+  uint32_t size = 5;
+  MojoResult result =
+      mojo::WriteDataRaw(data_pipe.producer_handle.get(), "hello", &size,
+                         MOJO_WRITE_DATA_FLAG_NONE);
+  ASSERT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(5u, size);
+  data_pipe.producer_handle.reset();
+
+  url_loader_client_->OnStartLoadingResponseBody(
+      std::move(data_pipe.consumer_handle));
+  url_loader_client_->OnComplete(completion_status);
+
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_EQ("", request_peer_context_.data);
+
+  dispatcher_->SetDefersLoading(request_id_, true);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_EQ("", request_peer_context_.data);
+
+  dispatcher_->SetDefersLoading(request_id_, false);
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_EQ("", request_peer_context_.data);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_peer_context_.received_response);
+  EXPECT_TRUE(request_peer_context_.complete);
+  EXPECT_EQ("hello", request_peer_context_.data);
+}
+
+// As "transfer size update" message is handled specially in the implementation,
+// we have a separate test.
+TEST_F(URLLoaderClientImplTest, DeferWithTransferSizeUpdated) {
+  ResourceResponseHead response_head;
+  ResourceRequestCompletionStatus completion_status;
+
+  url_loader_client_->OnReceiveResponse(response_head, nullptr);
+  mojo::DataPipe data_pipe(DataPipeOptions());
+  uint32_t size = 5;
+  MojoResult result =
+      mojo::WriteDataRaw(data_pipe.producer_handle.get(), "hello", &size,
+                         MOJO_WRITE_DATA_FLAG_NONE);
+  ASSERT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(5u, size);
+  data_pipe.producer_handle.reset();
+
+  url_loader_client_->OnStartLoadingResponseBody(
+      std::move(data_pipe.consumer_handle));
+  url_loader_client_->OnTransferSizeUpdated(4);
+  url_loader_client_->OnComplete(completion_status);
+
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_EQ("", request_peer_context_.data);
+  EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
+
+  dispatcher_->SetDefersLoading(request_id_, true);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_EQ("", request_peer_context_.data);
+  EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
+
+  dispatcher_->SetDefersLoading(request_id_, false);
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_EQ("", request_peer_context_.data);
+  EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_peer_context_.received_response);
+  EXPECT_TRUE(request_peer_context_.complete);
+  EXPECT_EQ("hello", request_peer_context_.data);
+  EXPECT_EQ(4, request_peer_context_.total_encoded_data_length);
+}
+
+TEST_F(URLLoaderClientImplTest, SetDeferredDuringFlushingDeferredMessage) {
+  request_peer_context_.defer_on_redirect = true;
+
+  net::RedirectInfo redirect_info;
+  ResourceResponseHead response_head;
+  ResourceRequestCompletionStatus completion_status;
+
+  url_loader_client_->OnReceiveRedirect(redirect_info, response_head);
+  url_loader_client_->OnReceiveResponse(response_head, nullptr);
+  mojo::DataPipe data_pipe(DataPipeOptions());
+  uint32_t size = 5;
+  MojoResult result =
+      mojo::WriteDataRaw(data_pipe.producer_handle.get(), "hello", &size,
+                         MOJO_WRITE_DATA_FLAG_NONE);
+  ASSERT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(5u, size);
+  data_pipe.producer_handle.reset();
+
+  url_loader_client_->OnStartLoadingResponseBody(
+      std::move(data_pipe.consumer_handle));
+  url_loader_client_->OnTransferSizeUpdated(4);
+  url_loader_client_->OnComplete(completion_status);
+
+  EXPECT_EQ(0, request_peer_context_.seen_redirects);
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_EQ("", request_peer_context_.data);
+  EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
+
+  dispatcher_->SetDefersLoading(request_id_, true);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, request_peer_context_.seen_redirects);
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_EQ("", request_peer_context_.data);
+  EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
+
+  dispatcher_->SetDefersLoading(request_id_, false);
+  EXPECT_EQ(0, request_peer_context_.seen_redirects);
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_EQ("", request_peer_context_.data);
+  EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, request_peer_context_.seen_redirects);
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_EQ("", request_peer_context_.data);
+  EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
+  EXPECT_FALSE(request_peer_context_.cancelled);
+
+  dispatcher_->SetDefersLoading(request_id_, false);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, request_peer_context_.seen_redirects);
+  EXPECT_TRUE(request_peer_context_.received_response);
+  EXPECT_TRUE(request_peer_context_.complete);
+  EXPECT_EQ("hello", request_peer_context_.data);
+  EXPECT_EQ(4, request_peer_context_.total_encoded_data_length);
+  EXPECT_FALSE(request_peer_context_.cancelled);
+}
+
+TEST_F(URLLoaderClientImplTest,
+       SetDeferredDuringFlushingDeferredMessageOnTransferSizeUpdated) {
+  request_peer_context_.defer_on_transfer_size_updated = true;
+
+  ResourceResponseHead response_head;
+  ResourceRequestCompletionStatus completion_status;
+
+  url_loader_client_->OnReceiveResponse(response_head, nullptr);
+
+  url_loader_client_->OnTransferSizeUpdated(4);
+  url_loader_client_->OnComplete(completion_status);
+
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
+
+  dispatcher_->SetDefersLoading(request_id_, true);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
+
+  dispatcher_->SetDefersLoading(request_id_, false);
+  EXPECT_FALSE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_peer_context_.received_response);
+  EXPECT_FALSE(request_peer_context_.complete);
+  EXPECT_EQ(4, request_peer_context_.total_encoded_data_length);
+  EXPECT_FALSE(request_peer_context_.cancelled);
+
+  dispatcher_->SetDefersLoading(request_id_, false);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_peer_context_.received_response);
+  EXPECT_TRUE(request_peer_context_.complete);
+  EXPECT_EQ(4, request_peer_context_.total_encoded_data_length);
+  EXPECT_FALSE(request_peer_context_.cancelled);
+}
+
 }  // namespace content
diff --git a/content/child/url_response_body_consumer.cc b/content/child/url_response_body_consumer.cc
index 9d5c017..5014eeb 100644
--- a/content/child/url_response_body_consumer.cc
+++ b/content/child/url_response_body_consumer.cc
@@ -76,6 +76,15 @@
   handle_watcher_.Cancel();
 }
 
+void URLResponseBodyConsumer::SetDefersLoading() {
+  is_deferred_ = true;
+}
+
+void URLResponseBodyConsumer::UnsetDefersLoading() {
+  is_deferred_ = false;
+  OnReadable(MOJO_RESULT_OK);
+}
+
 void URLResponseBodyConsumer::Reclaim(uint32_t size) {
   MojoResult result = mojo::EndReadDataRaw(handle_.get(), size);
   DCHECK_EQ(MOJO_RESULT_OK, result);
@@ -89,17 +98,16 @@
 }
 
 void URLResponseBodyConsumer::OnReadable(MojoResult unused) {
-  DCHECK(!is_in_on_readable_);
-
-  if (has_been_cancelled_ || has_seen_end_of_data_)
+  if (has_been_cancelled_ || has_seen_end_of_data_ || is_deferred_)
     return;
 
+  DCHECK(!is_in_on_readable_);
+
   // Protect |this| as RequestPeer::OnReceivedData may call deref.
   scoped_refptr<URLResponseBodyConsumer> protect(this);
   base::AutoReset<bool> is_in_on_readable(&is_in_on_readable_, true);
 
-  // TODO(yhirano): Suppress notification when deferred.
-  while (!has_been_cancelled_) {
+  while (!has_been_cancelled_ && !is_deferred_) {
     const void* buffer = nullptr;
     uint32_t available = 0;
     MojoResult result = mojo::BeginReadDataRaw(
@@ -134,7 +142,7 @@
   // Cancel this instance in order not to notify twice.
   Cancel();
 
-  resource_dispatcher_->OnMessageReceived(
+  resource_dispatcher_->DispatchMessage(
       ResourceMsg_RequestComplete(request_id_, completion_status_));
   // |this| may be deleted.
 }
diff --git a/content/child/url_response_body_consumer.h b/content/child/url_response_body_consumer.h
index 09a988bdd..1947cb66 100644
--- a/content/child/url_response_body_consumer.h
+++ b/content/child/url_response_body_consumer.h
@@ -51,6 +51,9 @@
   // cancelled or done.
   void Cancel();
 
+  void SetDefersLoading();
+  void UnsetDefersLoading();
+
  private:
   friend class base::RefCounted<URLResponseBodyConsumer>;
   ~URLResponseBodyConsumer();
@@ -71,6 +74,7 @@
   bool has_received_completion_ = false;
   bool has_been_cancelled_ = false;
   bool has_seen_end_of_data_;
+  bool is_deferred_ = false;
   bool is_in_on_readable_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(URLResponseBodyConsumer);
diff --git a/content/public/browser/storage_partition.h b/content/public/browser/storage_partition.h
index 909b370..9cb62329 100644
--- a/content/public/browser/storage_partition.h
+++ b/content/public/browser/storage_partition.h
@@ -47,6 +47,7 @@
 class HostZoomLevelContext;
 class HostZoomMap;
 class IndexedDBContext;
+class PaymentAppContext;
 class PlatformNotificationContext;
 class ServiceWorkerContext;
 class ZoomLevelDelegate;
@@ -74,6 +75,7 @@
   virtual HostZoomLevelContext* GetHostZoomLevelContext() = 0;
   virtual ZoomLevelDelegate* GetZoomLevelDelegate() = 0;
   virtual PlatformNotificationContext* GetPlatformNotificationContext() = 0;
+  virtual PaymentAppContext* GetPaymentAppContext() = 0;
 
   enum : uint32_t {
     REMOVE_DATA_MASK_APPCACHE = 1 << 0,
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index b90db49..2823565 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -279,8 +279,6 @@
     "origin_trials/web_trial_token_validator_impl.h",
     "peripheral_content_heuristic.cc",
     "peripheral_content_heuristic.h",
-    "presentation/presentation_connection_client.cc",
-    "presentation/presentation_connection_client.h",
     "presentation/presentation_dispatcher.cc",
     "presentation/presentation_dispatcher.h",
     "push_messaging/push_messaging_dispatcher.cc",
diff --git a/content/renderer/presentation/presentation_connection_client.cc b/content/renderer/presentation/presentation_connection_client.cc
deleted file mode 100644
index 145e08d..0000000
--- a/content/renderer/presentation/presentation_connection_client.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/renderer/presentation/presentation_connection_client.h"
-
-#include "base/logging.h"
-#include "third_party/WebKit/public/platform/WebString.h"
-#include "third_party/WebKit/public/platform/WebURL.h"
-
-namespace content {
-
-PresentationConnectionClient::PresentationConnectionClient(
-    blink::mojom::PresentationSessionInfoPtr session_info)
-    : url_(session_info->url),
-      id_(blink::WebString::fromUTF8(session_info->id)) {}
-
-PresentationConnectionClient::PresentationConnectionClient(
-    const GURL& url,
-    const mojo::String& id)
-    : url_(url),
-      id_(blink::WebString::fromUTF8(id)) {}
-
-PresentationConnectionClient::~PresentationConnectionClient() {
-}
-
-blink::WebURL PresentationConnectionClient::getUrl() {
-    return url_;
-}
-
-blink::WebString PresentationConnectionClient::getId() {
-    return id_;
-}
-
-}  // namespace content
diff --git a/content/renderer/presentation/presentation_connection_client.h b/content/renderer/presentation/presentation_connection_client.h
deleted file mode 100644
index f8e0bac0..0000000
--- a/content/renderer/presentation/presentation_connection_client.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_RENDERER_PRESENTATION_PRESENTATION_SESSION_CLIENT_H_
-#define CONTENT_RENDERER_PRESENTATION_PRESENTATION_SESSION_CLIENT_H_
-
-#include "base/compiler_specific.h"
-#include "content/common/content_export.h"
-#include "third_party/WebKit/public/platform/modules/presentation/WebPresentationConnectionClient.h"
-#include "third_party/WebKit/public/platform/modules/presentation/presentation.mojom.h"
-#include "url/gurl.h"
-
-namespace content {
-
-// PresentationConnectionClient is passed to the Blink layer if presentation
-// session has been created successfully. Owned by the callback.
-class CONTENT_EXPORT PresentationConnectionClient
-    : public NON_EXPORTED_BASE(blink::WebPresentationConnectionClient) {
- public:
-  explicit PresentationConnectionClient(
-      blink::mojom::PresentationSessionInfoPtr session_info);
-  explicit PresentationConnectionClient(const GURL& url,
-                                        const mojo::String& id);
-  ~PresentationConnectionClient() override;
-
-  // WebPresentationConnectionClient implementation.
-  blink::WebURL getUrl() override;
-  blink::WebString getId() override;
-
- private:
-  blink::WebURL url_;
-  blink::WebString id_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_PRESENTATION_PRESENTATION_SESSION_CLIENT_H_
diff --git a/content/renderer/presentation/presentation_dispatcher.cc b/content/renderer/presentation/presentation_dispatcher.cc
index 77a1fca..2e084ee 100644
--- a/content/renderer/presentation/presentation_dispatcher.cc
+++ b/content/renderer/presentation/presentation_dispatcher.cc
@@ -14,7 +14,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/public/common/presentation_constants.h"
 #include "content/public/renderer/render_frame.h"
-#include "content/renderer/presentation/presentation_connection_client.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/WebKit/public/platform/WebString.h"
 #include "third_party/WebKit/public/platform/WebURL.h"
@@ -23,10 +23,26 @@
 #include "third_party/WebKit/public/platform/modules/presentation/WebPresentationController.h"
 #include "third_party/WebKit/public/platform/modules/presentation/WebPresentationError.h"
 #include "third_party/WebKit/public/platform/modules/presentation/WebPresentationReceiver.h"
+#include "third_party/WebKit/public/platform/modules/presentation/WebPresentationSessionInfo.h"
 #include "third_party/WebKit/public/platform/modules/presentation/presentation.mojom.h"
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "url/gurl.h"
 
+namespace mojo {
+
+// Temporary type converter since Presentation API has not been Onion Soup-ed.
+template <>
+struct TypeConverter<blink::WebPresentationSessionInfo,
+                     blink::mojom::PresentationSessionInfoPtr> {
+  static blink::WebPresentationSessionInfo Convert(
+      const blink::mojom::PresentationSessionInfoPtr& input) {
+    return blink::WebPresentationSessionInfo(
+        blink::WebURL(input->url), blink::WebString::fromUTF8(input->id));
+  }
+};
+
+}  // namespace mojo
+
 namespace {
 
 blink::WebPresentationError::ErrorType GetWebPresentationErrorTypeFromMojo(
@@ -105,7 +121,7 @@
 
 void PresentationDispatcher::startSession(
     const blink::WebVector<blink::WebURL>& presentationUrls,
-    std::unique_ptr<blink::WebPresentationConnectionClientCallbacks> callback) {
+    std::unique_ptr<blink::WebPresentationConnectionCallback> callback) {
   DCHECK(callback);
   ConnectToPresentationServiceIfNeeded();
 
@@ -124,7 +140,7 @@
 void PresentationDispatcher::joinSession(
     const blink::WebVector<blink::WebURL>& presentationUrls,
     const blink::WebString& presentationId,
-    std::unique_ptr<blink::WebPresentationConnectionClientCallbacks> callback) {
+    std::unique_ptr<blink::WebPresentationConnectionCallback> callback) {
   DCHECK(callback);
   ConnectToPresentationServiceIfNeeded();
 
@@ -380,12 +396,12 @@
   if (!session_info.is_null()) {
     presentation_service_->ListenForConnectionMessages(session_info.Clone());
     controller_->didStartDefaultSession(
-        new PresentationConnectionClient(std::move(session_info)));
+        mojo::ConvertTo<blink::WebPresentationSessionInfo>(session_info));
   }
 }
 
 void PresentationDispatcher::OnSessionCreated(
-    std::unique_ptr<blink::WebPresentationConnectionClientCallbacks> callback,
+    std::unique_ptr<blink::WebPresentationConnectionCallback> callback,
     blink::mojom::PresentationSessionInfoPtr session_info,
     blink::mojom::PresentationErrorPtr error) {
   DCHECK(callback);
@@ -400,39 +416,37 @@
   DCHECK(!session_info.is_null());
   presentation_service_->ListenForConnectionMessages(session_info.Clone());
   callback->onSuccess(
-      base::MakeUnique<PresentationConnectionClient>(std::move(session_info)));
+      mojo::ConvertTo<blink::WebPresentationSessionInfo>(session_info));
 }
 
 void PresentationDispatcher::OnReceiverConnectionAvailable(
     blink::mojom::PresentationSessionInfoPtr session_info) {
   if (receiver_) {
     receiver_->onReceiverConnectionAvailable(
-        new PresentationConnectionClient(std::move(session_info)));
+        mojo::ConvertTo<blink::WebPresentationSessionInfo>(session_info));
   }
 }
 
 void PresentationDispatcher::OnConnectionStateChanged(
-    blink::mojom::PresentationSessionInfoPtr connection,
+    blink::mojom::PresentationSessionInfoPtr session_info,
     blink::mojom::PresentationConnectionState state) {
   if (!controller_)
     return;
 
-  DCHECK(!connection.is_null());
   controller_->didChangeSessionState(
-      new PresentationConnectionClient(std::move(connection)),
+      mojo::ConvertTo<blink::WebPresentationSessionInfo>(session_info),
       GetWebPresentationConnectionStateFromMojo(state));
 }
 
 void PresentationDispatcher::OnConnectionClosed(
-    blink::mojom::PresentationSessionInfoPtr connection,
+    blink::mojom::PresentationSessionInfoPtr session_info,
     blink::mojom::PresentationConnectionCloseReason reason,
     const std::string& message) {
   if (!controller_)
     return;
 
-  DCHECK(!connection.is_null());
   controller_->didCloseConnection(
-      new PresentationConnectionClient(std::move(connection)),
+      mojo::ConvertTo<blink::WebPresentationSessionInfo>(session_info),
       GetWebPresentationConnectionCloseReasonFromMojo(reason),
       blink::WebString::fromUTF8(message));
 }
@@ -446,20 +460,20 @@
   for (size_t i = 0; i < messages.size(); ++i) {
     // Note: Passing batches of messages to the Blink layer would be more
     // efficient.
-    std::unique_ptr<PresentationConnectionClient> session_client(
-        new PresentationConnectionClient(session_info->url, session_info->id));
+    auto web_session_info =
+        mojo::ConvertTo<blink::WebPresentationSessionInfo>(session_info);
     switch (messages[i]->type) {
       case blink::mojom::PresentationMessageType::TEXT: {
         // TODO(mfoltz): Do we need to DCHECK(messages[i]->message)?
         controller_->didReceiveSessionTextMessage(
-            session_client.release(),
+            web_session_info,
             blink::WebString::fromUTF8(messages[i]->message.value()));
         break;
       }
       case blink::mojom::PresentationMessageType::BINARY: {
         // TODO(mfoltz): Do we need to DCHECK(messages[i]->data)?
         controller_->didReceiveSessionBinaryMessage(
-            session_client.release(), &(messages[i]->data->front()),
+            web_session_info, &(messages[i]->data->front()),
             messages[i]->data->size());
         break;
       }
diff --git a/content/renderer/presentation/presentation_dispatcher.h b/content/renderer/presentation/presentation_dispatcher.h
index 03cdf46..91dba49 100644
--- a/content/renderer/presentation/presentation_dispatcher.h
+++ b/content/renderer/presentation/presentation_dispatcher.h
@@ -71,15 +71,13 @@
   void setController(blink::WebPresentationController* controller) override;
   void setReceiver(blink::WebPresentationReceiver*) override;
 
-  void startSession(
-      const blink::WebVector<blink::WebURL>& presentationUrls,
-      std::unique_ptr<blink::WebPresentationConnectionClientCallbacks> callback)
-      override;
-  void joinSession(
-      const blink::WebVector<blink::WebURL>& presentationUrls,
-      const blink::WebString& presentationId,
-      std::unique_ptr<blink::WebPresentationConnectionClientCallbacks> callback)
-      override;
+  void startSession(const blink::WebVector<blink::WebURL>& presentationUrls,
+                    std::unique_ptr<blink::WebPresentationConnectionCallback>
+                        callback) override;
+  void joinSession(const blink::WebVector<blink::WebURL>& presentationUrls,
+                   const blink::WebString& presentationId,
+                   std::unique_ptr<blink::WebPresentationConnectionCallback>
+                       callback) override;
   void sendString(const blink::WebURL& presentationUrl,
                   const blink::WebString& presentationId,
                   const blink::WebString& message) override;
@@ -114,10 +112,10 @@
   void OnScreenAvailabilityNotSupported(const GURL& url) override;
   void OnScreenAvailabilityUpdated(const GURL& url, bool available) override;
   void OnConnectionStateChanged(
-      blink::mojom::PresentationSessionInfoPtr connection,
+      blink::mojom::PresentationSessionInfoPtr session_info,
       blink::mojom::PresentationConnectionState state) override;
   void OnConnectionClosed(
-      blink::mojom::PresentationSessionInfoPtr connection,
+      blink::mojom::PresentationSessionInfoPtr session_info,
       blink::mojom::PresentationConnectionCloseReason reason,
       const std::string& message) override;
   void OnConnectionMessagesReceived(
@@ -127,7 +125,7 @@
       blink::mojom::PresentationSessionInfoPtr session_info) override;
 
   void OnSessionCreated(
-      std::unique_ptr<blink::WebPresentationConnectionClientCallbacks> callback,
+      std::unique_ptr<blink::WebPresentationConnectionCallback> callback,
       blink::mojom::PresentationSessionInfoPtr session_info,
       blink::mojom::PresentationErrorPtr error);
   void OnReceiverConnectionAvailable(
diff --git a/gpu/config/gpu_driver_bug_list_json.cc b/gpu/config/gpu_driver_bug_list_json.cc
index 6b93df46..56d598fc 100644
--- a/gpu/config/gpu_driver_bug_list_json.cc
+++ b/gpu/config/gpu_driver_bug_list_json.cc
@@ -200,9 +200,9 @@
     {
       "id": 31,
       "cr_bugs": [154715, 10068, 269829, 294779, 285292],
-      "description": "The Mali-Txxx driver does not guarantee flush ordering",
+      "description": "The Mali-xxx driver does not guarantee flush ordering",
       "gl_vendor": "ARM.*",
-      "gl_renderer": "Mali-T.*",
+      "gl_renderer": "Mali.*",
       "features": [
         "use_virtualized_gl_contexts"
       ]
diff --git a/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm b/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm
index 6d2277e..db32a0a3 100644
--- a/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm
+++ b/ios/chrome/browser/reading_list/reading_list_web_state_observer.mm
@@ -7,6 +7,7 @@
 #import <Foundation/Foundation.h>
 
 #include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
 #include "components/reading_list/ios/reading_list_model.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
@@ -200,6 +201,7 @@
 
   if (load_completion_status == web::PageLoadCompletionStatus::SUCCESS) {
     reading_list_model_->SetReadStatus(pending_url_, true);
+    UMA_HISTOGRAM_BOOLEAN("ReadingList.OfflineVersionDisplayed", false);
   } else {
     LoadOfflineReadingListEntry(item);
   }
@@ -272,4 +274,5 @@
   item->SetVirtualURL(pending_url_);
   web_state()->GetNavigationManager()->Reload(false);
   reading_list_model_->SetReadStatus(entry->URL(), true);
+  UMA_HISTOGRAM_BOOLEAN("ReadingList.OfflineVersionDisplayed", true);
 }
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8Binding.h b/third_party/WebKit/Source/bindings/core/v8/V8Binding.h
index 29a5a46..ddcb471 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8Binding.h
+++ b/third_party/WebKit/Source/bindings/core/v8/V8Binding.h
@@ -658,9 +658,7 @@
 inline v8::MaybeLocal<v8::Value> v8DateOrNaN(v8::Isolate* isolate,
                                              double value) {
   ASSERT(isolate);
-  return v8::Date::New(
-      isolate->GetCurrentContext(),
-      std::isfinite(value) ? value : std::numeric_limits<double>::quiet_NaN());
+  return v8::Date::New(isolate->GetCurrentContext(), value);
 }
 
 // FIXME: Remove the special casing for NodeFilter and XPathNSResolver.
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationConnection.cpp b/third_party/WebKit/Source/modules/presentation/PresentationConnection.cpp
index 67f98aace..cbcb2d105 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationConnection.cpp
+++ b/third_party/WebKit/Source/modules/presentation/PresentationConnection.cpp
@@ -25,7 +25,6 @@
 #include "modules/presentation/PresentationController.h"
 #include "modules/presentation/PresentationReceiver.h"
 #include "modules/presentation/PresentationRequest.h"
-#include "public/platform/modules/presentation/WebPresentationConnectionClient.h"
 #include "wtf/Assertions.h"
 #include "wtf/text/AtomicString.h"
 #include <memory>
@@ -163,10 +162,9 @@
 // static
 PresentationConnection* PresentationConnection::take(
     ScriptPromiseResolver* resolver,
-    std::unique_ptr<WebPresentationConnectionClient> client,
+    const WebPresentationSessionInfo& sessionInfo,
     PresentationRequest* request) {
   ASSERT(resolver);
-  ASSERT(client);
   ASSERT(request);
   ASSERT(resolver->getExecutionContext()->isDocument());
 
@@ -179,19 +177,19 @@
   if (!controller)
     return nullptr;
 
-  return take(controller, std::move(client), request);
+  return take(controller, sessionInfo, request);
 }
 
 // static
 PresentationConnection* PresentationConnection::take(
     PresentationController* controller,
-    std::unique_ptr<WebPresentationConnectionClient> client,
+    const WebPresentationSessionInfo& sessionInfo,
     PresentationRequest* request) {
   ASSERT(controller);
   ASSERT(request);
 
   PresentationConnection* connection = new PresentationConnection(
-      controller->frame(), client->getId(), client->getUrl());
+      controller->frame(), sessionInfo.id, sessionInfo.url);
   controller->registerConnection(connection);
 
   // Fire onconnectionavailable event asynchronously.
@@ -208,12 +206,11 @@
 // static
 PresentationConnection* PresentationConnection::take(
     PresentationReceiver* receiver,
-    std::unique_ptr<WebPresentationConnectionClient> client) {
+    const WebPresentationSessionInfo& sessionInfo) {
   DCHECK(receiver);
-  DCHECK(client);
 
   PresentationConnection* connection = new PresentationConnection(
-      receiver->frame(), client->getId(), client->getUrl());
+      receiver->frame(), sessionInfo.id, sessionInfo.url);
   receiver->registerConnection(connection);
 
   return connection;
@@ -407,9 +404,8 @@
 }
 
 bool PresentationConnection::matches(
-    WebPresentationConnectionClient* client) const {
-  return client && m_url == KURL(client->getUrl()) &&
-         m_id == static_cast<String>(client->getId());
+    const WebPresentationSessionInfo& sessionInfo) const {
+  return m_url == KURL(sessionInfo.url) && m_id == String(sessionInfo.id);
 }
 
 void PresentationConnection::didChangeState(
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationConnection.h b/third_party/WebKit/Source/modules/presentation/PresentationConnection.h
index 2a995c3d..d09b9cd 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationConnection.h
+++ b/third_party/WebKit/Source/modules/presentation/PresentationConnection.h
@@ -11,7 +11,8 @@
 #include "core/fileapi/FileError.h"
 #include "platform/heap/Handle.h"
 #include "platform/weborigin/KURL.h"
-#include "public/platform/modules/presentation/WebPresentationConnectionClient.h"
+#include "public/platform/modules/presentation/WebPresentationController.h"
+#include "public/platform/modules/presentation/WebPresentationSessionInfo.h"
 #include "wtf/text/WTFString.h"
 #include <memory>
 
@@ -34,19 +35,14 @@
 
  public:
   // For CallbackPromiseAdapter.
-  using WebType = std::unique_ptr<WebPresentationConnectionClient>;
-
-  static PresentationConnection* take(
-      ScriptPromiseResolver*,
-      std::unique_ptr<WebPresentationConnectionClient>,
-      PresentationRequest*);
-  static PresentationConnection* take(
-      PresentationController*,
-      std::unique_ptr<WebPresentationConnectionClient>,
-      PresentationRequest*);
-  static PresentationConnection* take(
-      PresentationReceiver*,
-      std::unique_ptr<WebPresentationConnectionClient>);
+  static PresentationConnection* take(ScriptPromiseResolver*,
+                                      const WebPresentationSessionInfo&,
+                                      PresentationRequest*);
+  static PresentationConnection* take(PresentationController*,
+                                      const WebPresentationSessionInfo&,
+                                      PresentationRequest*);
+  static PresentationConnection* take(PresentationReceiver*,
+                                      const WebPresentationSessionInfo&);
   ~PresentationConnection() override;
 
   // EventTarget implementation.
@@ -73,9 +69,9 @@
   DEFINE_ATTRIBUTE_EVENT_LISTENER(close);
   DEFINE_ATTRIBUTE_EVENT_LISTENER(terminate);
 
-  // Returns true if and only if the WebPresentationConnectionClient represents
-  // this connection.
-  bool matches(WebPresentationConnectionClient*) const;
+  // Returns true if and only if the the session info represents this
+  // connection.
+  bool matches(const WebPresentationSessionInfo&) const;
 
   // Notifies the connection about its state change.
   void didChangeState(WebPresentationConnectionState);
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationConnectionCallbacks.cpp b/third_party/WebKit/Source/modules/presentation/PresentationConnectionCallbacks.cpp
index a9d2404..8de07df2 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationConnectionCallbacks.cpp
+++ b/third_party/WebKit/Source/modules/presentation/PresentationConnectionCallbacks.cpp
@@ -9,7 +9,6 @@
 #include "modules/presentation/PresentationConnection.h"
 #include "modules/presentation/PresentationError.h"
 #include "modules/presentation/PresentationRequest.h"
-#include "public/platform/modules/presentation/WebPresentationConnectionClient.h"
 #include "public/platform/modules/presentation/WebPresentationError.h"
 #include "wtf/PtrUtil.h"
 #include <memory>
@@ -25,16 +24,12 @@
 }
 
 void PresentationConnectionCallbacks::onSuccess(
-    std::unique_ptr<WebPresentationConnectionClient>
-        PresentationConnectionClient) {
-  std::unique_ptr<WebPresentationConnectionClient> result(
-      WTF::wrapUnique(PresentationConnectionClient.release()));
-
+    const WebPresentationSessionInfo& sessionInfo) {
   if (!m_resolver->getExecutionContext() ||
       m_resolver->getExecutionContext()->isContextDestroyed())
     return;
-  m_resolver->resolve(PresentationConnection::take(
-      m_resolver.get(), std::move(result), m_request));
+  m_resolver->resolve(
+      PresentationConnection::take(m_resolver.get(), sessionInfo, m_request));
 }
 
 void PresentationConnectionCallbacks::onError(
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationConnectionCallbacks.h b/third_party/WebKit/Source/modules/presentation/PresentationConnectionCallbacks.h
index 257e50b..79b33160 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationConnectionCallbacks.h
+++ b/third_party/WebKit/Source/modules/presentation/PresentationConnectionCallbacks.h
@@ -13,7 +13,7 @@
 
 class PresentationRequest;
 class ScriptPromiseResolver;
-class WebPresentationConnectionClient;
+struct WebPresentationSessionInfo;
 struct WebPresentationError;
 
 // PresentationConnectionCallbacks extends WebCallbacks to resolve the
@@ -21,13 +21,13 @@
 // the PresentationRequest object that originated the call in its constructor
 // and will pass it to the created PresentationConnection.
 class PresentationConnectionCallbacks final
-    : public WebCallbacks<std::unique_ptr<WebPresentationConnectionClient>,
+    : public WebCallbacks<const WebPresentationSessionInfo&,
                           const WebPresentationError&> {
  public:
   PresentationConnectionCallbacks(ScriptPromiseResolver*, PresentationRequest*);
   ~PresentationConnectionCallbacks() override = default;
 
-  void onSuccess(std::unique_ptr<WebPresentationConnectionClient>) override;
+  void onSuccess(const WebPresentationSessionInfo&) override;
   void onError(const WebPresentationError&) override;
 
  private:
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationController.cpp b/third_party/WebKit/Source/modules/presentation/PresentationController.cpp
index 65d4ede..0655e0f2 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationController.cpp
+++ b/third_party/WebKit/Source/modules/presentation/PresentationController.cpp
@@ -63,58 +63,46 @@
 }
 
 void PresentationController::didStartDefaultSession(
-    WebPresentationConnectionClient* connectionClient) {
+    const WebPresentationSessionInfo& sessionInfo) {
   if (!m_presentation || !m_presentation->defaultRequest())
     return;
-  PresentationConnection::take(this, WTF::wrapUnique(connectionClient),
+  PresentationConnection::take(this, sessionInfo,
                                m_presentation->defaultRequest());
 }
 
 void PresentationController::didChangeSessionState(
-    WebPresentationConnectionClient* connectionClient,
+    const WebPresentationSessionInfo& sessionInfo,
     WebPresentationConnectionState state) {
-  std::unique_ptr<WebPresentationConnectionClient> client =
-      WTF::wrapUnique(connectionClient);
-
-  PresentationConnection* connection = findConnection(client.get());
+  PresentationConnection* connection = findConnection(sessionInfo);
   if (!connection)
     return;
   connection->didChangeState(state);
 }
 
 void PresentationController::didCloseConnection(
-    WebPresentationConnectionClient* connectionClient,
+    const WebPresentationSessionInfo& sessionInfo,
     WebPresentationConnectionCloseReason reason,
     const WebString& message) {
-  std::unique_ptr<WebPresentationConnectionClient> client =
-      WTF::wrapUnique(connectionClient);
-
-  PresentationConnection* connection = findConnection(client.get());
+  PresentationConnection* connection = findConnection(sessionInfo);
   if (!connection)
     return;
   connection->didClose(reason, message);
 }
 
 void PresentationController::didReceiveSessionTextMessage(
-    WebPresentationConnectionClient* connectionClient,
+    const WebPresentationSessionInfo& sessionInfo,
     const WebString& message) {
-  std::unique_ptr<WebPresentationConnectionClient> client =
-      WTF::wrapUnique(connectionClient);
-
-  PresentationConnection* connection = findConnection(client.get());
+  PresentationConnection* connection = findConnection(sessionInfo);
   if (!connection)
     return;
   connection->didReceiveTextMessage(message);
 }
 
 void PresentationController::didReceiveSessionBinaryMessage(
-    WebPresentationConnectionClient* connectionClient,
+    const WebPresentationSessionInfo& sessionInfo,
     const uint8_t* data,
     size_t length) {
-  std::unique_ptr<WebPresentationConnectionClient> client =
-      WTF::wrapUnique(connectionClient);
-
-  PresentationConnection* connection = findConnection(client.get());
+  PresentationConnection* connection = findConnection(sessionInfo);
   if (!connection)
     return;
   connection->didReceiveBinaryMessage(data, length);
@@ -149,9 +137,9 @@
 }
 
 PresentationConnection* PresentationController::findConnection(
-    WebPresentationConnectionClient* connectionClient) {
+    const WebPresentationSessionInfo& sessionInfo) {
   for (const auto& connection : m_connections) {
-    if (connection->matches(connectionClient))
+    if (connection->matches(sessionInfo))
       return connection.get();
   }
 
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationController.h b/third_party/WebKit/Source/modules/presentation/PresentationController.h
index 9aecc0f4..80a141e 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationController.h
+++ b/third_party/WebKit/Source/modules/presentation/PresentationController.h
@@ -18,9 +18,6 @@
 namespace blink {
 
 class PresentationConnection;
-class WebPresentationConnectionClient;
-enum class WebPresentationConnectionCloseReason;
-enum class WebPresentationConnectionState;
 
 // The coordinator between the various page exposed properties and the content
 // layer represented via |WebPresentationClient|.
@@ -48,15 +45,15 @@
   DECLARE_VIRTUAL_TRACE();
 
   // Implementation of WebPresentationController.
-  void didStartDefaultSession(WebPresentationConnectionClient*) override;
-  void didChangeSessionState(WebPresentationConnectionClient*,
+  void didStartDefaultSession(const WebPresentationSessionInfo&) override;
+  void didChangeSessionState(const WebPresentationSessionInfo&,
                              WebPresentationConnectionState) override;
-  void didCloseConnection(WebPresentationConnectionClient*,
+  void didCloseConnection(const WebPresentationSessionInfo&,
                           WebPresentationConnectionCloseReason,
                           const WebString& message) override;
-  void didReceiveSessionTextMessage(WebPresentationConnectionClient*,
+  void didReceiveSessionTextMessage(const WebPresentationSessionInfo&,
                                     const WebString&) override;
-  void didReceiveSessionBinaryMessage(WebPresentationConnectionClient*,
+  void didReceiveSessionBinaryMessage(const WebPresentationSessionInfo&,
                                       const uint8_t* data,
                                       size_t length) override;
 
@@ -81,7 +78,7 @@
 
   // Return the connection associated with the given |connectionClient| or
   // null if it doesn't exist.
-  PresentationConnection* findConnection(WebPresentationConnectionClient*);
+  PresentationConnection* findConnection(const WebPresentationSessionInfo&);
 
   // The WebPresentationClient which allows communicating with the embedder.
   // It is not owned by the PresentationController but the controller will
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationReceiver.cpp b/third_party/WebKit/Source/modules/presentation/PresentationReceiver.cpp
index 9d22bcd..aa15f675 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationReceiver.cpp
+++ b/third_party/WebKit/Source/modules/presentation/PresentationReceiver.cpp
@@ -40,12 +40,10 @@
 }
 
 void PresentationReceiver::onReceiverConnectionAvailable(
-    WebPresentationConnectionClient* connectionClient) {
-  DCHECK(connectionClient);
+    const WebPresentationSessionInfo& sessionInfo) {
   // take() will call PresentationReceiver::registerConnection()
   // and register the connection.
-  auto connection =
-      PresentationConnection::take(this, WTF::wrapUnique(connectionClient));
+  auto connection = PresentationConnection::take(this, sessionInfo);
 
   // receiver.connectionList property not accessed
   if (!m_connectionListProperty)
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationReceiver.h b/third_party/WebKit/Source/modules/presentation/PresentationReceiver.h
index c4d3c2c..c59d67c 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationReceiver.h
+++ b/third_party/WebKit/Source/modules/presentation/PresentationReceiver.h
@@ -20,7 +20,6 @@
 class PresentationConnection;
 class PresentationConnectionList;
 class WebPresentationClient;
-class WebPresentationConnectionClient;
 
 // Implements the PresentationReceiver interface from the Presentation API from
 // which websites can implement the receiving side of a presentation session.
@@ -44,7 +43,8 @@
   ScriptPromise connectionList(ScriptState*);
 
   // Implementation of WebPresentationController.
-  void onReceiverConnectionAvailable(WebPresentationConnectionClient*) override;
+  void onReceiverConnectionAvailable(
+      const WebPresentationSessionInfo&) override;
   void registerConnection(PresentationConnection*);
 
   DECLARE_VIRTUAL_TRACE();
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationReceiverTest.cpp b/third_party/WebKit/Source/modules/presentation/PresentationReceiverTest.cpp
index 8ec4b95..9b6c601 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationReceiverTest.cpp
+++ b/third_party/WebKit/Source/modules/presentation/PresentationReceiverTest.cpp
@@ -11,7 +11,6 @@
 #include "modules/presentation/PresentationConnectionList.h"
 #include "platform/testing/URLTestHelpers.h"
 #include "public/platform/modules/presentation/WebPresentationClient.h"
-#include "public/platform/modules/presentation/WebPresentationConnectionClient.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include <memory>
@@ -31,15 +30,15 @@
 };
 
 class MockWebPresentationClient : public WebPresentationClient {
-  void startSession(const WebVector<WebURL>& presentationUrls,
-                    std::unique_ptr<WebPresentationConnectionClientCallbacks>
-                        callbacks) override {
+  void startSession(
+      const WebVector<WebURL>& presentationUrls,
+      std::unique_ptr<WebPresentationConnectionCallback> callbacks) override {
     return startSession_(presentationUrls, callbacks);
   }
-  void joinSession(const WebVector<WebURL>& presentationUrls,
-                   const WebString& presentationId,
-                   std::unique_ptr<WebPresentationConnectionClientCallbacks>
-                       callbacks) override {
+  void joinSession(
+      const WebVector<WebURL>& presentationUrls,
+      const WebString& presentationId,
+      std::unique_ptr<WebPresentationConnectionCallback> callbacks) override {
     return joinSession_(presentationUrls, presentationId, callbacks);
   }
 
@@ -54,16 +53,14 @@
 
   MOCK_METHOD1(setReceiver, void(WebPresentationReceiver*));
 
-  MOCK_METHOD2(
-      startSession_,
-      void(const WebVector<WebURL>& presentationUrls,
-           std::unique_ptr<WebPresentationConnectionClientCallbacks>&));
+  MOCK_METHOD2(startSession_,
+               void(const WebVector<WebURL>& presentationUrls,
+                    std::unique_ptr<WebPresentationConnectionCallback>&));
 
-  MOCK_METHOD3(
-      joinSession_,
-      void(const WebVector<WebURL>& presentationUrls,
-           const WebString& presentationId,
-           std::unique_ptr<WebPresentationConnectionClientCallbacks>&));
+  MOCK_METHOD3(joinSession_,
+               void(const WebVector<WebURL>& presentationUrls,
+                    const WebString& presentationId,
+                    std::unique_ptr<WebPresentationConnectionCallback>&));
 
   MOCK_METHOD3(sendString,
                void(const WebURL& presentationUrl,
@@ -101,15 +98,6 @@
   MOCK_METHOD1(setDefaultPresentationUrls, void(const WebVector<WebURL>&));
 };
 
-class TestWebPresentationConnectionClient
-    : public WebPresentationConnectionClient {
- public:
-  WebString getId() override { return WebString::fromUTF8("id"); }
-  WebURL getUrl() override {
-    return URLTestHelpers::toKURL("http://www.example.com");
-  }
-};
-
 class PresentationReceiverTest : public ::testing::Test {
  public:
   void addConnectionavailableEventListener(EventListener*,
@@ -167,8 +155,8 @@
   receiver->connectionList(scope.getScriptState());
 
   // Receive first connection.
-  auto connectionClient = new TestWebPresentationConnectionClient();
-  receiver->onReceiverConnectionAvailable(connectionClient);
+  receiver->onReceiverConnectionAvailable(
+      WebPresentationSessionInfo(KURL(KURL(), "http://example.com"), "id"));
 
   verifyConnectionListPropertyState(ScriptPromisePropertyBase::Resolved,
                                     receiver);
@@ -185,13 +173,13 @@
   EXPECT_CALL(*eventHandler, handleEvent(testing::_, testing::_)).Times(1);
 
   receiver->connectionList(scope.getScriptState());
-  // Receive first connection.
-  auto connectionClient1 = new TestWebPresentationConnectionClient();
-  receiver->onReceiverConnectionAvailable(connectionClient1);
 
+  WebPresentationSessionInfo sessionInfo(KURL(KURL(), "http://example.com"),
+                                         "id");
+  // Receive first connection.
+  receiver->onReceiverConnectionAvailable(sessionInfo);
   // Receive second connection.
-  auto connectionClient2 = new TestWebPresentationConnectionClient();
-  receiver->onReceiverConnectionAvailable(connectionClient2);
+  receiver->onReceiverConnectionAvailable(sessionInfo);
 
   verifyConnectionListSize(2, receiver);
 }
@@ -205,13 +193,12 @@
   addConnectionavailableEventListener(eventHandler, receiver);
   EXPECT_CALL(*eventHandler, handleEvent(testing::_, testing::_)).Times(0);
 
+  WebPresentationSessionInfo sessionInfo(KURL(KURL(), "http://example.com"),
+                                         "id");
   // Receive first connection.
-  auto connectionClient1 = new TestWebPresentationConnectionClient();
-  receiver->onReceiverConnectionAvailable(connectionClient1);
-
+  receiver->onReceiverConnectionAvailable(sessionInfo);
   // Receive second connection.
-  auto connectionClient2 = new TestWebPresentationConnectionClient();
-  receiver->onReceiverConnectionAvailable(connectionClient2);
+  receiver->onReceiverConnectionAvailable(sessionInfo);
 
   receiver->connectionList(scope.getScriptState());
   verifyConnectionListPropertyState(ScriptPromisePropertyBase::Resolved,
diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn
index 458e00b5..21de3101 100644
--- a/third_party/WebKit/public/BUILD.gn
+++ b/third_party/WebKit/public/BUILD.gn
@@ -363,9 +363,12 @@
     "platform/modules/payments/WebPaymentItem.h",
     "platform/modules/payments/WebPaymentMethodData.h",
     "platform/modules/permissions/WebPermissionType.h",
+    "platform/modules/presentation/WebPresentationAvailabilityObserver.h",
     "platform/modules/presentation/WebPresentationClient.h",
     "platform/modules/presentation/WebPresentationController.h",
     "platform/modules/presentation/WebPresentationError.h",
+    "platform/modules/presentation/WebPresentationReceiver.h",
+    "platform/modules/presentation/WebPresentationSessionInfo.h",
     "platform/modules/push_messaging/WebPushClient.h",
     "platform/modules/push_messaging/WebPushError.h",
     "platform/modules/push_messaging/WebPushPermissionStatus.h",
diff --git a/third_party/WebKit/public/platform/modules/presentation/WebPresentationClient.h b/third_party/WebKit/public/platform/modules/presentation/WebPresentationClient.h
index 41527229..1c9e7b3 100644
--- a/third_party/WebKit/public/platform/modules/presentation/WebPresentationClient.h
+++ b/third_party/WebKit/public/platform/modules/presentation/WebPresentationClient.h
@@ -14,19 +14,19 @@
 
 class WebPresentationAvailabilityObserver;
 class WebPresentationController;
-class WebPresentationConnectionClient;
+struct WebPresentationError;
 class WebPresentationReceiver;
+struct WebPresentationSessionInfo;
 class WebString;
 class WebURL;
-struct WebPresentationError;
 template <typename T>
 class WebVector;
 
 // If session was created, callback's onSuccess() is invoked with the
 // information about the presentation session created by the embedder.
 // Otherwise, onError() is invoked with the error code and message.
-using WebPresentationConnectionClientCallbacks =
-    WebCallbacks<std::unique_ptr<WebPresentationConnectionClient>,
+using WebPresentationConnectionCallback =
+    WebCallbacks<const WebPresentationSessionInfo&,
                  const WebPresentationError&>;
 
 // Callback for .getAvailability().
@@ -48,13 +48,13 @@
   // Called when the frame requests to start a new session.
   virtual void startSession(
       const WebVector<WebURL>& presentationUrls,
-      std::unique_ptr<WebPresentationConnectionClientCallbacks>) = 0;
+      std::unique_ptr<WebPresentationConnectionCallback>) = 0;
 
   // Called when the frame requests to join an existing session.
   virtual void joinSession(
       const WebVector<WebURL>& presentationUrls,
       const WebString& presentationId,
-      std::unique_ptr<WebPresentationConnectionClientCallbacks>) = 0;
+      std::unique_ptr<WebPresentationConnectionCallback>) = 0;
 
   // Called when the frame requests to send String message to an existing
   // session.
diff --git a/third_party/WebKit/public/platform/modules/presentation/WebPresentationConnectionClient.h b/third_party/WebKit/public/platform/modules/presentation/WebPresentationConnectionClient.h
deleted file mode 100644
index 0ad0419d..0000000
--- a/third_party/WebKit/public/platform/modules/presentation/WebPresentationConnectionClient.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef WebPresentationConnectionClient_h
-#define WebPresentationConnectionClient_h
-
-#include "public/platform/WebString.h"
-#include "public/platform/WebURL.h"
-
-namespace blink {
-
-enum class WebPresentationConnectionState {
-  Connecting = 0,
-  Connected,
-  Closed,
-  Terminated,
-};
-
-enum class WebPresentationConnectionCloseReason { Error = 0, Closed, WentAway };
-
-// The implementation the embedder has to provide for the Presentation API to
-// work.
-class WebPresentationConnectionClient {
- public:
-  virtual ~WebPresentationConnectionClient() {}
-
-  virtual WebString getId() = 0;
-  virtual WebURL getUrl() = 0;
-};
-
-}  // namespace blink
-
-#endif  // WebPresentationConnectionClient_h
diff --git a/third_party/WebKit/public/platform/modules/presentation/WebPresentationController.h b/third_party/WebKit/public/platform/modules/presentation/WebPresentationController.h
index 2dcf0d3d..e59c632 100644
--- a/third_party/WebKit/public/platform/modules/presentation/WebPresentationController.h
+++ b/third_party/WebKit/public/platform/modules/presentation/WebPresentationController.h
@@ -9,10 +9,17 @@
 
 namespace blink {
 
-class WebPresentationConnectionClient;
+struct WebPresentationSessionInfo;
 class WebString;
-enum class WebPresentationConnectionCloseReason;
-enum class WebPresentationConnectionState;
+
+enum class WebPresentationConnectionCloseReason { Error = 0, Closed, WentAway };
+
+enum class WebPresentationConnectionState {
+  Connecting = 0,
+  Connected,
+  Closed,
+  Terminated,
+};
 
 // The delegate Blink provides to WebPresentationClient in order to get updates.
 class BLINK_PLATFORM_EXPORT WebPresentationController {
@@ -21,23 +28,23 @@
 
   // Called when the presentation session is started by the embedder using
   // the default presentation URL and id.
-  virtual void didStartDefaultSession(WebPresentationConnectionClient*) = 0;
+  virtual void didStartDefaultSession(const WebPresentationSessionInfo&) = 0;
 
   // Called when the state of a session changes.
-  virtual void didChangeSessionState(WebPresentationConnectionClient*,
+  virtual void didChangeSessionState(const WebPresentationSessionInfo&,
                                      WebPresentationConnectionState) = 0;
 
   // Called when a connection closes.
-  virtual void didCloseConnection(WebPresentationConnectionClient*,
+  virtual void didCloseConnection(const WebPresentationSessionInfo&,
                                   WebPresentationConnectionCloseReason,
                                   const WebString& message) = 0;
 
   // Called when a text message of a session is received.
-  virtual void didReceiveSessionTextMessage(WebPresentationConnectionClient*,
+  virtual void didReceiveSessionTextMessage(const WebPresentationSessionInfo&,
                                             const WebString& message) = 0;
 
   // Called when a binary message of a session is received.
-  virtual void didReceiveSessionBinaryMessage(WebPresentationConnectionClient*,
+  virtual void didReceiveSessionBinaryMessage(const WebPresentationSessionInfo&,
                                               const uint8_t* data,
                                               size_t length) = 0;
 };
diff --git a/third_party/WebKit/public/platform/modules/presentation/WebPresentationReceiver.h b/third_party/WebKit/public/platform/modules/presentation/WebPresentationReceiver.h
index 04ad7352e..15593441 100644
--- a/third_party/WebKit/public/platform/modules/presentation/WebPresentationReceiver.h
+++ b/third_party/WebKit/public/platform/modules/presentation/WebPresentationReceiver.h
@@ -9,7 +9,7 @@
 
 namespace blink {
 
-class WebPresentationConnectionClient;
+struct WebPresentationSessionInfo;
 
 // The delegate Blink provides to WebPresentationReceiverClient in order to get
 // updates.
@@ -19,7 +19,7 @@
 
   // Called when receiver page gets an incoming connection.
   virtual void onReceiverConnectionAvailable(
-      WebPresentationConnectionClient*) = 0;
+      const WebPresentationSessionInfo&) = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/public/platform/modules/presentation/WebPresentationSessionInfo.h b/third_party/WebKit/public/platform/modules/presentation/WebPresentationSessionInfo.h
new file mode 100644
index 0000000..db04a50b
--- /dev/null
+++ b/third_party/WebKit/public/platform/modules/presentation/WebPresentationSessionInfo.h
@@ -0,0 +1,27 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WebPresentationSessionInfo_h
+#define WebPresentationSessionInfo_h
+
+#include "public/platform/WebString.h"
+#include "public/platform/WebURL.h"
+
+namespace blink {
+
+// A simple wrapper around blink::mojom::PresentationSessionInfo. Since the
+// presentation API has not been Onion Soup-ed, there are portions implemented
+// in Blink and the embedder. Because it is not possible to use the STL and WTF
+// mojo bindings alongside each other, going between Blink and the embedder
+// requires this indirection.
+struct WebPresentationSessionInfo {
+  WebPresentationSessionInfo(const WebURL& url, const WebString& id)
+      : url(url), id(id) {}
+  WebURL url;
+  WebString id;
+};
+
+}  // namespace blink
+
+#endif  // WebPresentationSessionInfo_h