diff --git a/.gitignore b/.gitignore
index c2ab5fb..6211b63a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -412,6 +412,11 @@
 /third_party/nacl_sdk_binaries/
 /third_party/netty-tcnative/src
 /third_party/netty4/src
+/third_party/node/linux
+/third_party/node/mac
+/third_party/node/node_modules
+/third_party/node/*.tar.gz
+/third_party/node/win
 /third_party/nss
 /third_party/objenesis/lib/*.jar
 /third_party/omaha/src/omaha
diff --git a/DEPS b/DEPS
index 5aafd26f..283e747 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '17c1b84066cacf7ea797b04c678748600ef6ec36',
+  'v8_revision': '8ac979195637575c2ac7a0041ee0c9cd676a8b0a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -255,7 +255,7 @@
     Var('chromium_git') + '/external/github.com/google/pywebsocket.git' + '@' + '2d7b73c3acbd0f41dcab487ae5c97c6feae06ce2',
 
   'src/media/cdm/api':
-    Var('chromium_git') + '/chromium/cdm.git' + '@' + '6a62dcef02523e2d5be4defb68a7d9363c7389d2',
+    Var('chromium_git') + '/chromium/cdm.git' + '@' + '379a18058fee03b429bd5b4a330b78add56efec4',
 
   'src/third_party/mesa/src':
     Var('chromium_git') + '/chromium/deps/mesa.git' + '@' + 'ef811c6bd4de74e13e7035ca882cc77f85793fef',
@@ -1101,6 +1101,56 @@
                 '--version',
     ],
   },
+
+  # Pull down Node binaries for WebUI toolchain.
+  {
+    'name': 'node_linux64',
+    'pattern': '.',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=linux*',
+                '--extract',
+                '--no_auth',
+                '--bucket', 'chromium-nodejs/6.9.4',
+                '-s', 'src/third_party/node/linux/node-linux-x64.tar.gz.sha1',
+    ],
+  },
+  {
+    'name': 'node_mac',
+    'pattern': '.',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=darwin',
+                '--extract',
+                '--no_auth',
+                '--bucket', 'chromium-nodejs/6.9.4',
+                '-s', 'src/third_party/node/mac/node-darwin-x64.tar.gz.sha1',
+    ],
+  },
+  {
+    'name': 'node_win',
+    'pattern': '.',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=win32',
+                '--no_auth',
+                '--bucket', 'chromium-nodejs/6.9.4',
+                '-s', 'src/third_party/node/win/node.exe.sha1',
+    ],
+  },
+
+  # Pull down NPM dependencies for WebUI toolchain.
+  {
+    'name': 'webui_node_modules',
+    'pattern': '.',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--extract',
+                '--no_auth',
+                '--bucket', 'chromium-nodejs',
+                '-s', 'src/third_party/node/node_modules.tar.gz.sha1',
+    ],
+  },
 ]
 
 recursedeps = [
diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc
index c2fb501..c3fef0f 100644
--- a/android_webview/browser/aw_browser_context.cc
+++ b/android_webview/browser/aw_browser_context.cc
@@ -194,12 +194,12 @@
   // is given a GUID, stored in this file in the app's data directory.
   const FilePath guid_file_path =
       GetPath().Append(FILE_PATH_LITERAL("metrics_guid"));
-
   AwMetricsServiceClient::GetInstance()->Initialize(
       user_pref_service_.get(),
       content::BrowserContext::GetDefaultStoragePartition(this)->
           GetURLRequestContext(),
       guid_file_path);
+
   web_restriction_provider_.reset(
       new web_restrictions::WebRestrictionsClient());
   pref_change_registrar_.Add(
diff --git a/android_webview/browser/aw_metrics_service_client.cc b/android_webview/browser/aw_metrics_service_client.cc
index facd0fe..c2bdad0 100644
--- a/android_webview/browser/aw_metrics_service_client.cc
+++ b/android_webview/browser/aw_metrics_service_client.cc
@@ -4,218 +4,9 @@
 
 #include "android_webview/browser/aw_metrics_service_client.h"
 
-#include "android_webview/common/aw_version_info_values.h"
-#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/guid.h"
-#include "base/i18n/rtl.h"
-#include "components/metrics/call_stack_profile_metrics_provider.h"
-#include "components/metrics/enabled_state_provider.h"
-#include "components/metrics/gpu/gpu_metrics_provider.h"
-#include "components/metrics/metrics_pref_names.h"
-#include "components/metrics/metrics_service.h"
-#include "components/metrics/metrics_state_manager.h"
-#include "components/metrics/net/net_metrics_log_uploader.h"
-#include "components/metrics/profiler/profiler_metrics_provider.h"
-#include "components/metrics/ui/screen_info_metrics_provider.h"
-#include "components/metrics/url_constants.h"
-#include "components/prefs/pref_service.h"
-#include "content/public/browser/browser_thread.h"
-
 namespace android_webview {
 
-base::LazyInstance<AwMetricsServiceClient>::Leaky g_lazy_instance_;
-
-namespace {
-
-const int kUploadIntervalMinutes = 30;
-
-// Callbacks for metrics::MetricsStateManager::Create. Store/LoadClientInfo
-// allow Windows Chrome to back up ClientInfo. They're no-ops for WebView.
-
-void StoreClientInfo(const metrics::ClientInfo& client_info) {}
-
-std::unique_ptr<metrics::ClientInfo> LoadClientInfo() {
-  std::unique_ptr<metrics::ClientInfo> client_info;
-  return client_info;
-}
-
-// A GUID in text form is composed of 32 hex digits and 4 hyphens.
-const size_t GUID_SIZE = 32 + 4;
-
-void GetOrCreateGUID(const base::FilePath guid_file_path, std::string* guid) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
-
-  // Try to read an existing GUID.
-  if (base::ReadFileToStringWithMaxSize(guid_file_path, guid, GUID_SIZE)) {
-    if (base::IsValidGUID(*guid))
-      return;
-    else
-      LOG(ERROR) << "Found invalid GUID";
-  }
-
-  // We must write a new GUID.
-  *guid = base::GenerateGUID();
-  if (!base::WriteFile(guid_file_path, guid->c_str(), guid->size()))
-    LOG(ERROR) << "Failed to write new GUID";
-  return;
-}
-
-}  // namespace
-
-// static
-AwMetricsServiceClient* AwMetricsServiceClient::GetInstance() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  return g_lazy_instance_.Pointer();
-}
-
-void AwMetricsServiceClient::Initialize(
-    PrefService* pref_service,
-    net::URLRequestContextGetter* request_context,
-    const base::FilePath guid_file_path) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(!is_initialized_);
-
-  pref_service_ = pref_service;
-  request_context_ = request_context;
-
-  std::string* guid = new std::string;
-  // Initialization happens on the UI thread, but getting the GUID should happen
-  // on the file I/O thread. So we start to initialize, then post to get the
-  // GUID, and then pick up where we left off, back on the UI thread, in
-  // InitializeWithGUID.
-  content::BrowserThread::PostTaskAndReply(
-      content::BrowserThread::FILE,
-      FROM_HERE,
-      base::Bind(&GetOrCreateGUID, guid_file_path, guid),
-      base::Bind(&AwMetricsServiceClient::InitializeWithGUID,
-                 base::Unretained(this), base::Owned(guid)));
-}
-
-void AwMetricsServiceClient::InitializeWithGUID(std::string* guid) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(!is_initialized_);
-
-  pref_service_->SetString(metrics::prefs::kMetricsClientID, *guid);
-
-  metrics_state_manager_ = metrics::MetricsStateManager::Create(
-      pref_service_, this, base::Bind(&StoreClientInfo),
-      base::Bind(&LoadClientInfo));
-
-  metrics_service_.reset(new ::metrics::MetricsService(
-      metrics_state_manager_.get(), this, pref_service_));
-
-  metrics_service_->RegisterMetricsProvider(
-      std::unique_ptr<metrics::MetricsProvider>(
-          new metrics::NetworkMetricsProvider(
-              content::BrowserThread::GetBlockingPool())));
-
-  metrics_service_->RegisterMetricsProvider(
-      std::unique_ptr<metrics::MetricsProvider>(
-          new metrics::GPUMetricsProvider));
-
-  metrics_service_->RegisterMetricsProvider(
-      std::unique_ptr<metrics::MetricsProvider>(
-          new metrics::ScreenInfoMetricsProvider));
-
-  metrics_service_->RegisterMetricsProvider(
-      std::unique_ptr<metrics::MetricsProvider>(
-          new metrics::ProfilerMetricsProvider()));
-
-  metrics_service_->RegisterMetricsProvider(
-      std::unique_ptr<metrics::MetricsProvider>(
-          new metrics::CallStackProfileMetricsProvider));
-
-  metrics_service_->InitializeMetricsRecordingState();
-
-  is_initialized_ = true;
-
-  if (IsReportingEnabled())
-    metrics_service_->Start();
-}
-
-void AwMetricsServiceClient::SetMetricsEnabled(bool enabled) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  // If the client is already initialized, apply the setting immediately.
-  // Otherwise, it will be applied on initialization.
-  if (is_initialized_ && is_enabled_ != enabled) {
-    if (enabled)
-      metrics_service_->Start();
-    else
-      metrics_service_->Stop();
-  }
-  is_enabled_ = enabled;
-}
-
-bool AwMetricsServiceClient::IsConsentGiven() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  return is_enabled_;
-}
-
-metrics::MetricsService* AwMetricsServiceClient::GetMetricsService() {
-  return metrics_service_.get();
-}
-
-// In Chrome, UMA and Breakpad are enabled/disabled together by the same
-// checkbox and they share the same client ID (a.k.a. GUID). SetMetricsClientId
-// is intended to provide the ID to Breakpad. In WebView, UMA and Breakpad are
-// independent, so this is a no-op.
-
-void AwMetricsServiceClient::SetMetricsClientId(const std::string& client_id) {}
-
-int32_t AwMetricsServiceClient::GetProduct() {
-  return ::metrics::ChromeUserMetricsExtension::ANDROID_WEBVIEW;
-}
-
-std::string AwMetricsServiceClient::GetApplicationLocale() {
-  return base::i18n::GetConfiguredLocale();
-}
-
-bool AwMetricsServiceClient::GetBrand(std::string* brand_code) {
-  // WebView doesn't use brand codes.
-  return false;
-}
-
-metrics::SystemProfileProto::Channel AwMetricsServiceClient::GetChannel() {
-  // "Channel" means stable, beta, etc. WebView doesn't have channel info yet.
-  // TODO(paulmiller) Update this once we have channel info.
-  return metrics::SystemProfileProto::CHANNEL_UNKNOWN;
-}
-
-std::string AwMetricsServiceClient::GetVersionString() {
-  return PRODUCT_VERSION;
-}
-
-void AwMetricsServiceClient::InitializeSystemProfileMetrics(
-    const base::Closure& done_callback) {
-  done_callback.Run();
-}
-
-void AwMetricsServiceClient::CollectFinalMetricsForLog(
-    const base::Closure& done_callback) {
-  done_callback.Run();
-}
-
-std::unique_ptr<metrics::MetricsLogUploader>
-AwMetricsServiceClient::CreateUploader(
-    const std::string& server_url,
-    const std::string& mime_type,
-    const base::Callback<void(int)>& on_upload_complete) {
-  return std::unique_ptr<::metrics::MetricsLogUploader>(
-      new metrics::NetMetricsLogUploader(
-          request_context_, server_url, mime_type, on_upload_complete));
-}
-
-base::TimeDelta AwMetricsServiceClient::GetStandardUploadInterval() {
-  return base::TimeDelta::FromMinutes(kUploadIntervalMinutes);
-}
-
-AwMetricsServiceClient::AwMetricsServiceClient()
-    : is_initialized_(false),
-      is_enabled_(false),
-      pref_service_(nullptr),
-      request_context_(nullptr) {}
+AwMetricsServiceClient::AwMetricsServiceClient() {}
 
 AwMetricsServiceClient::~AwMetricsServiceClient() {}
 
diff --git a/android_webview/browser/aw_metrics_service_client.h b/android_webview/browser/aw_metrics_service_client.h
index 3bfc5123..82ac198 100644
--- a/android_webview/browser/aw_metrics_service_client.h
+++ b/android_webview/browser/aw_metrics_service_client.h
@@ -5,12 +5,6 @@
 #ifndef ANDROID_WEBVIEW_BROWSER_AW_METRICS_SERVICE_CLIENT_IMPL_H_
 #define ANDROID_WEBVIEW_BROWSER_AW_METRICS_SERVICE_CLIENT_IMPL_H_
 
-#include <memory>
-#include <string>
-
-#include "android_webview/browser/aw_metrics_service_client.h"
-#include "base/lazy_instance.h"
-#include "base/macros.h"
 #include "components/metrics/enabled_state_provider.h"
 #include "components/metrics/metrics_service_client.h"
 
@@ -20,69 +14,25 @@
 class FilePath;
 }
 
-namespace metrics {
-class MetricsStateManager;
-}
-
 namespace net {
 class URLRequestContextGetter;
 }
 
 namespace android_webview {
 
-// This singleton manages metrics for an app using any number of WebViews. The
-// homonymous Java class is responsible for turning metrics on and off. This
-// singleton must always be used on the same thread. (Currently the UI thread
-// is enforced, but it could be any thread.) This is to prevent enable/disable
-// race conditions, and because MetricsService is single-threaded.
-// Initialization is asynchronous; even after Initialize has returned, some
-// methods may not be ready to use (see below).
 class AwMetricsServiceClient : public metrics::MetricsServiceClient,
                                public metrics::EnabledStateProvider {
-  friend struct base::DefaultLazyInstanceTraits<AwMetricsServiceClient>;
-
  public:
-  // These may be called at any time.
   static AwMetricsServiceClient* GetInstance();
-  void Initialize(PrefService* pref_service,
-                  net::URLRequestContextGetter* request_context,
-                  const base::FilePath guid_file_path);
-  void SetMetricsEnabled(bool enabled);
+  virtual void Initialize(PrefService* pref_service,
+                          net::URLRequestContextGetter* request_context,
+                          const base::FilePath guid_file_path) = 0;
 
-  // metrics::EnabledStateProvider:
-  bool IsConsentGiven() override;
-
-  // These implement metrics::MetricsServiceClient. They must not be called
-  // until initialization has asynchronously finished.
-  metrics::MetricsService* GetMetricsService() override;
-  void SetMetricsClientId(const std::string& client_id) override;
-  int32_t GetProduct() override;
-  std::string GetApplicationLocale() override;
-  bool GetBrand(std::string* brand_code) override;
-  metrics::SystemProfileProto::Channel GetChannel() override;
-  std::string GetVersionString() override;
-  void InitializeSystemProfileMetrics(
-      const base::Closure& done_callback) override;
-  void CollectFinalMetricsForLog(const base::Closure& done_callback) override;
-  std::unique_ptr<metrics::MetricsLogUploader> CreateUploader(
-      const std::string& server_url,
-      const std::string& mime_type,
-      const base::Callback<void(int)>& on_upload_complete) override;
-  base::TimeDelta GetStandardUploadInterval() override;
-
- private:
+ protected:
   AwMetricsServiceClient();
   ~AwMetricsServiceClient() override;
 
-  void InitializeWithGUID(std::string* guid);
-
-  bool is_initialized_;
-  bool is_enabled_;
-  PrefService* pref_service_;
-  net::URLRequestContextGetter* request_context_;
-  std::unique_ptr<metrics::MetricsStateManager> metrics_state_manager_;
-  std::unique_ptr<metrics::MetricsService> metrics_service_;
-
+ private:
   DISALLOW_COPY_AND_ASSIGN(AwMetricsServiceClient);
 };
 
diff --git a/android_webview/glue/glue.gni b/android_webview/glue/glue.gni
index e3f044c..c85dcd6 100644
--- a/android_webview/glue/glue.gni
+++ b/android_webview/glue/glue.gni
@@ -6,6 +6,7 @@
 # ResourceRewrite.java need to be generated according 'glue' deps.
 glue_library_deps = [
   "//android_webview:android_webview_java",
+  "//android_webview:android_webview_platform_services_java",
   "//base:base_java",
   "//content/public/android:content_java",
   "//net/android:net_java",
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
index f98410d6..b7fb06b 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
@@ -24,6 +24,7 @@
 import android.webkit.GeolocationPermissions;
 import android.webkit.ServiceWorkerController;
 import android.webkit.TokenBindingService;
+import android.webkit.ValueCallback;
 import android.webkit.WebStorage;
 import android.webkit.WebView;
 import android.webkit.WebViewDatabase;
@@ -40,11 +41,13 @@
 import org.chromium.android_webview.AwContentsStatics;
 import org.chromium.android_webview.AwCookieManager;
 import org.chromium.android_webview.AwDevToolsServer;
+import org.chromium.android_webview.AwMetricsServiceClient;
 import org.chromium.android_webview.AwNetworkChangeNotifierRegistrationPolicy;
 import org.chromium.android_webview.AwQuotaManagerBridge;
 import org.chromium.android_webview.AwResource;
 import org.chromium.android_webview.AwSettings;
 import org.chromium.android_webview.HttpAuthDatabase;
+import org.chromium.android_webview.PlatformServiceBridge;
 import org.chromium.android_webview.ResourcesContextWrapperFactory;
 import org.chromium.base.BuildConfig;
 import org.chromium.base.CommandLine;
@@ -396,7 +399,7 @@
 
         // Make sure that ResourceProvider is initialized before starting the browser process.
         final String webViewPackageName = WebViewFactory.getLoadedPackageInfo().packageName;
-        Context context = ContextUtils.getApplicationContext();
+        final Context context = ContextUtils.getApplicationContext();
         setUpResources(webViewPackageName, context);
         initPlatSupportLibrary();
         initNetworkChangeNotifier(context);
@@ -405,6 +408,14 @@
         AwBrowserProcess.start();
         AwBrowserProcess.handleMinidumps(webViewPackageName);
 
+        PlatformServiceBridge.getInstance(context)
+                .queryMetricsSetting(new ValueCallback<Boolean>() {
+                    public void onReceiveValue(Boolean enabled) {
+                        ThreadUtils.assertOnUiThread();
+                        AwMetricsServiceClient.setConsentSetting(context, enabled);
+                    }
+                });
+
         if (isBuildDebuggable()) {
             setWebContentsDebuggingEnabled(true);
         }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java b/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java
index 32ee78b..52869fb 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwBrowserContext.java
@@ -24,13 +24,11 @@
     private AwGeolocationPermissions mGeolocationPermissions;
     private AwFormDatabase mFormDatabase;
     private AppWebMessagePortService mMessagePortService;
-    private AwMetricsServiceClient mMetricsServiceClient;
     private AwServiceWorkerController mServiceWorkerController;
     private Context mApplicationContext;
 
     public AwBrowserContext(SharedPreferences sharedPreferences, Context applicationContext) {
         mSharedPreferences = sharedPreferences;
-        mMetricsServiceClient = new AwMetricsServiceClient(applicationContext);
         mApplicationContext = applicationContext;
     }
 
@@ -55,10 +53,6 @@
         return mMessagePortService;
     }
 
-    public AwMetricsServiceClient getMetricsServiceClient() {
-        return mMetricsServiceClient;
-    }
-
     public AwServiceWorkerController getServiceWorkerController() {
         if (mServiceWorkerController == null) {
             mServiceWorkerController = new AwServiceWorkerController(mApplicationContext, this);
diff --git a/android_webview/java/src/org/chromium/android_webview/AwMetricsServiceClient.java b/android_webview/java/src/org/chromium/android_webview/AwMetricsServiceClient.java
index 09173a3..209c8a6 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwMetricsServiceClient.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwMetricsServiceClient.java
@@ -7,15 +7,25 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.webkit.ValueCallback;
 
 import org.chromium.base.Log;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 
 /**
- * Java twin of the homonymous C++ class. The Java side is only responsible for
- * switching metrics on and off. Since the setting is a platform feature, it
- * must be obtained through PlatformServiceBridge.
+ * Determines whether metrics should be enabled.
+ *
+ * This requires the following steps:
+ * 1) Check the platform's metrics consent setting.
+ * 2) Check if the app has opted out.
+ * 3) Wait for the native AwMetricsServiceClient to call nativeInitialized.
+ * 4) If enabled, inform the native AwMetricsServiceClient via nativeSetMetricsEnabled.
+ *
+ * Step 1 is done asynchronously and the result is passed to setConsentSetting, which does step 2.
+ * This happens in parallel with native AwMetricsServiceClient initialization; either
+ * nativeInitialized or setConsentSetting might fire first. Whichever fires second should call
+ * nativeSetMetricsEnabled.
  */
 @JNINamespace("android_webview")
 public class AwMetricsServiceClient {
@@ -25,10 +35,13 @@
     // reporting. See https://developer.android.com/reference/android/webkit/WebView.html
     private static final String OPT_OUT_META_DATA_STR = "android.webkit.WebView.MetricsOptOut";
 
-    private static boolean isAppOptedOut(Context applicationContext) {
+    private static boolean sIsClientReady; // Is the native AwMetricsServiceClient initialized?
+    private static boolean sShouldEnable; // Have steps 1 and 2 passed?
+
+    private static boolean isAppOptedOut(Context appContext) {
         try {
-            ApplicationInfo info = applicationContext.getPackageManager().getApplicationInfo(
-                    applicationContext.getPackageName(), PackageManager.GET_META_DATA);
+            ApplicationInfo info = appContext.getPackageManager().getApplicationInfo(
+                    appContext.getPackageName(), PackageManager.GET_META_DATA);
             if (info.metaData == null) {
                 // null means no such tag was found.
                 return false;
@@ -43,18 +56,27 @@
         }
     }
 
-    public AwMetricsServiceClient(Context applicationContext) {
-        if (isAppOptedOut(applicationContext)) {
+    public static void setConsentSetting(Context appContext, boolean userConsent) {
+        ThreadUtils.assertOnUiThread();
+
+        if (!userConsent || isAppOptedOut(appContext)) {
+            // Metrics defaults to off, so no need to call nativeSetMetricsEnabled(false).
             return;
         }
 
-        // Check if the user has consented.
-        PlatformServiceBridge.getInstance(applicationContext)
-                .setMetricsSettingListener(new ValueCallback<Boolean>() {
-                    public void onReceiveValue(Boolean enabled) {
-                        nativeSetMetricsEnabled(enabled);
-                    }
-                });
+        sShouldEnable = true;
+        if (sIsClientReady) {
+            nativeSetMetricsEnabled(true);
+        }
+    }
+
+    @CalledByNative
+    public static void nativeInitialized() {
+        ThreadUtils.assertOnUiThread();
+        sIsClientReady = true;
+        if (sShouldEnable) {
+            nativeSetMetricsEnabled(true);
+        }
     }
 
     public static native void nativeSetMetricsEnabled(boolean enabled);
diff --git a/android_webview/java/src/org/chromium/android_webview/PlatformServiceBridge.java b/android_webview/java/src/org/chromium/android_webview/PlatformServiceBridge.java
index 8818ce0f..b41d091 100644
--- a/android_webview/java/src/org/chromium/android_webview/PlatformServiceBridge.java
+++ b/android_webview/java/src/org/chromium/android_webview/PlatformServiceBridge.java
@@ -8,6 +8,7 @@
 import android.webkit.ValueCallback;
 
 import org.chromium.base.Log;
+import org.chromium.base.ThreadUtils;
 
 import java.lang.reflect.InvocationTargetException;
 
@@ -24,7 +25,9 @@
 
     protected PlatformServiceBridge() {}
 
-    public static PlatformServiceBridge getInstance(Context applicationContext) {
+    public static PlatformServiceBridge getInstance(Context appContext) {
+        ThreadUtils.assertOnUiThread(); // Avoid race conditions on sInstance.
+
         if (sInstance != null) {
             return sInstance;
         }
@@ -33,7 +36,7 @@
         try {
             Class<?> cls = Class.forName(PLATFORM_SERVICE_BRIDGE);
             sInstance = (PlatformServiceBridge) cls.getDeclaredConstructor(Context.class)
-                    .newInstance(applicationContext);
+                    .newInstance(appContext);
             return sInstance;
         } catch (ClassNotFoundException e) {
             // This is not an error; it just means this device doesn't have specialized services.
@@ -49,13 +52,21 @@
         return sInstance;
     }
 
-    // Try to enable WebView to use Google Play Services (a.k.a. GMS) APIs. Return true on success.
-    // Do not use GMS APIs before this has returned true, or if it returns false. This can be called
-    // from multiple threads, so long as no thread uses GMS APIs before at least one call has
-    // returned true. (The easy way is for each thread to wait for its own call to return true.)
+    // TODO(paulmiller): remove; replaced by canUseGms
     public boolean tryEnableGms() {
         return false;
     }
 
-    public void setMetricsSettingListener(ValueCallback<Boolean> callback) {}
+    // Can WebView use Google Play Services (a.k.a. GMS)?
+    public boolean canUseGms() {
+        return false;
+    }
+
+    // Overriding implementations may call "callback" asynchronously. For simplicity (and not
+    // because of any technical limitation) we require that "queryMetricsSetting" and "callback"
+    // both get called on WebView's UI thread.
+    public void queryMetricsSetting(ValueCallback<Boolean> callback) {
+        ThreadUtils.assertOnUiThread();
+        callback.onReceiveValue(false);
+    }
 }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwStrictModeTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwStrictModeTest.java
index d12cd37..898d9eb 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwStrictModeTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwStrictModeTest.java
@@ -7,7 +7,6 @@
 import android.os.StrictMode;
 import android.support.test.filters.LargeTest;
 
-import org.chromium.android_webview.AwBrowserProcess;
 import org.chromium.base.test.util.Feature;
 
 /**
@@ -99,11 +98,11 @@
 
     private void startEverythingSync() throws Exception {
         getActivity();
+        createAwBrowserContext();
+        startBrowserProcess();
         getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                createAwBrowserContext();
-                AwBrowserProcess.start();
                 mAwTestContainerView = createAwTestContainerView(mContentsClient);
             }
         });
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java
index d37e9b4..a0ce792 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwTestBase.java
@@ -98,12 +98,18 @@
         if (mBrowserContext != null) {
             throw new AndroidRuntimeException("There should only be one browser context.");
         }
-        Context appContext = getInstrumentation().getTargetContext().getApplicationContext();
-        mBrowserContext = new AwBrowserContext(new InMemorySharedPreferences(), appContext);
+        final InMemorySharedPreferences prefs = new InMemorySharedPreferences();
+        final Context appContext = getInstrumentation().getTargetContext().getApplicationContext();
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mBrowserContext = new AwBrowserContext(prefs, appContext);
+            }
+        });
     }
 
     protected void startBrowserProcess() throws Exception {
-        // The activity must be launched in order for proper webview statics to be setup.
+        // The Activity must be launched in order for proper webview statics to be setup.
         getActivity();
         getInstrumentation().runOnMainSync(new Runnable() {
             @Override
diff --git a/android_webview/native/BUILD.gn b/android_webview/native/BUILD.gn
index 6eaba42..2b2d04e 100644
--- a/android_webview/native/BUILD.gn
+++ b/android_webview/native/BUILD.gn
@@ -68,8 +68,8 @@
     "aw_locale_manager_impl.h",
     "aw_media_url_interceptor.cc",
     "aw_media_url_interceptor.h",
-    "aw_metrics_switch.cc",
-    "aw_metrics_switch.h",
+    "aw_metrics_service_client_impl.cc",
+    "aw_metrics_service_client_impl.h",
     "aw_pdf_exporter.cc",
     "aw_pdf_exporter.h",
     "aw_picture.cc",
diff --git a/android_webview/native/android_webview_jni_registrar.cc b/android_webview/native/android_webview_jni_registrar.cc
index 7697aa14..9523993 100644
--- a/android_webview/native/android_webview_jni_registrar.cc
+++ b/android_webview/native/android_webview_jni_registrar.cc
@@ -14,7 +14,7 @@
 #include "android_webview/native/aw_form_database.h"
 #include "android_webview/native/aw_gl_functor.h"
 #include "android_webview/native/aw_http_auth_handler.h"
-#include "android_webview/native/aw_metrics_switch.h"
+#include "android_webview/native/aw_metrics_service_client_impl.h"
 #include "android_webview/native/aw_pdf_exporter.h"
 #include "android_webview/native/aw_picture.h"
 #include "android_webview/native/aw_quota_manager_bridge_impl.h"
diff --git a/android_webview/native/aw_metrics_service_client_impl.cc b/android_webview/native/aw_metrics_service_client_impl.cc
new file mode 100644
index 0000000..3668f26
--- /dev/null
+++ b/android_webview/native/aw_metrics_service_client_impl.cc
@@ -0,0 +1,235 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "android_webview/native/aw_metrics_service_client_impl.h"
+
+#include "android_webview/common/aw_version_info_values.h"
+#include "android_webview/jni/AwMetricsServiceClient_jni.h"
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/guid.h"
+#include "base/i18n/rtl.h"
+#include "components/metrics/call_stack_profile_metrics_provider.h"
+#include "components/metrics/enabled_state_provider.h"
+#include "components/metrics/gpu/gpu_metrics_provider.h"
+#include "components/metrics/metrics_pref_names.h"
+#include "components/metrics/metrics_service.h"
+#include "components/metrics/metrics_state_manager.h"
+#include "components/metrics/net/net_metrics_log_uploader.h"
+#include "components/metrics/profiler/profiler_metrics_provider.h"
+#include "components/metrics/ui/screen_info_metrics_provider.h"
+#include "components/metrics/url_constants.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace android_webview {
+
+base::LazyInstance<AwMetricsServiceClientImpl>::Leaky g_lazy_instance_;
+
+namespace {
+
+const int kUploadIntervalMinutes = 30;
+
+// Callbacks for metrics::MetricsStateManager::Create. Store/LoadClientInfo
+// allow Windows Chrome to back up ClientInfo. They're no-ops for WebView.
+
+void StoreClientInfo(const metrics::ClientInfo& client_info) {}
+
+std::unique_ptr<metrics::ClientInfo> LoadClientInfo() {
+  std::unique_ptr<metrics::ClientInfo> client_info;
+  return client_info;
+}
+
+// A GUID in text form is composed of 32 hex digits and 4 hyphens.
+const size_t GUID_SIZE = 32 + 4;
+
+void GetOrCreateGUID(const base::FilePath guid_file_path, std::string* guid) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
+
+  // Try to read an existing GUID.
+  if (base::ReadFileToStringWithMaxSize(guid_file_path, guid, GUID_SIZE)) {
+    if (base::IsValidGUID(*guid))
+      return;
+    else
+      LOG(ERROR) << "Overwriting invalid GUID";
+  }
+
+  // We must write a new GUID.
+  *guid = base::GenerateGUID();
+  if (!base::WriteFile(guid_file_path, guid->c_str(), guid->size())) {
+    // If writing fails, proceed anyway with the new GUID. It won't be persisted
+    // to the next run, but we can still collect metrics with this 1-time GUID.
+    LOG(ERROR) << "Failed to write new GUID";
+  }
+  return;
+}
+
+}  // namespace
+
+// static
+AwMetricsServiceClient* AwMetricsServiceClient::GetInstance() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  return g_lazy_instance_.Pointer();
+}
+
+void AwMetricsServiceClientImpl::Initialize(
+    PrefService* pref_service,
+    net::URLRequestContextGetter* request_context,
+    const base::FilePath guid_file_path) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  DCHECK(pref_service_ == nullptr); // Initialize should only happen once.
+  DCHECK(request_context_ == nullptr);
+  pref_service_ = pref_service;
+  request_context_ = request_context;
+
+  std::string* guid = new std::string;
+  // Initialization happens on the UI thread, but getting the GUID should happen
+  // on the file I/O thread. So we start to initialize, then post to get the
+  // GUID, and then pick up where we left off, back on the UI thread, in
+  // InitializeWithGUID.
+  content::BrowserThread::PostTaskAndReply(
+      content::BrowserThread::FILE,
+      FROM_HERE,
+      base::Bind(&GetOrCreateGUID, guid_file_path, guid),
+      base::Bind(&AwMetricsServiceClientImpl::InitializeWithGUID,
+                 base::Unretained(this), base::Owned(guid)));
+}
+
+void AwMetricsServiceClientImpl::InitializeWithGUID(std::string* guid) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  pref_service_->SetString(metrics::prefs::kMetricsClientID, *guid);
+
+  metrics_state_manager_ = metrics::MetricsStateManager::Create(
+      pref_service_, this, base::Bind(&StoreClientInfo),
+      base::Bind(&LoadClientInfo));
+
+  metrics_service_.reset(new ::metrics::MetricsService(
+      metrics_state_manager_.get(), this, pref_service_));
+
+  metrics_service_->RegisterMetricsProvider(
+      std::unique_ptr<metrics::MetricsProvider>(
+          new metrics::NetworkMetricsProvider(
+              content::BrowserThread::GetBlockingPool())));
+
+  metrics_service_->RegisterMetricsProvider(
+      std::unique_ptr<metrics::MetricsProvider>(
+          new metrics::GPUMetricsProvider));
+
+  metrics_service_->RegisterMetricsProvider(
+      std::unique_ptr<metrics::MetricsProvider>(
+          new metrics::ScreenInfoMetricsProvider));
+
+  metrics_service_->RegisterMetricsProvider(
+      std::unique_ptr<metrics::MetricsProvider>(
+          new metrics::ProfilerMetricsProvider()));
+
+  metrics_service_->RegisterMetricsProvider(
+      std::unique_ptr<metrics::MetricsProvider>(
+          new metrics::CallStackProfileMetricsProvider));
+
+  metrics_service_->InitializeMetricsRecordingState();
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_AwMetricsServiceClient_nativeInitialized(env);
+}
+
+bool AwMetricsServiceClientImpl::IsConsentGiven() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  return is_enabled_;
+}
+
+void AwMetricsServiceClientImpl::SetMetricsEnabled(bool enabled) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  if (is_enabled_ != enabled) {
+    if (enabled) {
+      // TODO(paulmiller): Actually enable metrics when the server-side is ready
+      //metrics_service_->Start();
+    } else {
+      metrics_service_->Stop();
+    }
+    is_enabled_ = enabled;
+  }
+}
+
+metrics::MetricsService* AwMetricsServiceClientImpl::GetMetricsService() {
+  return metrics_service_.get();
+}
+
+// In Chrome, UMA and Breakpad are enabled/disabled together by the same
+// checkbox and they share the same client ID (a.k.a. GUID). SetMetricsClientId
+// is intended to provide the ID to Breakpad. In WebView, UMA and Breakpad are
+// independent, so this is a no-op.
+void AwMetricsServiceClientImpl::SetMetricsClientId(
+    const std::string& client_id) {}
+
+int32_t AwMetricsServiceClientImpl::GetProduct() {
+  return ::metrics::ChromeUserMetricsExtension::ANDROID_WEBVIEW;
+}
+
+std::string AwMetricsServiceClientImpl::GetApplicationLocale() {
+  return base::i18n::GetConfiguredLocale();
+}
+
+bool AwMetricsServiceClientImpl::GetBrand(std::string* brand_code) {
+  // WebView doesn't use brand codes.
+  return false;
+}
+
+metrics::SystemProfileProto::Channel AwMetricsServiceClientImpl::GetChannel() {
+  // "Channel" means stable, beta, etc. WebView doesn't have channel info yet.
+  // TODO(paulmiller) Update this once we have channel info.
+  return metrics::SystemProfileProto::CHANNEL_UNKNOWN;
+}
+
+std::string AwMetricsServiceClientImpl::GetVersionString() {
+  return PRODUCT_VERSION;
+}
+
+void AwMetricsServiceClientImpl::InitializeSystemProfileMetrics(
+    const base::Closure& done_callback) {
+  done_callback.Run();
+}
+
+void AwMetricsServiceClientImpl::CollectFinalMetricsForLog(
+    const base::Closure& done_callback) {
+  done_callback.Run();
+}
+
+std::unique_ptr<metrics::MetricsLogUploader>
+AwMetricsServiceClientImpl::CreateUploader(
+    const std::string& server_url,
+    const std::string& mime_type,
+    const base::Callback<void(int)>& on_upload_complete) {
+  return std::unique_ptr<::metrics::MetricsLogUploader>(
+      new metrics::NetMetricsLogUploader(
+          request_context_, server_url, mime_type, on_upload_complete));
+}
+
+base::TimeDelta AwMetricsServiceClientImpl::GetStandardUploadInterval() {
+  return base::TimeDelta::FromMinutes(kUploadIntervalMinutes);
+}
+
+AwMetricsServiceClientImpl::AwMetricsServiceClientImpl()
+    : is_enabled_(false),
+      pref_service_(nullptr),
+      request_context_(nullptr) {}
+
+AwMetricsServiceClientImpl::~AwMetricsServiceClientImpl() {}
+
+// static
+void SetMetricsEnabled(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jclass>& jcaller,
+    jboolean enabled) {
+  g_lazy_instance_.Pointer()->SetMetricsEnabled(enabled);
+}
+
+bool RegisterAwMetricsServiceClient(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace android_webview
diff --git a/android_webview/native/aw_metrics_service_client_impl.h b/android_webview/native/aw_metrics_service_client_impl.h
new file mode 100644
index 0000000..75242ad
--- /dev/null
+++ b/android_webview/native/aw_metrics_service_client_impl.h
@@ -0,0 +1,80 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ANDROID_WEBVIEW_NATIVE_AW_METRICS_SERVICE_CLIENT_IMPL_
+#define ANDROID_WEBVIEW_NATIVE_AW_METRICS_SERVICE_CLIENT_IMPL_
+
+#include "android_webview/browser/aw_metrics_service_client.h"
+
+#include <jni.h>
+#include <string>
+
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+
+namespace metrics {
+class MetricsStateManager;
+}
+
+namespace android_webview {
+
+// This singleton manages metrics for an app using any number of WebViews. It
+// must always be used on the same thread. (Currently the UI thread is enforced,
+// but it could be any thread.) This is to prevent enable/disable race
+// conditions, and because MetricsService is single-threaded. Initialization is
+// asynchronous; even after Initialize has returned, some methods may not be
+// ready to use (see below).
+class AwMetricsServiceClientImpl : public AwMetricsServiceClient {
+  friend struct base::DefaultLazyInstanceTraits<AwMetricsServiceClientImpl>;
+
+ public:
+  void Initialize(PrefService* pref_service,
+                  net::URLRequestContextGetter* request_context,
+                  const base::FilePath guid_file_path) override;
+
+  // metrics::EnabledStateProvider implementation
+  bool IsConsentGiven() override;
+
+  // The below functions must not be called until initialization has
+  // asynchronously finished.
+
+  void SetMetricsEnabled(bool enabled);
+
+  // metrics::MetricsServiceClient implementation
+  metrics::MetricsService* GetMetricsService() override;
+  void SetMetricsClientId(const std::string& client_id) override;
+  int32_t GetProduct() override;
+  std::string GetApplicationLocale() override;
+  bool GetBrand(std::string* brand_code) override;
+  metrics::SystemProfileProto::Channel GetChannel() override;
+  std::string GetVersionString() override;
+  void InitializeSystemProfileMetrics(
+      const base::Closure& done_callback) override;
+  void CollectFinalMetricsForLog(const base::Closure& done_callback) override;
+  std::unique_ptr<metrics::MetricsLogUploader> CreateUploader(
+      const std::string& server_url,
+      const std::string& mime_type,
+      const base::Callback<void(int)>& on_upload_complete) override;
+  base::TimeDelta GetStandardUploadInterval() override;
+
+ private:
+  AwMetricsServiceClientImpl();
+  ~AwMetricsServiceClientImpl() override;
+
+  void InitializeWithGUID(std::string* guid);
+
+  bool is_enabled_;
+  PrefService* pref_service_;
+  net::URLRequestContextGetter* request_context_;
+  std::unique_ptr<metrics::MetricsStateManager> metrics_state_manager_;
+  std::unique_ptr<metrics::MetricsService> metrics_service_;
+
+  DISALLOW_COPY_AND_ASSIGN(AwMetricsServiceClientImpl);
+};
+
+bool RegisterAwMetricsServiceClient(JNIEnv* env);
+
+}  // namespace android_webview
+
+#endif  // ANDROID_WEBVIEW_NATIVE_AW_METRICS_SWITCH_
diff --git a/android_webview/native/aw_metrics_switch.cc b/android_webview/native/aw_metrics_switch.cc
deleted file mode 100644
index 44ab70a3b..0000000
--- a/android_webview/native/aw_metrics_switch.cc
+++ /dev/null
@@ -1,23 +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 "android_webview/native/aw_metrics_switch.h"
-
-#include "android_webview/browser/aw_metrics_service_client.h"
-#include "android_webview/jni/AwMetricsServiceClient_jni.h"
-
-namespace android_webview {
-
-static void SetMetricsEnabled(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jclass>& jcaller,
-    jboolean enabled) {
-  AwMetricsServiceClient::GetInstance()->SetMetricsEnabled(enabled);
-}
-
-bool RegisterAwMetricsServiceClient(JNIEnv* env) {
-  return RegisterNativesImpl(env);
-}
-
-}  // namespace android_webview
diff --git a/android_webview/native/aw_metrics_switch.h b/android_webview/native/aw_metrics_switch.h
deleted file mode 100644
index 3f07a60..0000000
--- a/android_webview/native/aw_metrics_switch.h
+++ /dev/null
@@ -1,16 +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 ANDROID_WEBVIEW_NATIVE_AW_METRICS_SWITCH_
-#define ANDROID_WEBVIEW_NATIVE_AW_METRICS_SWITCH_
-
-#include <jni.h>
-
-namespace android_webview {
-
-bool RegisterAwMetricsServiceClient(JNIEnv* env);
-
-}  // namespace android_webview
-
-#endif  // ANDROID_WEBVIEW_NATIVE_AW_METRICS_SWITCH_
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 002a70bf..bdecf4a 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2042,6 +2042,7 @@
     "task_scheduler/test_utils.h",
     "template_util_unittest.cc",
     "test/histogram_tester_unittest.cc",
+    "test/mock_callback_unittest.cc",
     "test/scoped_mock_time_message_loop_task_runner_unittest.cc",
     "test/scoped_task_scheduler_unittest.cc",
     "test/test_pending_task_unittest.cc",
diff --git a/base/json/json_value_converter.h b/base/json/json_value_converter.h
index 187c4c4..68ebfa2 100644
--- a/base/json/json_value_converter.h
+++ b/base/json/json_value_converter.h
@@ -14,7 +14,7 @@
 #include "base/base_export.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
+#include "base/memory/ptr_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_piece.h"
 #include "base/values.h"
@@ -65,9 +65,9 @@
 //     }
 //   };
 //
-// For repeated field, we just assume ScopedVector for its container
-// and you can put RegisterRepeatedInt or some other types.  Use
-// RegisterRepeatedMessage for nested repeated fields.
+// For repeated field, we just assume std::vector<std::unique_ptr<ElementType>>
+// for its container and you can put RegisterRepeatedInt or some other types.
+// Use RegisterRepeatedMessage for nested repeated fields.
 //
 // Sometimes JSON format uses string representations for other types such
 // like enum, timestamp, or URL.  You can use RegisterCustomField method
@@ -247,12 +247,13 @@
 };
 
 template <typename Element>
-class RepeatedValueConverter : public ValueConverter<ScopedVector<Element> > {
+class RepeatedValueConverter
+    : public ValueConverter<std::vector<std::unique_ptr<Element>>> {
  public:
   RepeatedValueConverter() {}
 
   bool Convert(const base::Value& value,
-               ScopedVector<Element>* field) const override {
+               std::vector<std::unique_ptr<Element>>* field) const override {
     const base::ListValue* list = NULL;
     if (!value.GetAsList(&list)) {
       // The field is not a list.
@@ -267,7 +268,7 @@
 
       std::unique_ptr<Element> e(new Element);
       if (basic_converter_.Convert(*element, e.get())) {
-        field->push_back(e.release());
+        field->push_back(std::move(e));
       } else {
         DVLOG(1) << "failure at " << i << "-th element";
         return false;
@@ -283,12 +284,12 @@
 
 template <typename NestedType>
 class RepeatedMessageConverter
-    : public ValueConverter<ScopedVector<NestedType> > {
+    : public ValueConverter<std::vector<std::unique_ptr<NestedType>>> {
  public:
   RepeatedMessageConverter() {}
 
   bool Convert(const base::Value& value,
-               ScopedVector<NestedType>* field) const override {
+               std::vector<std::unique_ptr<NestedType>>* field) const override {
     const base::ListValue* list = NULL;
     if (!value.GetAsList(&list))
       return false;
@@ -301,7 +302,7 @@
 
       std::unique_ptr<NestedType> nested(new NestedType);
       if (converter_.Convert(*element, nested.get())) {
-        field->push_back(nested.release());
+        field->push_back(std::move(nested));
       } else {
         DVLOG(1) << "failure at " << i << "-th element";
         return false;
@@ -317,7 +318,7 @@
 
 template <typename NestedType>
 class RepeatedCustomValueConverter
-    : public ValueConverter<ScopedVector<NestedType> > {
+    : public ValueConverter<std::vector<std::unique_ptr<NestedType>>> {
  public:
   typedef bool(*ConvertFunc)(const base::Value* value, NestedType* field);
 
@@ -325,7 +326,7 @@
       : convert_func_(convert_func) {}
 
   bool Convert(const base::Value& value,
-               ScopedVector<NestedType>* field) const override {
+               std::vector<std::unique_ptr<NestedType>>* field) const override {
     const base::ListValue* list = NULL;
     if (!value.GetAsList(&list))
       return false;
@@ -338,7 +339,7 @@
 
       std::unique_ptr<NestedType> nested(new NestedType);
       if ((*convert_func_)(element, nested.get())) {
-        field->push_back(nested.release());
+        field->push_back(std::move(nested));
       } else {
         DVLOG(1) << "failure at " << i << "-th element";
         return false;
@@ -364,41 +365,42 @@
 
   void RegisterIntField(const std::string& field_name,
                         int StructType::* field) {
-    fields_.push_back(new internal::FieldConverter<StructType, int>(
+    fields_.push_back(MakeUnique<internal::FieldConverter<StructType, int>>(
         field_name, field, new internal::BasicValueConverter<int>));
   }
 
   void RegisterStringField(const std::string& field_name,
                            std::string StructType::* field) {
-    fields_.push_back(new internal::FieldConverter<StructType, std::string>(
-        field_name, field, new internal::BasicValueConverter<std::string>));
+    fields_.push_back(
+        MakeUnique<internal::FieldConverter<StructType, std::string>>(
+            field_name, field, new internal::BasicValueConverter<std::string>));
   }
 
   void RegisterStringField(const std::string& field_name,
                            string16 StructType::* field) {
-    fields_.push_back(new internal::FieldConverter<StructType, string16>(
-        field_name, field, new internal::BasicValueConverter<string16>));
+    fields_.push_back(
+        MakeUnique<internal::FieldConverter<StructType, string16>>(
+            field_name, field, new internal::BasicValueConverter<string16>));
   }
 
   void RegisterBoolField(const std::string& field_name,
                          bool StructType::* field) {
-    fields_.push_back(new internal::FieldConverter<StructType, bool>(
+    fields_.push_back(MakeUnique<internal::FieldConverter<StructType, bool>>(
         field_name, field, new internal::BasicValueConverter<bool>));
   }
 
   void RegisterDoubleField(const std::string& field_name,
                            double StructType::* field) {
-    fields_.push_back(new internal::FieldConverter<StructType, double>(
+    fields_.push_back(MakeUnique<internal::FieldConverter<StructType, double>>(
         field_name, field, new internal::BasicValueConverter<double>));
   }
 
   template <class NestedType>
   void RegisterNestedField(
       const std::string& field_name, NestedType StructType::* field) {
-    fields_.push_back(new internal::FieldConverter<StructType, NestedType>(
-            field_name,
-            field,
-            new internal::NestedValueConverter<NestedType>));
+    fields_.push_back(
+        MakeUnique<internal::FieldConverter<StructType, NestedType>>(
+            field_name, field, new internal::NestedValueConverter<NestedType>));
   }
 
   template <typename FieldType>
@@ -406,10 +408,10 @@
       const std::string& field_name,
       FieldType StructType::* field,
       bool (*convert_func)(const StringPiece&, FieldType*)) {
-    fields_.push_back(new internal::FieldConverter<StructType, FieldType>(
-        field_name,
-        field,
-        new internal::CustomFieldConverter<FieldType>(convert_func)));
+    fields_.push_back(
+        MakeUnique<internal::FieldConverter<StructType, FieldType>>(
+            field_name, field,
+            new internal::CustomFieldConverter<FieldType>(convert_func)));
   }
 
   template <typename FieldType>
@@ -417,71 +419,76 @@
       const std::string& field_name,
       FieldType StructType::* field,
       bool (*convert_func)(const base::Value*, FieldType*)) {
-    fields_.push_back(new internal::FieldConverter<StructType, FieldType>(
-        field_name,
-        field,
-        new internal::ValueFieldConverter<FieldType>(convert_func)));
+    fields_.push_back(
+        MakeUnique<internal::FieldConverter<StructType, FieldType>>(
+            field_name, field,
+            new internal::ValueFieldConverter<FieldType>(convert_func)));
   }
 
-  void RegisterRepeatedInt(const std::string& field_name,
-                           ScopedVector<int> StructType::* field) {
+  void RegisterRepeatedInt(
+      const std::string& field_name,
+      std::vector<std::unique_ptr<int>> StructType::*field) {
     fields_.push_back(
-        new internal::FieldConverter<StructType, ScopedVector<int> >(
+        MakeUnique<internal::FieldConverter<StructType,
+                                            std::vector<std::unique_ptr<int>>>>(
             field_name, field, new internal::RepeatedValueConverter<int>));
   }
 
-  void RegisterRepeatedString(const std::string& field_name,
-                              ScopedVector<std::string> StructType::* field) {
+  void RegisterRepeatedString(
+      const std::string& field_name,
+      std::vector<std::unique_ptr<std::string>> StructType::*field) {
     fields_.push_back(
-        new internal::FieldConverter<StructType, ScopedVector<std::string> >(
-            field_name,
-            field,
+        MakeUnique<internal::FieldConverter<
+            StructType, std::vector<std::unique_ptr<std::string>>>>(
+            field_name, field,
             new internal::RepeatedValueConverter<std::string>));
   }
 
-  void RegisterRepeatedString(const std::string& field_name,
-                              ScopedVector<string16> StructType::* field) {
-    fields_.push_back(
-        new internal::FieldConverter<StructType, ScopedVector<string16> >(
-            field_name,
-            field,
-            new internal::RepeatedValueConverter<string16>));
+  void RegisterRepeatedString(
+      const std::string& field_name,
+      std::vector<std::unique_ptr<string16>> StructType::*field) {
+    fields_.push_back(MakeUnique<internal::FieldConverter<
+                          StructType, std::vector<std::unique_ptr<string16>>>>(
+        field_name, field, new internal::RepeatedValueConverter<string16>));
   }
 
-  void RegisterRepeatedDouble(const std::string& field_name,
-                              ScopedVector<double> StructType::* field) {
-    fields_.push_back(
-        new internal::FieldConverter<StructType, ScopedVector<double> >(
-            field_name, field, new internal::RepeatedValueConverter<double>));
+  void RegisterRepeatedDouble(
+      const std::string& field_name,
+      std::vector<std::unique_ptr<double>> StructType::*field) {
+    fields_.push_back(MakeUnique<internal::FieldConverter<
+                          StructType, std::vector<std::unique_ptr<double>>>>(
+        field_name, field, new internal::RepeatedValueConverter<double>));
   }
 
-  void RegisterRepeatedBool(const std::string& field_name,
-                            ScopedVector<bool> StructType::* field) {
-    fields_.push_back(
-        new internal::FieldConverter<StructType, ScopedVector<bool> >(
-            field_name, field, new internal::RepeatedValueConverter<bool>));
+  void RegisterRepeatedBool(
+      const std::string& field_name,
+      std::vector<std::unique_ptr<bool>> StructType::*field) {
+    fields_.push_back(MakeUnique<internal::FieldConverter<
+                          StructType, std::vector<std::unique_ptr<bool>>>>(
+        field_name, field, new internal::RepeatedValueConverter<bool>));
   }
 
   template <class NestedType>
   void RegisterRepeatedCustomValue(
       const std::string& field_name,
-      ScopedVector<NestedType> StructType::* field,
+      std::vector<std::unique_ptr<NestedType>> StructType::*field,
       bool (*convert_func)(const base::Value*, NestedType*)) {
     fields_.push_back(
-        new internal::FieldConverter<StructType, ScopedVector<NestedType> >(
-            field_name,
-            field,
+        MakeUnique<internal::FieldConverter<
+            StructType, std::vector<std::unique_ptr<NestedType>>>>(
+            field_name, field,
             new internal::RepeatedCustomValueConverter<NestedType>(
                 convert_func)));
   }
 
   template <class NestedType>
-  void RegisterRepeatedMessage(const std::string& field_name,
-                               ScopedVector<NestedType> StructType::* field) {
+  void RegisterRepeatedMessage(
+      const std::string& field_name,
+      std::vector<std::unique_ptr<NestedType>> StructType::*field) {
     fields_.push_back(
-        new internal::FieldConverter<StructType, ScopedVector<NestedType> >(
-            field_name,
-            field,
+        MakeUnique<internal::FieldConverter<
+            StructType, std::vector<std::unique_ptr<NestedType>>>>(
+            field_name, field,
             new internal::RepeatedMessageConverter<NestedType>));
   }
 
@@ -492,7 +499,7 @@
 
     for (size_t i = 0; i < fields_.size(); ++i) {
       const internal::FieldConverterBase<StructType>* field_converter =
-          fields_[i];
+          fields_[i].get();
       const base::Value* field = NULL;
       if (dictionary_value->Get(field_converter->field_path(), &field)) {
         if (!field_converter->ConvertField(*field, output)) {
@@ -505,7 +512,8 @@
   }
 
  private:
-  ScopedVector<internal::FieldConverterBase<StructType> > fields_;
+  std::vector<std::unique_ptr<internal::FieldConverterBase<StructType>>>
+      fields_;
 
   DISALLOW_COPY_AND_ASSIGN(JSONValueConverter);
 };
diff --git a/base/json/json_value_converter_unittest.cc b/base/json/json_value_converter_unittest.cc
index 56ade24..6a603d3a 100644
--- a/base/json/json_value_converter_unittest.cc
+++ b/base/json/json_value_converter_unittest.cc
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "base/json/json_reader.h"
-#include "base/memory/scoped_vector.h"
 #include "base/strings/string_piece.h"
 #include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -27,8 +26,8 @@
   bool baz;
   bool bstruct;
   SimpleEnum simple_enum;
-  ScopedVector<int> ints;
-  ScopedVector<std::string> string_values;
+  std::vector<std::unique_ptr<int>> ints;
+  std::vector<std::unique_ptr<std::string>> string_values;
   SimpleMessage() : foo(0), baz(false), bstruct(false), simple_enum(FOO) {}
 
   static bool ParseSimpleEnum(const StringPiece& value, SimpleEnum* field) {
@@ -80,7 +79,7 @@
 struct NestedMessage {
   double foo;
   SimpleMessage child;
-  ScopedVector<SimpleMessage> children;
+  std::vector<std::unique_ptr<SimpleMessage>> children;
 
   NestedMessage() : foo(0) {}
 
@@ -163,7 +162,7 @@
   EXPECT_EQ("value_2", *message.child.string_values[1]);
 
   EXPECT_EQ(2, static_cast<int>(message.children.size()));
-  const SimpleMessage* first_child = message.children[0];
+  const SimpleMessage* first_child = message.children[0].get();
   ASSERT_TRUE(first_child);
   EXPECT_EQ(2, first_child->foo);
   EXPECT_EQ("foobar", first_child->bar);
@@ -172,7 +171,7 @@
   ASSERT_EQ(1U, first_child->string_values.size());
   EXPECT_EQ("value_1", *first_child->string_values[0]);
 
-  const SimpleMessage* second_child = message.children[1];
+  const SimpleMessage* second_child = message.children[1].get();
   ASSERT_TRUE(second_child);
   EXPECT_EQ(3, second_child->foo);
   EXPECT_EQ("barbaz", second_child->bar);
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index 265ef44a..256cae2 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -48,6 +48,7 @@
     "launcher/test_result.h",
     "launcher/test_results_tracker.h",
     "launcher/unit_test_launcher.h",
+    "mock_callback.h",
     "mock_chrome_application_mac.h",
     "mock_chrome_application_mac.mm",
     "mock_devices_changed_observer.cc",
diff --git a/base/test/mock_callback.h b/base/test/mock_callback.h
new file mode 100644
index 0000000..7ac4d346
--- /dev/null
+++ b/base/test/mock_callback.h
@@ -0,0 +1,366 @@
+// This file was GENERATED by command:
+//     pump.py mock_callback.h.pump
+// DO NOT EDIT BY HAND!!!
+
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Analogous to GMock's built-in MockFunction, but for base::Callback instead of
+// std::function. It takes the full callback type as a parameter, so that it can
+// support both OnceCallback and RepeatingCallback.
+//
+// Use:
+//   using FooCallback = base::Callback<int(std::string)>;
+//
+//   TEST(FooTest, RunsCallbackWithBarArgument) {
+//     base::MockCallback<FooCallback> callback;
+//     EXPECT_CALL(callback, Run("bar")).WillOnce(Return(1));
+//     Foo(callback.Get());
+//   }
+//
+// Can be used with StrictMock and NiceMock. Caller must ensure that it outlives
+// any base::Callback obtained from it.
+
+#ifndef BASE_TEST_MOCK_CALLBACK_H_
+#define BASE_TEST_MOCK_CALLBACK_H_
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace base {
+
+// clang-format off
+
+template <typename F>
+class MockCallback;
+
+template <typename R>
+class MockCallback<Callback<R()>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD0_T(Run, R());
+
+  Callback<R()> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R>
+class MockCallback<OnceCallback<R()>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD0_T(Run, R());
+
+  OnceCallback<R()> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1>
+class MockCallback<Callback<R(A1)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD1_T(Run, R(A1));
+
+  Callback<R(A1)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1>
+class MockCallback<OnceCallback<R(A1)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD1_T(Run, R(A1));
+
+  OnceCallback<R(A1)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2>
+class MockCallback<Callback<R(A1, A2)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD2_T(Run, R(A1, A2));
+
+  Callback<R(A1, A2)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2>
+class MockCallback<OnceCallback<R(A1, A2)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD2_T(Run, R(A1, A2));
+
+  OnceCallback<R(A1, A2)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3>
+class MockCallback<Callback<R(A1, A2, A3)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD3_T(Run, R(A1, A2, A3));
+
+  Callback<R(A1, A2, A3)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3>
+class MockCallback<OnceCallback<R(A1, A2, A3)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD3_T(Run, R(A1, A2, A3));
+
+  OnceCallback<R(A1, A2, A3)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+class MockCallback<Callback<R(A1, A2, A3, A4)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD4_T(Run, R(A1, A2, A3, A4));
+
+  Callback<R(A1, A2, A3, A4)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+class MockCallback<OnceCallback<R(A1, A2, A3, A4)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD4_T(Run, R(A1, A2, A3, A4));
+
+  OnceCallback<R(A1, A2, A3, A4)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5>
+class MockCallback<Callback<R(A1, A2, A3, A4, A5)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD5_T(Run, R(A1, A2, A3, A4, A5));
+
+  Callback<R(A1, A2, A3, A4, A5)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5>
+class MockCallback<OnceCallback<R(A1, A2, A3, A4, A5)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD5_T(Run, R(A1, A2, A3, A4, A5));
+
+  OnceCallback<R(A1, A2, A3, A4, A5)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6>
+class MockCallback<Callback<R(A1, A2, A3, A4, A5, A6)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD6_T(Run, R(A1, A2, A3, A4, A5, A6));
+
+  Callback<R(A1, A2, A3, A4, A5, A6)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6>
+class MockCallback<OnceCallback<R(A1, A2, A3, A4, A5, A6)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD6_T(Run, R(A1, A2, A3, A4, A5, A6));
+
+  OnceCallback<R(A1, A2, A3, A4, A5, A6)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7>
+class MockCallback<Callback<R(A1, A2, A3, A4, A5, A6, A7)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD7_T(Run, R(A1, A2, A3, A4, A5, A6, A7));
+
+  Callback<R(A1, A2, A3, A4, A5, A6, A7)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7>
+class MockCallback<OnceCallback<R(A1, A2, A3, A4, A5, A6, A7)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD7_T(Run, R(A1, A2, A3, A4, A5, A6, A7));
+
+  OnceCallback<R(A1, A2, A3, A4, A5, A6, A7)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8>
+class MockCallback<Callback<R(A1, A2, A3, A4, A5, A6, A7, A8)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD8_T(Run, R(A1, A2, A3, A4, A5, A6, A7, A8));
+
+  Callback<R(A1, A2, A3, A4, A5, A6, A7, A8)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8>
+class MockCallback<OnceCallback<R(A1, A2, A3, A4, A5, A6, A7, A8)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD8_T(Run, R(A1, A2, A3, A4, A5, A6, A7, A8));
+
+  OnceCallback<R(A1, A2, A3, A4, A5, A6, A7, A8)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8, typename A9>
+class MockCallback<Callback<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD9_T(Run, R(A1, A2, A3, A4, A5, A6, A7, A8, A9));
+
+  Callback<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8, typename A9>
+class MockCallback<OnceCallback<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD9_T(Run, R(A1, A2, A3, A4, A5, A6, A7, A8, A9));
+
+  OnceCallback<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8, typename A9,
+    typename A10>
+class MockCallback<Callback<R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD10_T(Run, R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10));
+
+  Callback<R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8, typename A9,
+    typename A10>
+class MockCallback<OnceCallback<R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD10_T(Run, R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10));
+
+  OnceCallback<R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+// clang-format on
+
+}  // namespace base
+
+#endif  // BASE_TEST_MOCK_CALLBACK_H_
diff --git a/base/test/mock_callback.h.pump b/base/test/mock_callback.h.pump
new file mode 100644
index 0000000..3372789
--- /dev/null
+++ b/base/test/mock_callback.h.pump
@@ -0,0 +1,85 @@
+$$ This is a pump file for generating file templates.  Pump is a python
+$$ script that is part of the Google Test suite of utilities.  Description
+$$ can be found here:
+$$
+$$ https://github.com/google/googletest/blob/master/googletest/docs/PumpManual.md
+$$
+$$ MAX_ARITY controls the number of arguments that MockCallback supports.
+$$ It is choosen to match the number GMock supports.
+$var MAX_ARITY = 10
+$$
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Analogous to GMock's built-in MockFunction, but for base::Callback instead of
+// std::function. It takes the full callback type as a parameter, so that it can
+// support both OnceCallback and RepeatingCallback.
+//
+// Use:
+//   using FooCallback = base::Callback<int(std::string)>;
+//
+//   TEST(FooTest, RunsCallbackWithBarArgument) {
+//     base::MockCallback<FooCallback> callback;
+//     EXPECT_CALL(callback, Run("bar")).WillOnce(Return(1));
+//     Foo(callback.Get());
+//   }
+//
+// Can be used with StrictMock and NiceMock. Caller must ensure that it outlives
+// any base::Callback obtained from it.
+
+#ifndef BASE_TEST_MOCK_CALLBACK_H_
+#define BASE_TEST_MOCK_CALLBACK_H_
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace base {
+
+// clang-format off
+
+template <typename F>
+class MockCallback;
+
+$range i 0..MAX_ARITY
+$for i [[
+$range j 1..i
+$var run_type = [[R($for j, [[A$j]])]]
+
+template <typename R$for j [[, typename A$j]]>
+class MockCallback<Callback<$run_type>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD$(i)_T(Run, $run_type);
+
+  Callback<$run_type> Get() {
+    return Bind(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+template <typename R$for j [[, typename A$j]]>
+class MockCallback<OnceCallback<$run_type>> {
+ public:
+  MockCallback() = default;
+  MOCK_METHOD$(i)_T(Run, $run_type);
+
+  OnceCallback<$run_type> Get() {
+    return BindOnce(&MockCallback::Run, Unretained(this));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCallback);
+};
+
+]]
+
+// clang-format on
+
+}  // namespace base
+
+#endif  // BASE_TEST_MOCK_CALLBACK_H_
diff --git a/base/test/mock_callback_unittest.cc b/base/test/mock_callback_unittest.cc
new file mode 100644
index 0000000..c5f109f
--- /dev/null
+++ b/base/test/mock_callback_unittest.cc
@@ -0,0 +1,59 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/mock_callback.h"
+
+#include "base/callback.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using testing::InSequence;
+using testing::Return;
+
+namespace base {
+namespace {
+
+TEST(MockCallbackTest, ZeroArgs) {
+  MockCallback<Closure> mock_closure;
+  EXPECT_CALL(mock_closure, Run());
+  mock_closure.Get().Run();
+
+  MockCallback<Callback<int()>> mock_int_callback;
+  {
+    InSequence sequence;
+    EXPECT_CALL(mock_int_callback, Run()).WillOnce(Return(42));
+    EXPECT_CALL(mock_int_callback, Run()).WillOnce(Return(88));
+  }
+  EXPECT_EQ(42, mock_int_callback.Get().Run());
+  EXPECT_EQ(88, mock_int_callback.Get().Run());
+}
+
+TEST(MockCallbackTest, WithArgs) {
+  MockCallback<Callback<int(int, int)>> mock_two_int_callback;
+  EXPECT_CALL(mock_two_int_callback, Run(1, 2)).WillOnce(Return(42));
+  EXPECT_CALL(mock_two_int_callback, Run(0, 0)).WillRepeatedly(Return(-1));
+  Callback<int(int, int)> two_int_callback = mock_two_int_callback.Get();
+  EXPECT_EQ(-1, two_int_callback.Run(0, 0));
+  EXPECT_EQ(42, two_int_callback.Run(1, 2));
+  EXPECT_EQ(-1, two_int_callback.Run(0, 0));
+}
+
+TEST(MockCallbackTest, ZeroArgsOnce) {
+  MockCallback<OnceClosure> mock_closure;
+  EXPECT_CALL(mock_closure, Run());
+  mock_closure.Get().Run();
+
+  MockCallback<OnceCallback<int()>> mock_int_callback;
+  EXPECT_CALL(mock_int_callback, Run()).WillOnce(Return(88));
+  EXPECT_EQ(88, mock_int_callback.Get().Run());
+}
+
+TEST(MockCallbackTest, WithArgsOnce) {
+  MockCallback<OnceCallback<int(int, int)>> mock_two_int_callback;
+  EXPECT_CALL(mock_two_int_callback, Run(1, 2)).WillOnce(Return(42));
+  OnceCallback<int(int, int)> two_int_callback = mock_two_int_callback.Get();
+  EXPECT_EQ(42, std::move(two_int_callback).Run(1, 2));
+}
+
+}  // namespace
+}  // namespace base
diff --git a/build/android/gyp/util/proguard_util.py b/build/android/gyp/util/proguard_util.py
index 547a7646..1977833 100644
--- a/build/android/gyp/util/proguard_util.py
+++ b/build/android/gyp/util/proguard_util.py
@@ -151,6 +151,19 @@
       inputs += [self._tested_apk_info_path]
     return inputs
 
+  def _WriteFlagsFile(self, out):
+    # Quite useful for auditing proguard flags.
+    for config in self._configs:
+      out.write('#' * 80 + '\n')
+      out.write(config + '\n')
+      out.write('#' * 80 + '\n')
+      with open(config) as config_file:
+        out.write(config_file.read().rstrip())
+      out.write('\n\n')
+    out.write('#' * 80 + '\n')
+    out.write('Command-line\n')
+    out.write('#' * 80 + '\n')
+    out.write(' '.join(self._cmd) + '\n')
 
   def CheckOutput(self):
     self.build()
@@ -160,6 +173,10 @@
     open(self._outjar + '.seeds', 'w').close()
     open(self._outjar + '.usage', 'w').close()
     open(self._outjar + '.mapping', 'w').close()
+
+    with open(self._outjar + '.flags', 'w') as out:
+      self._WriteFlagsFile(out)
+
     # Warning: and Error: are sent to stderr, but messages and Note: are sent
     # to stdout.
     stdout_filter = None
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 3c0541f..a662457 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -767,8 +767,9 @@
       depfile = "${target_gen_dir}/${target_name}.d"
       outputs = [
         _output_jar_path,
-        "$_output_jar_path.seeds",
+        "$_output_jar_path.flags",
         "$_output_jar_path.mapping",
+        "$_output_jar_path.seeds",
         "$_output_jar_path.usage",
       ]
       args = [
diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc
index 1b8799b..35ecffb 100644
--- a/cc/output/gl_renderer.cc
+++ b/cc/output/gl_renderer.cc
@@ -2141,7 +2141,11 @@
   int resource_multiplier_location = -1;
   int resource_offset_location = -1;
   const Program* program = GetProgram(ProgramKey::YUVVideo(
-      tex_coord_precision, sampler, use_alpha_plane, use_nv12, use_color_lut));
+      tex_coord_precision, sampler,
+      use_alpha_plane ? YUV_HAS_ALPHA_TEXTURE : YUV_NO_ALPHA_TEXTURE,
+      use_nv12 ? UV_TEXTURE_MODE_UV : UV_TEXTURE_MODE_U_V,
+      use_color_lut ? COLOR_CONVERSION_MODE_2D_LUT_AS_3D_FROM_YUV
+                    : COLOR_CONVERSION_MODE_NONE));
   DCHECK(program && (program->initialized() || IsContextLost()));
   SetUseProgram(program->program());
   matrix_location = program->matrix_location();
diff --git a/cc/output/gl_renderer_unittest.cc b/cc/output/gl_renderer_unittest.cc
index 20a5e36..dc5a6aaa 100644
--- a/cc/output/gl_renderer_unittest.cc
+++ b/cc/output/gl_renderer_unittest.cc
@@ -186,11 +186,17 @@
         ProgramKey::Tile(precision, sampler, NO_AA, DO_SWIZZLE, true)));
 
     // Iterate over alpha plane, nv12, and color_lut parameters.
+    UVTextureMode uv_modes[2] = {UV_TEXTURE_MODE_UV, UV_TEXTURE_MODE_U_V};
+    YUVAlphaTextureMode a_modes[2] = {YUV_NO_ALPHA_TEXTURE,
+                                      YUV_HAS_ALPHA_TEXTURE};
+    ColorConversionMode c_modes[2] = {
+        COLOR_CONVERSION_MODE_NONE,
+        COLOR_CONVERSION_MODE_2D_LUT_AS_3D_FROM_YUV};
     for (int j = 0; j < 2; j++) {
       for (int k = 0; k < 2; k++) {
         for (int l = 0; l < 2; l++) {
-          const Program* program = renderer()->GetProgram(
-              ProgramKey::YUVVideo(precision, sampler, j, k, l));
+          const Program* program = renderer()->GetProgram(ProgramKey::YUVVideo(
+              precision, sampler, a_modes[j], uv_modes[k], c_modes[l]));
           EXPECT_PROGRAM_VALID(program);
         }
       }
diff --git a/cc/output/program_binding.cc b/cc/output/program_binding.cc
index 70ed5709..d80ef68 100644
--- a/cc/output/program_binding.cc
+++ b/cc/output/program_binding.cc
@@ -27,8 +27,9 @@
          mask_mode_ == other.mask_mode_ &&
          mask_for_background_ == other.mask_for_background_ &&
          has_color_matrix_ == other.has_color_matrix_ &&
-         use_alpha_texture_ == other.use_alpha_texture_ &&
-         use_nv12_ == other.use_nv12_ && use_color_lut_ == other.use_color_lut_;
+         yuv_alpha_texture_mode_ == other.yuv_alpha_texture_mode_ &&
+         uv_texture_mode_ == other.uv_texture_mode_ &&
+         color_conversion_mode_ == other.color_conversion_mode_;
 }
 
 // static
@@ -108,16 +109,20 @@
 // static
 ProgramKey ProgramKey::YUVVideo(TexCoordPrecision precision,
                                 SamplerType sampler,
-                                bool use_alpha_texture,
-                                bool use_nv12,
-                                bool use_color_lut) {
+                                YUVAlphaTextureMode yuv_alpha_texture_mode,
+                                UVTextureMode uv_texture_mode,
+                                ColorConversionMode color_conversion_mode) {
   ProgramKey result;
   result.type_ = PROGRAM_TYPE_YUV_VIDEO;
   result.precision_ = precision;
   result.sampler_ = sampler;
-  result.use_alpha_texture_ = use_alpha_texture;
-  result.use_nv12_ = use_nv12;
-  result.use_color_lut_ = use_color_lut;
+  result.yuv_alpha_texture_mode_ = yuv_alpha_texture_mode;
+  DCHECK(yuv_alpha_texture_mode == YUV_NO_ALPHA_TEXTURE ||
+         yuv_alpha_texture_mode == YUV_HAS_ALPHA_TEXTURE);
+  result.uv_texture_mode_ = uv_texture_mode;
+  DCHECK(uv_texture_mode == UV_TEXTURE_MODE_UV ||
+         uv_texture_mode == UV_TEXTURE_MODE_U_V);
+  result.color_conversion_mode_ = color_conversion_mode;
   return result;
 }
 
diff --git a/cc/output/program_binding.h b/cc/output/program_binding.h
index a95587f..e8cfe681 100644
--- a/cc/output/program_binding.h
+++ b/cc/output/program_binding.h
@@ -92,9 +92,9 @@
   static ProgramKey VideoStream(TexCoordPrecision precision);
   static ProgramKey YUVVideo(TexCoordPrecision precision,
                              SamplerType sampler,
-                             bool use_alpha_texture,
-                             bool use_nv12,
-                             bool use_color_lut);
+                             YUVAlphaTextureMode yuv_alpha_texture_mode,
+                             UVTextureMode uv_texture_mode,
+                             ColorConversionMode color_conversion_mode);
 
   bool operator==(const ProgramKey& other) const;
 
@@ -118,9 +118,10 @@
   bool mask_for_background_ = false;
   bool has_color_matrix_ = false;
 
-  bool use_alpha_texture_ = false;
-  bool use_nv12_ = false;
-  bool use_color_lut_ = false;
+  YUVAlphaTextureMode yuv_alpha_texture_mode_ = YUV_NO_ALPHA_TEXTURE;
+  UVTextureMode uv_texture_mode_ = UV_TEXTURE_MODE_NA;
+
+  ColorConversionMode color_conversion_mode_ = COLOR_CONVERSION_MODE_NONE;
 };
 
 struct ProgramKeyHash {
@@ -137,9 +138,9 @@
            (static_cast<size_t>(key.mask_mode_) << 21) ^
            (static_cast<size_t>(key.mask_for_background_) << 22) ^
            (static_cast<size_t>(key.has_color_matrix_) << 23) ^
-           (static_cast<size_t>(key.use_alpha_texture_) << 24) ^
-           (static_cast<size_t>(key.use_nv12_) << 25) ^
-           (static_cast<size_t>(key.use_color_lut_) << 26);
+           (static_cast<size_t>(key.yuv_alpha_texture_mode_) << 24) ^
+           (static_cast<size_t>(key.uv_texture_mode_) << 25) ^
+           (static_cast<size_t>(key.color_conversion_mode_) << 26);
   }
 };
 
@@ -384,9 +385,10 @@
     vertex_shader_.is_ya_uv_ = true;
 
     fragment_shader_.input_color_type_ = INPUT_COLOR_SOURCE_YUV_TEXTURES;
-    fragment_shader_.use_alpha_texture_ = key.use_alpha_texture_;
-    fragment_shader_.use_nv12_ = key.use_nv12_;
-    fragment_shader_.use_color_lut_ = key.use_color_lut_;
+    fragment_shader_.has_uniform_alpha_ = true;
+    fragment_shader_.yuv_alpha_texture_mode_ = key.yuv_alpha_texture_mode_;
+    fragment_shader_.uv_texture_mode_ = key.uv_texture_mode_;
+    fragment_shader_.color_conversion_mode_ = key.color_conversion_mode_;
   }
 
   void InitializeInternal(ContextProvider* context_provider) {
diff --git a/cc/output/shader.cc b/cc/output/shader.cc
index c86344c..ccc5cce 100644
--- a/cc/output/shader.cc
+++ b/cc/output/shader.cc
@@ -387,7 +387,7 @@
 std::string FragmentShader::GetShaderString() const {
   // TODO(ccameron): Merge YUV shaders into the main shader generator.
   std::string source;
-  if (input_color_type_ == INPUT_COLOR_SOURCE_YUV_TEXTURES)
+  if (color_conversion_mode_ == COLOR_CONVERSION_MODE_2D_LUT_AS_3D_FROM_YUV)
     source = GetShaderStringYUVVideo();
   else
     source = GetShaderSource();
@@ -405,7 +405,7 @@
                           unsigned program,
                           int* base_uniform_index) {
   // TODO(ccameron): Merge YUV shaders into the main shader generator.
-  if (input_color_type_ == INPUT_COLOR_SOURCE_YUV_TEXTURES) {
+  if (color_conversion_mode_ == COLOR_CONVERSION_MODE_2D_LUT_AS_3D_FROM_YUV) {
     InitYUVVideo(context, program, base_uniform_index);
     return;
   }
@@ -437,7 +437,19 @@
         uniforms.push_back("fragmentTexTransform");
       break;
     case INPUT_COLOR_SOURCE_YUV_TEXTURES:
-      NOTREACHED();
+      uniforms.push_back("y_texture");
+      if (uv_texture_mode_ == UV_TEXTURE_MODE_UV)
+        uniforms.push_back("uv_texture");
+      if (uv_texture_mode_ == UV_TEXTURE_MODE_U_V) {
+        uniforms.push_back("u_texture");
+        uniforms.push_back("v_texture");
+      }
+      if (yuv_alpha_texture_mode_ == YUV_HAS_ALPHA_TEXTURE)
+        uniforms.push_back("a_texture");
+      uniforms.push_back("ya_clamp_rect");
+      uniforms.push_back("uv_clamp_rect");
+      uniforms.push_back("yuv_matrix");
+      uniforms.push_back("yuv_adj");
       break;
     case INPUT_COLOR_SOURCE_UNIFORM:
       uniforms.push_back("color");
@@ -475,7 +487,19 @@
         fragment_tex_transform_location_ = locations[index++];
       break;
     case INPUT_COLOR_SOURCE_YUV_TEXTURES:
-      NOTREACHED();
+      y_texture_location_ = locations[index++];
+      if (uv_texture_mode_ == UV_TEXTURE_MODE_UV)
+        uv_texture_location_ = locations[index++];
+      if (uv_texture_mode_ == UV_TEXTURE_MODE_U_V) {
+        u_texture_location_ = locations[index++];
+        v_texture_location_ = locations[index++];
+      }
+      if (yuv_alpha_texture_mode_ == YUV_HAS_ALPHA_TEXTURE)
+        a_texture_location_ = locations[index++];
+      ya_clamp_rect_location_ = locations[index++];
+      uv_clamp_rect_location_ = locations[index++];
+      yuv_matrix_location_ = locations[index++];
+      yuv_adj_location_ = locations[index++];
       break;
     case INPUT_COLOR_SOURCE_UNIFORM:
       color_location_ = locations[index++];
@@ -808,7 +832,32 @@
       }
       break;
     case INPUT_COLOR_SOURCE_YUV_TEXTURES:
-      NOTREACHED();
+      HDR("uniform SamplerType y_texture;");
+      SRC("vec2 ya_clamped =");
+      SRC("    max(ya_clamp_rect.xy, min(ya_clamp_rect.zw, v_yaTexCoord));");
+      SRC("vec2 uv_clamped =");
+      SRC("    max(uv_clamp_rect.xy, min(uv_clamp_rect.zw, v_uvTexCoord));");
+      SRC("vec3 yuv;");
+      SRC("yuv.x = TextureLookup(y_texture, ya_clamped).x;");
+      if (uv_texture_mode_ == UV_TEXTURE_MODE_UV) {
+        HDR("uniform SamplerType uv_texture;");
+        SRC("yuv.yz = TextureLookup(uv_texture, uv_clamped).xy;");
+      }
+      if (uv_texture_mode_ == UV_TEXTURE_MODE_U_V) {
+        HDR("uniform SamplerType u_texture;");
+        HDR("uniform SamplerType v_texture;");
+        SRC("yuv.y = TextureLookup(u_texture, uv_clamped).x;");
+        SRC("yuv.z = TextureLookup(v_texture, uv_clamped).x;");
+      }
+      if (yuv_alpha_texture_mode_ == YUV_HAS_ALPHA_TEXTURE)
+        HDR("uniform SamplerType a_texture;");
+      HDR("uniform vec4 ya_clamp_rect;");
+      HDR("uniform vec4 uv_clamp_rect;");
+      HDR("uniform mat3 yuv_matrix;");
+      HDR("uniform vec3 yuv_adj;");
+      HDR("varying TexCoordPrecision vec2 v_yaTexCoord;");
+      HDR("varying TexCoordPrecision vec2 v_uvTexCoord;");
+      SRC("vec4 texColor = vec4(yuv_matrix * (yuv + yuv_adj), 1.0);");
       break;
     case INPUT_COLOR_SOURCE_UNIFORM:
       DCHECK(!ignore_sampler_type_);
@@ -891,6 +940,8 @@
       line += " * aa";
     if (mask_mode_ != NO_MASK)
       line += " * maskColor.a";
+    if (yuv_alpha_texture_mode_ == YUV_HAS_ALPHA_TEXTURE)
+      line += " * TextureLookup(a_texture, ya_clamped).x";
     line += ";\n";
     source += line;
   }
@@ -945,20 +996,22 @@
                              locations,
                              base_uniform_index);
   y_texture_location_ = locations[0];
-  if (!use_nv12_) {
+  if (uv_texture_mode_ == UV_TEXTURE_MODE_U_V) {
     u_texture_location_ = locations[1];
     v_texture_location_ = locations[2];
-  } else {
+  }
+  if (uv_texture_mode_ == UV_TEXTURE_MODE_UV) {
     uv_texture_location_ = locations[3];
   }
-  if (use_alpha_texture_) {
+  if (yuv_alpha_texture_mode_ == YUV_HAS_ALPHA_TEXTURE) {
     a_texture_location_ = locations[4];
   }
-  if (use_color_lut_) {
+  if (color_conversion_mode_ == COLOR_CONVERSION_MODE_2D_LUT_AS_3D_FROM_YUV) {
     lut_texture_location_ = locations[5];
     resource_multiplier_location_ = locations[6];
     resource_offset_location_ = locations[7];
-  } else {
+  }
+  if (color_conversion_mode_ == COLOR_CONVERSION_MODE_NONE) {
     yuv_matrix_location_ = locations[8];
     yuv_adj_location_ = locations[9];
   }
@@ -980,14 +1033,15 @@
   });
 
   std::string functions = "";
-  if (use_nv12_) {
+  if (uv_texture_mode_ == UV_TEXTURE_MODE_UV) {
     head += "  uniform SamplerType uv_texture;\n";
     functions += SHADER0([]() {
       vec2 GetUV(vec2 uv_clamped) {
         return TextureLookup(uv_texture, uv_clamped).xy;
       }
     });
-  } else {
+  }
+  if (uv_texture_mode_ == UV_TEXTURE_MODE_U_V) {
     head += "  uniform SamplerType u_texture;\n";
     head += "  uniform SamplerType v_texture;\n";
     functions += SHADER0([]() {
@@ -998,7 +1052,7 @@
     });
   }
 
-  if (use_alpha_texture_) {
+  if (yuv_alpha_texture_mode_ == YUV_HAS_ALPHA_TEXTURE) {
     head += "  uniform SamplerType a_texture;\n";
     functions += SHADER0([]() {
       float GetAlpha(vec2 ya_clamped) {
@@ -1011,7 +1065,7 @@
     });
   }
 
-  if (use_color_lut_) {
+  if (color_conversion_mode_ == COLOR_CONVERSION_MODE_2D_LUT_AS_3D_FROM_YUV) {
     head += "  uniform sampler2D lut_texture;\n";
     head += "  uniform float resource_multiplier;\n";
     head += "  uniform float resource_offset;\n";
@@ -1034,7 +1088,8 @@
         return LUT(lut_texture, yuv, 17.0).xyz;
       }
     });
-  } else {
+  }
+  if (color_conversion_mode_ == COLOR_CONVERSION_MODE_NONE) {
     head += "  uniform mat3 yuv_matrix;\n";
     head += "  uniform vec3 yuv_adj;\n";
     functions += SHADER0([]() {
diff --git a/cc/output/shader.h b/cc/output/shader.h
index 6550f901..e69d4f4d 100644
--- a/cc/output/shader.h
+++ b/cc/output/shader.h
@@ -114,6 +114,29 @@
   INPUT_COLOR_SOURCE_UNIFORM,
 };
 
+enum UVTextureMode {
+  // Shader does not use YUV textures.
+  UV_TEXTURE_MODE_NA,
+  // UV plane is a single texture.
+  UV_TEXTURE_MODE_UV,
+  // U and V planes have separate textures.
+  UV_TEXTURE_MODE_U_V,
+};
+
+enum YUVAlphaTextureMode {
+  YUV_ALPHA_TEXTURE_MODE_NA,
+  YUV_NO_ALPHA_TEXTURE,
+  YUV_HAS_ALPHA_TEXTURE,
+};
+
+enum ColorConversionMode {
+  // No color conversion is performed.
+  COLOR_CONVERSION_MODE_NONE,
+  // Conversion is done directly from YUV to output RGB space, via a 3D texture
+  // represented as a 2D texture.
+  COLOR_CONVERSION_MODE_2D_LUT_AS_3D_FROM_YUV,
+};
+
 // TODO(ccameron): Merge this with BlendMode.
 enum FragColorMode {
   FRAG_COLOR_MODE_DEFAULT,
@@ -256,9 +279,10 @@
   bool mask_for_background_ = false;
 
   // YUV-only parameters.
-  bool use_alpha_texture_ = false;
-  bool use_nv12_ = false;
-  bool use_color_lut_ = false;
+  YUVAlphaTextureMode yuv_alpha_texture_mode_ = YUV_ALPHA_TEXTURE_MODE_NA;
+  UVTextureMode uv_texture_mode_ = UV_TEXTURE_MODE_UV;
+
+  ColorConversionMode color_conversion_mode_ = COLOR_CONVERSION_MODE_NONE;
 
   // YUV uniform locations.
   int y_texture_location_ = -1;
diff --git a/chrome/android/java/res/layout/search_engine_recent_title.xml b/chrome/android/java/res/layout/search_engine_recent_title.xml
index 9bd4c4d..9b5c64b 100644
--- a/chrome/android/java/res/layout/search_engine_recent_title.xml
+++ b/chrome/android/java/res/layout/search_engine_recent_title.xml
@@ -7,7 +7,7 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:text="@string/search_engine_recently_visited"
-    android:textColor="@color/light_active_color"
+    android:textColor="@color/google_blue_700"
     style="@style/RobotoMediumStyle"
     android:gravity="center_vertical"
     android:paddingBottom="16dp"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEngineAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEngineAdapter.java
index 4370b0c..32572c75 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEngineAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEngineAdapter.java
@@ -268,7 +268,7 @@
                 link.setVisibility(View.GONE);
             } else {
                 ForegroundColorSpan linkSpan = new ForegroundColorSpan(
-                        ApiCompatibilityUtils.getColor(resources, R.color.pref_accent_color));
+                        ApiCompatibilityUtils.getColor(resources, R.color.google_blue_700));
                 if (LocationUtils.getInstance().isSystemLocationSettingEnabled()) {
                     String message = mContext.getString(
                             locationEnabled(position, true)
diff --git a/chrome/app/settings_chromium_strings.grdp b/chrome/app/settings_chromium_strings.grdp
index 43b388d1..69dd33d6 100644
--- a/chrome/app/settings_chromium_strings.grdp
+++ b/chrome/app/settings_chromium_strings.grdp
@@ -39,7 +39,7 @@
   <!-- Default Browser Page -->
   <if expr="not chromeos">
     <message name="IDS_SETTINGS_DEFAULT_BROWSER_DEFAULT" desc="The text displayed when Chrome is the default browser">
-      Chromium is your default browser. Cheers.
+      Chromium is your default browser. Cheers!
     </message>
     <message name="IDS_SETTINGS_DEFAULT_BROWSER_MAKE_DEFAULT" desc="Default browser checkbox label">
       Make Chromium the default browser
diff --git a/chrome/app/settings_google_chrome_strings.grdp b/chrome/app/settings_google_chrome_strings.grdp
index bae71b7..5cf2399 100644
--- a/chrome/app/settings_google_chrome_strings.grdp
+++ b/chrome/app/settings_google_chrome_strings.grdp
@@ -39,7 +39,7 @@
   <!-- Default Browser Page -->
   <if expr="not chromeos">
     <message name="IDS_SETTINGS_DEFAULT_BROWSER_DEFAULT" desc="The text displayed when Chrome is not the default browser">
-      Google Chrome is your default browser. Cheers.
+      Google Chrome is your default browser. Cheers!
     </message>
     <message name="IDS_SETTINGS_DEFAULT_BROWSER_MAKE_DEFAULT" desc="Default browser checkbox label">
       Make Google Chrome the default browser
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index d2fde5a..2ed9ea5 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -248,7 +248,7 @@
     Show home button
   </message>
   <message name="IDS_SETTINGS_SHOW_BOOKMARKS_BAR" desc="Label for the checkbox which enables or disables showing the bookmarks bar in the toolbar.">
-    Always show the bookmarks bar
+    Show bookmarks bar
   </message>
   <message name="IDS_SETTINGS_HOME_PAGE_NTP" desc="Description of the New Tab Page when set as the home page.">
     Use the New Tab Page
@@ -954,7 +954,7 @@
     Google Cloud Print
   </message>
   <message name="IDS_SETTINGS_PRINTING_CLOUD_PRINTERS_DESCRIPTION" desc="In Printing Settings, the title description of the google cloud printers setting section.">
-    Setup cloud printing devices
+    Set up cloud printing devices
   </message>
 
   <!-- Downloads Page -->
@@ -1582,13 +1582,13 @@
     Keep Wi-Fi on during sleep
   </message>
   <message name="IDS_SETTINGS_MANAGE_CERTIFICATES" desc="Text for manage certificates button in Privacy options">
-    Manage Certificates
+    Manage certificates
   </message>
   <message name="IDS_SETTINGS_MANAGE_CERTIFICATES_DESCRIPTION" desc="Secondary, continued explanation of how to manage SSL certificates and settings in Privacy options">
     Manage HTTPS/SSL certificates and settings
   </message>
   <message name="IDS_SETTINGS_CONTENT_SETTINGS" desc="Text of the button that takes a user to settings page thats allows users to modify site settings. Also the title of that settings page.">
-    Content Settings
+    Content settings
   </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS" desc="Text of the button that takes a user to the enhanced settings page thats allows users to modify site settings. Also the title of that settings page.">
     Site Settings
@@ -1939,10 +1939,10 @@
     Search cookies
   </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_THIRD_PARTY_COOKIE" desc="Label for the Block 3rd-party cookie checkbox on the Cookies category.">
-    Block third-party cookies.
+    Block third-party cookies
   </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_THIRD_PARTY_COOKIE_SUBLABEL" desc="A sub-label below the Block 3rd-party cookie checkbox.">
-    Prevent third-party websites from saving and reading cookie data.
+    Prevent third-party websites from saving and reading cookie data
   </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_DELETE_DATA_POST_SESSION" desc="Label for the checkbox that allows the user to automatically delete their data at the end of the browser session.">
     Keep local data only until you quit your browser
@@ -2222,7 +2222,7 @@
   </if>
 
   <message name="IDS_SETTINGS_SYNC_OVERVIEW" desc="The message that appears in the options dialog when sync has not been set up by the user.">
-    Sign in to get your bookmarks, history, passwords and other settings on all your devices. You'll also automatically be signed in to your Google services.
+    Sign in to get your bookmarks, history, passwords, and other settings on all your devices. You'll also automatically be signed in to your Google services.
   </message>
   <message name="IDS_SETTINGS_SYNC_DISCONNECT" desc="The text to display on the button to indicate stop syncing functionality.">
     Sign Out
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 4f43954..9659be9 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -772,8 +772,6 @@
     "login/screens/error_screen.cc",
     "login/screens/error_screen.h",
     "login/screens/error_screen_actor_delegate.h",
-    "login/screens/eula_model.cc",
-    "login/screens/eula_model.h",
     "login/screens/eula_screen.cc",
     "login/screens/eula_screen.h",
     "login/screens/eula_view.h",
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.cc b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.cc
index 2f56fa51..dd980bdc 100644
--- a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.cc
+++ b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller.cc
@@ -125,6 +125,10 @@
   if (target_screen_calibration_view->GetDisplayPointLocation(&display_point)) {
     touch_point_quad_[state_index] =
         std::make_pair(display_point, touch->location());
+  } else {
+    // TODO(malaykeshav): Display some kind of error for the user.
+    NOTREACHED() << "Touch calibration failed. Could not retrieve location for"
+                    " display point. Retry calibration.";
   }
 
   // If this is the final state, then store all calibration data and stop
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller_unittest.cc b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller_unittest.cc
index 4ac1ea6..080e208 100644
--- a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller_unittest.cc
+++ b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_controller_unittest.cc
@@ -56,7 +56,7 @@
         ctrl->touch_calibrator_views_[target_display.id()].get();
 
     // End the background fade in animation.
-    target_calibrator_view->SkipCurrentAnimationForTest();
+    target_calibrator_view->SkipCurrentAnimation();
 
     // TouchCalibratorView on the display being calibrated should be at the
     // state where the first display point is visible.
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc
index 123aefa..5924a25c 100644
--- a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc
+++ b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc
@@ -9,10 +9,12 @@
 #include "ash/shell.h"
 #include "ui/aura/window.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/gfx/animation/linear_animation.h"
 #include "ui/gfx/animation/throb_animation.h"
 #include "ui/gfx/canvas.h"
 #include "ui/strings/grit/ui_strings.h"
+#include "ui/views/background.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/widget/widget.h"
 
@@ -24,23 +26,28 @@
 
 constexpr int kAnimationFrameRate = 100;
 constexpr int kFadeDurationInMs = 150;
+constexpr int kPointMoveDurationInMs = 600;
+constexpr int kPointMoveDurationLongInMs = 700;
 
 const SkColor kExitLabelColor = SkColorSetARGBInline(255, 96, 96, 96);
 const SkColor kExitLabelShadowColor = SkColorSetARGBInline(255, 11, 11, 11);
 constexpr int kExitLabelWidth = 300;
 constexpr int kExitLabelHeight = 20;
 
+const SkColor kTapHereLabelColor = SK_ColorWHITE;
+
 constexpr int kHintBoxWidth = 298;
 constexpr int kHintBoxHeight = 180;
 constexpr int kHintBoxLabelTextSize = 5;
 constexpr int kHintBoxSublabelTextSize = 3;
 
-constexpr int kThrobberCircleViewWidth = 128;
+constexpr int kThrobberCircleViewWidth = 64;
 constexpr float kThrobberCircleRadiusFactor = 3.f / 8.f;
 
 constexpr int kTouchPointViewOffset = 100;
 
 constexpr int kTapLabelHeight = 48;
+constexpr int kTapLabelWidth = 80;
 
 const SkColor kHintLabelTextColor = SK_ColorBLACK;
 const SkColor kHintSublabelTextColor = SkColorSetARGBInline(255, 161, 161, 161);
@@ -50,7 +57,7 @@
 
 constexpr int kCircleAnimationDurationMs = 900;
 
-constexpr int kHintRectBorderRadius = 8;
+constexpr int kHintRectBorderRadius = 4;
 
 constexpr float kBackgroundFinalOpacity = 0.75f;
 
@@ -78,6 +85,17 @@
   return gfx::Size(width, height);
 }
 
+void AnimateLayerToPosition(views::View* view,
+                            int duration,
+                            gfx::Point end_position) {
+  ui::ScopedLayerAnimationSettings slide_settings(view->layer()->GetAnimator());
+  slide_settings.SetPreemptionStrategy(
+      ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+  slide_settings.SetTransitionDuration(
+      base::TimeDelta::FromMilliseconds(duration));
+  view->SetBoundsRect(gfx::Rect(end_position, view->size()));
+}
+
 }  // namespace
 
 // Creates a throbbing animated view with two concentric circles. The radius of
@@ -291,6 +309,7 @@
     : display_(target_display),
       is_primary_view_(is_primary_view),
       exit_label_(nullptr),
+      tap_label_(nullptr),
       throbber_circle_(nullptr),
       hint_box_view_(nullptr),
       touch_point_view_(nullptr) {
@@ -349,18 +368,41 @@
   // Initialize the touch point view that contains the animated circle that the
   // user needs to tap.
   const int kTouchPointViewHeight = kThrobberCircleViewWidth + kTapLabelHeight;
+  const int kThrobberCircleViewHorizontalOffset =
+      (kTapLabelWidth - kThrobberCircleViewWidth) / 2;
 
   throbber_circle_ =
       new CircularThrobberView(kThrobberCircleViewWidth, kInnerCircleColor,
                                kOuterCircleColor, kCircleAnimationDurationMs);
-  throbber_circle_->SetPosition(gfx::Point(0, 0));
+  throbber_circle_->SetPosition(
+      gfx::Point(kThrobberCircleViewHorizontalOffset, 0));
+
+  // Initialize the tap label.
+  tap_label_ = new views::Label(
+      rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_TAP_HERE_LABEL),
+      rb.GetFontListWithDelta(6, gfx::Font::FontStyle::NORMAL,
+                              gfx::Font::Weight::NORMAL));
+  tap_label_->SetBounds(0, kThrobberCircleViewWidth, kTapLabelWidth,
+                        kTapLabelHeight);
+  tap_label_->SetEnabledColor(kTapHereLabelColor);
+  tap_label_->SetDisabledColor(kTapHereLabelColor);
+  tap_label_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
+  tap_label_->SetAutoColorReadabilityEnabled(false);
+  tap_label_->SetSubpixelRenderingEnabled(false);
+  tap_label_->SetVisible(false);
 
   touch_point_view_ = new views::View;
   touch_point_view_->SetBounds(kTouchPointViewOffset, kTouchPointViewOffset,
-                               kThrobberCircleViewWidth, kTouchPointViewHeight);
+                               kTapLabelWidth, kTouchPointViewHeight);
   touch_point_view_->SetVisible(false);
+  touch_point_view_->SetPaintToLayer(true);
+  touch_point_view_->layer()->SetFillsBoundsOpaquely(false);
+  touch_point_view_->layer()->GetAnimator()->AddObserver(this);
+  touch_point_view_->set_background(
+      views::Background::CreateSolidBackground(SK_ColorTRANSPARENT));
 
   touch_point_view_->AddChildView(throbber_circle_);
+  touch_point_view_->AddChildView(tap_label_);
 
   AddChildView(touch_point_view_);
 
@@ -411,7 +453,10 @@
 }
 
 void TouchCalibratorView::AnimationProgressed(const gfx::Animation* animation) {
-  SchedulePaint();
+  if (!is_primary_view_) {
+    SchedulePaint();
+    return;
+  }
 }
 
 void TouchCalibratorView::AnimationCanceled(const gfx::Animation* animation) {
@@ -433,9 +478,38 @@
   }
 }
 
+void TouchCalibratorView::OnLayerAnimationStarted(
+    ui::LayerAnimationSequence* sequence) {}
+
+void TouchCalibratorView::OnLayerAnimationEnded(
+    ui::LayerAnimationSequence* sequence) {
+  switch (state_) {
+    case ANIMATING_1_TO_2:
+      state_ = DISPLAY_POINT_2;
+      tap_label_->SetVisible(true);
+      break;
+    case ANIMATING_2_TO_3:
+      state_ = DISPLAY_POINT_3;
+      break;
+    case ANIMATING_3_TO_4:
+      state_ = DISPLAY_POINT_4;
+      break;
+    default:
+      break;
+  }
+}
+
+void TouchCalibratorView::OnLayerAnimationAborted(
+    ui::LayerAnimationSequence* sequence) {
+  OnLayerAnimationEnded(sequence);
+}
+
+void TouchCalibratorView::OnLayerAnimationScheduled(
+    ui::LayerAnimationSequence* sequence) {}
+
 void TouchCalibratorView::AdvanceToNextState() {
   // Stop any previous animations and skip them to the end.
-  animator_->End();
+  SkipCurrentAnimation();
 
   switch (state_) {
     case UNKNOWN:
@@ -445,26 +519,75 @@
       end_opacity_value_ = kBackgroundFinalOpacity;
 
       paint_.setStyle(SkPaint::kFill_Style);
-
       animator_->SetDuration(kFadeDurationInMs);
-      break;
+      animator_->Start();
+      return;
+    case DISPLAY_POINT_1:
+      state_ = ANIMATING_1_TO_2;
+
+      // The touch point has to be animated from the top left corner of the
+      // screen to the top right corner.
+      AnimateLayerToPosition(
+          touch_point_view_, kPointMoveDurationInMs,
+          gfx::Point(display_.bounds().width() - kTouchPointViewOffset -
+                         touch_point_view_->width(),
+                     touch_point_view_->y()));
+      hint_box_view_->SetVisible(false);
+      return;
+    case DISPLAY_POINT_2:
+      state_ = ANIMATING_2_TO_3;
+
+      // The touch point has to be animated from the top right corner of the
+      // screen to the bottom left corner.
+      AnimateLayerToPosition(
+          touch_point_view_, kPointMoveDurationLongInMs,
+          gfx::Point(kTouchPointViewOffset, display_.bounds().height() -
+                                                kTouchPointViewOffset -
+                                                touch_point_view_->height()));
+      return;
+    case DISPLAY_POINT_3:
+      state_ = ANIMATING_3_TO_4;
+
+      // The touch point has to be animated from the bottom left corner of the
+      // screen to the bottom right corner.
+      AnimateLayerToPosition(
+          touch_point_view_, kPointMoveDurationInMs,
+          gfx::Point(display_.bounds().width() - kTouchPointViewOffset -
+                         touch_point_view_->width(),
+                     touch_point_view_->y()));
+      return;
     default:
-      break;
+      return;
   }
-  animator_->Start();
 }
 
 bool TouchCalibratorView::GetDisplayPointLocation(gfx::Point* location) {
+  DCHECK(location);
   if (!is_primary_view_)
     return false;
-  return false;
+
+  if (state_ != DISPLAY_POINT_1 && state_ != DISPLAY_POINT_2 &&
+      state_ != DISPLAY_POINT_3 && state_ != DISPLAY_POINT_4) {
+    return false;
+  }
+
+  if (!touch_point_view_ || !throbber_circle_)
+    return false;
+  // TODO(malaykeshav): Can use views::ConvertPointToScreen()
+  location->SetPoint(touch_point_view_->x() + touch_point_view_->width() / 2.f,
+                     touch_point_view_->y() + touch_point_view_->width() / 2.f);
+  return true;
 }
 
 void TouchCalibratorView::SkipToFinalState() {}
 
-void TouchCalibratorView::SkipCurrentAnimationForTest() {
+void TouchCalibratorView::SkipCurrentAnimation() {
   if (animator_->is_animating())
     animator_->End();
+  if (touch_point_view_ &&
+      touch_point_view_->layer()->GetAnimator()->is_animating()) {
+    touch_point_view_->layer()->GetAnimator()->StopAnimating();
+  }
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h
index de46566..69719506 100644
--- a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h
+++ b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_DISPLAY_TOUCH_CALIBRATOR_TOUCH_CALIBRATOR_VIEW_H_
 
 #include "base/macros.h"
+#include "ui/compositor/layer_animation_observer.h"
 #include "ui/display/display.h"
 #include "ui/gfx/animation/animation_delegate.h"
 #include "ui/views/view.h"
@@ -20,6 +21,10 @@
 class LinearAnimation;
 }
 
+namespace ui {
+class LayerAnimationSequence;
+}
+
 namespace chromeos {
 
 class CircularThrobberView;
@@ -30,7 +35,9 @@
 // touch calibration view.
 // |TouchCalibratorView| acts as a state machine and has an API to toggle its
 // state or get the current state.
-class TouchCalibratorView : public views::View, public gfx::AnimationDelegate {
+class TouchCalibratorView : public views::View,
+                            public gfx::AnimationDelegate,
+                            public ui::LayerAnimationObserver {
  public:
   // Different states of |TouchCalibratorView| in order.
   enum State {
@@ -68,6 +75,12 @@
   void AnimationProgressed(const gfx::Animation* animation) override;
   void AnimationCanceled(const gfx::Animation* animation) override;
 
+  // ui::LayerAnimationObserver
+  void OnLayerAnimationStarted(ui::LayerAnimationSequence* sequence) override;
+  void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override;
+  void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override;
+  void OnLayerAnimationScheduled(ui::LayerAnimationSequence* sequence) override;
+
   // Moves the touch calibrator view to its next state.
   void AdvanceToNextState();
 
@@ -81,7 +94,7 @@
   bool GetDisplayPointLocation(gfx::Point* location);
 
   // Skips/cancels any ongoing animation to its end.
-  void SkipCurrentAnimationForTest();
+  void SkipCurrentAnimation();
 
   // Returns the current state of the view.
   State state() { return state_; }
@@ -104,6 +117,8 @@
 
   // Text label indicating how to exit the touch calibration.
   views::Label* exit_label_;
+  // Text label indicating the significance of the touch point on screen.
+  views::Label* tap_label_;
 
   // Start and end opacity values used during the fade animation. This is set
   // before the animation begins.
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index 21f8af2..ebab6deb 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -161,7 +161,7 @@
   TargetVolume volume;
 
   // Entries to be added.
-  ScopedVector<TestEntryInfo> entries;
+  std::vector<std::unique_ptr<TestEntryInfo>> entries;
 
   // Registers the member information to the given converter.
   static void RegisterJSONConverter(
diff --git a/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc b/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
index b66802f..a037ab1 100644
--- a/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
@@ -180,9 +180,9 @@
   foo_app->set_application_id("foo_app_id");
   foo_app->set_name("Foo");
   foo_app->set_object_type("foo_object_type");
-  ScopedVector<std::string> foo_mime_types;
-  foo_mime_types.push_back(new std::string("text/plain"));
-  foo_mime_types.push_back(new std::string("text/html"));
+  std::vector<std::unique_ptr<std::string>> foo_mime_types;
+  foo_mime_types.push_back(base::MakeUnique<std::string>("text/plain"));
+  foo_mime_types.push_back(base::MakeUnique<std::string>("text/html"));
   foo_app->set_primary_mimetypes(std::move(foo_mime_types));
 
   // Bar.app can only handle "text/plain".
@@ -192,14 +192,14 @@
   bar_app->set_application_id("bar_app_id");
   bar_app->set_name("Bar");
   bar_app->set_object_type("bar_object_type");
-  ScopedVector<std::string> bar_mime_types;
-  bar_mime_types.push_back(new std::string("text/plain"));
+  std::vector<std::unique_ptr<std::string>> bar_mime_types;
+  bar_mime_types.push_back(base::MakeUnique<std::string>("text/plain"));
   bar_app->set_primary_mimetypes(std::move(bar_mime_types));
 
   // Prepare DriveAppRegistry from Foo.app and Bar.app.
-  ScopedVector<google_apis::AppResource> app_resources;
-  app_resources.push_back(foo_app.release());
-  app_resources.push_back(bar_app.release());
+  std::vector<std::unique_ptr<google_apis::AppResource>> app_resources;
+  app_resources.push_back(std::move(foo_app));
+  app_resources.push_back(std::move(bar_app));
   google_apis::AppList app_list;
   app_list.set_items(std::move(app_resources));
   drive::DriveAppRegistry drive_app_registry(NULL);
@@ -734,12 +734,12 @@
   baz_app->set_application_id(kBazId);
   baz_app->set_name("Baz");
   baz_app->set_object_type("baz_object_type");
-  ScopedVector<std::string> baz_mime_types;
-  baz_mime_types.push_back(new std::string("text/plain"));
+  std::vector<std::unique_ptr<std::string>> baz_mime_types;
+  baz_mime_types.push_back(base::MakeUnique<std::string>("text/plain"));
   baz_app->set_primary_mimetypes(std::move(baz_mime_types));
   // Set up DriveAppRegistry.
-  ScopedVector<google_apis::AppResource> app_resources;
-  app_resources.push_back(baz_app.release());
+  std::vector<std::unique_ptr<google_apis::AppResource>> app_resources;
+  app_resources.push_back(std::move(baz_app));
   google_apis::AppList app_list;
   app_list.set_items(std::move(app_resources));
   drive::DriveAppRegistry drive_app_registry(NULL);
@@ -783,13 +783,14 @@
   foo_app->set_application_id(kFooId);
   foo_app->set_name("Foo");
   foo_app->set_object_type("foo_object_type");
-  ScopedVector<std::string> foo_extensions;
-  foo_extensions.push_back(new std::string("gdoc"));  // Not ".gdoc"
+  std::vector<std::unique_ptr<std::string>> foo_extensions;
+  foo_extensions.push_back(
+      base::MakeUnique<std::string>("gdoc"));  // Not ".gdoc"
   foo_app->set_primary_file_extensions(std::move(foo_extensions));
 
   // Prepare DriveAppRegistry from Foo.app.
-  ScopedVector<google_apis::AppResource> app_resources;
-  app_resources.push_back(foo_app.release());
+  std::vector<std::unique_ptr<google_apis::AppResource>> app_resources;
+  app_resources.push_back(std::move(foo_app));
   google_apis::AppList app_list;
   app_list.set_items(std::move(app_resources));
   drive::DriveAppRegistry drive_app_registry(NULL);
diff --git a/chrome/browser/chromeos/login/screens/eula_model.cc b/chrome/browser/chromeos/login/screens/eula_model.cc
deleted file mode 100644
index 21b2884..0000000
--- a/chrome/browser/chromeos/login/screens/eula_model.cc
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/login/screens/eula_model.h"
-
-#include "chrome/browser/chromeos/login/wizard_controller.h"
-
-namespace chromeos {
-
-const char EulaModel::kUserActionAcceptButtonClicked[] = "accept-button";
-const char EulaModel::kUserActionBackButtonClicked[] = "back-button";
-const char EulaModel::kContextKeyUsageStatsEnabled[] = "usageStatsEnabled";
-
-EulaModel::EulaModel(BaseScreenDelegate* base_screen_delegate)
-    : BaseScreen(base_screen_delegate, OobeScreen::SCREEN_OOBE_EULA) {}
-
-EulaModel::~EulaModel() {}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/screens/eula_model.h b/chrome/browser/chromeos/login/screens/eula_model.h
deleted file mode 100644
index fb1a45a..0000000
--- a/chrome/browser/chromeos/login/screens/eula_model.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_EULA_MODEL_H_
-#define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_EULA_MODEL_H_
-
-#include "chrome/browser/chromeos/login/screens/base_screen.h"
-#include "url/gurl.h"
-
-namespace chromeos {
-
-class BaseScreenDelegate;
-class EulaView;
-
-// Allows us to get info from eula screen that we need.
-class EulaModel : public BaseScreen {
- public:
-  static const char kUserActionAcceptButtonClicked[];
-  static const char kUserActionBackButtonClicked[];
-  static const char kContextKeyUsageStatsEnabled[];
-
-  explicit EulaModel(BaseScreenDelegate* base_screen_delegate);
-  ~EulaModel() override;
-
-  // Returns URL of the OEM EULA page that should be displayed using current
-  // locale and manifest. Returns empty URL otherwise.
-  virtual GURL GetOemEulaUrl() const = 0;
-
-  // Initiate TPM password fetch. Will call actor's OnPasswordFetched() when
-  // done.
-  virtual void InitiatePasswordFetch() = 0;
-
-  // Returns true if usage statistics reporting is enabled.
-  virtual bool IsUsageStatsEnabled() const = 0;
-
-  // This method is called, when view is being destroyed. Note, if model
-  // is destroyed earlier then it has to call SetModel(NULL).
-  virtual void OnViewDestroyed(EulaView* view) = 0;
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_EULA_MODEL_H_
diff --git a/chrome/browser/chromeos/login/screens/eula_screen.cc b/chrome/browser/chromeos/login/screens/eula_screen.cc
index 3b2719fd..e17cc1e 100644
--- a/chrome/browser/chromeos/login/screens/eula_screen.cc
+++ b/chrome/browser/chromeos/login/screens/eula_screen.cc
@@ -11,24 +11,32 @@
 #include "chrome/browser/chromeos/customization/customization_document.h"
 #include "chrome/browser/chromeos/login/screens/base_screen_delegate.h"
 #include "chrome/browser/chromeos/login/screens/eula_view.h"
+#include "chrome/browser/chromeos/login/wizard_controller.h"
 #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
 #include "chromeos/dbus/cryptohome_client.h"
 #include "chromeos/dbus/dbus_method_call_status.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 
 namespace chromeos {
+namespace {
+
+constexpr const char kUserActionAcceptButtonClicked[] = "accept-button";
+constexpr const char kUserActionBackButtonClicked[] = "back-button";
+constexpr const char kContextKeyUsageStatsEnabled[] = "usageStatsEnabled";
+
+}  // namespace
 
 EulaScreen::EulaScreen(BaseScreenDelegate* base_screen_delegate,
                        Delegate* delegate,
                        EulaView* view)
-    : EulaModel(base_screen_delegate),
+    : BaseScreen(base_screen_delegate, OobeScreen::SCREEN_OOBE_EULA),
       delegate_(delegate),
       view_(view),
       password_fetcher_(this) {
   DCHECK(view_);
   DCHECK(delegate_);
   if (view_)
-    view_->Bind(*this);
+    view_->Bind(this);
 }
 
 EulaScreen::~EulaScreen() {
@@ -36,22 +44,6 @@
     view_->Unbind();
 }
 
-void EulaScreen::Show() {
-  // Command to own the TPM.
-  DBusThreadManager::Get()->GetCryptohomeClient()->TpmCanAttemptOwnership(
-      EmptyVoidDBusMethodCallback());
-  if (policy::DeviceCloudPolicyManagerChromeOS::GetZeroTouchEnrollmentMode() ==
-      policy::ZeroTouchEnrollmentMode::HANDS_OFF)
-    OnUserAction(EulaModel::kUserActionAcceptButtonClicked);
-  else if (view_)
-    view_->Show();
-}
-
-void EulaScreen::Hide() {
-  if (view_)
-    view_->Hide();
-}
-
 GURL EulaScreen::GetOemEulaUrl() const {
   const StartupCustomizationDocument* customization =
       StartupCustomizationDocument::GetInstance();
@@ -79,12 +71,6 @@
   }
 }
 
-void EulaScreen::OnPasswordFetched(const std::string& tpm_password) {
-  tpm_password_ = tpm_password;
-  if (view_)
-    view_->OnPasswordFetched(tpm_password_);
-}
-
 bool EulaScreen::IsUsageStatsEnabled() const {
   return delegate_ && delegate_->GetUsageStatisticsReporting();
 }
@@ -94,6 +80,22 @@
     view_ = NULL;
 }
 
+void EulaScreen::Show() {
+  // Command to own the TPM.
+  DBusThreadManager::Get()->GetCryptohomeClient()->TpmCanAttemptOwnership(
+      EmptyVoidDBusMethodCallback());
+  if (policy::DeviceCloudPolicyManagerChromeOS::GetZeroTouchEnrollmentMode() ==
+      policy::ZeroTouchEnrollmentMode::HANDS_OFF)
+    OnUserAction(kUserActionAcceptButtonClicked);
+  else if (view_)
+    view_->Show();
+}
+
+void EulaScreen::Hide() {
+  if (view_)
+    view_->Hide();
+}
+
 void EulaScreen::OnUserAction(const std::string& action_id) {
   if (action_id == kUserActionAcceptButtonClicked)
     Finish(BaseScreenDelegate::EULA_ACCEPTED);
@@ -111,4 +113,10 @@
   }
 }
 
+void EulaScreen::OnPasswordFetched(const std::string& tpm_password) {
+  tpm_password_ = tpm_password;
+  if (view_)
+    view_->OnPasswordFetched(tpm_password_);
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/screens/eula_screen.h b/chrome/browser/chromeos/login/screens/eula_screen.h
index 83a0d4a..4c03ebc 100644
--- a/chrome/browser/chromeos/login/screens/eula_screen.h
+++ b/chrome/browser/chromeos/login/screens/eula_screen.h
@@ -10,16 +10,18 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "chrome/browser/chromeos/login/screens/base_screen.h"
-#include "chrome/browser/chromeos/login/screens/eula_model.h"
 #include "chromeos/tpm/tpm_password_fetcher.h"
 #include "components/login/screens/screen_context.h"
 #include "url/gurl.h"
 
 namespace chromeos {
 
+class BaseScreenDelegate;
+class EulaView;
+
 // Representation independent class that controls OOBE screen showing EULA
 // to users.
-class EulaScreen : public EulaModel, public TpmPasswordFetcherDelegate {
+class EulaScreen : public BaseScreen, public TpmPasswordFetcherDelegate {
  public:
   class Delegate {
    public:
@@ -35,20 +37,31 @@
              EulaView* view);
   ~EulaScreen() override;
 
-  // EulaModel implementation:
+  // Returns URL of the OEM EULA page that should be displayed using current
+  // locale and manifest. Returns empty URL otherwise.
+  GURL GetOemEulaUrl() const;
+
+  // Initiate TPM password fetch. Will call actor's OnPasswordFetched() when
+  // done.
+  void InitiatePasswordFetch();
+
+  // Returns true if usage statistics reporting is enabled.
+  bool IsUsageStatsEnabled() const;
+
+  // This method is called, when view is being destroyed. Note, if model
+  // is destroyed earlier then it has to call SetModel(NULL).
+  void OnViewDestroyed(EulaView* view);
+
+ private:
+  // BaseScreen implementation:
   void Show() override;
   void Hide() override;
-  GURL GetOemEulaUrl() const override;
-  void InitiatePasswordFetch() override;
-  bool IsUsageStatsEnabled() const override;
-  void OnViewDestroyed(EulaView* view) override;
   void OnUserAction(const std::string& action_id) override;
   void OnContextKeyUpdated(const ::login::ScreenContext::KeyType& key) override;
 
   // TpmPasswordFetcherDelegate implementation:
   void OnPasswordFetched(const std::string& tpm_password) override;
 
- private:
   // URL of the OEM EULA page (on disk).
   GURL oem_eula_page_;
 
diff --git a/chrome/browser/chromeos/login/screens/eula_view.h b/chrome/browser/chromeos/login/screens/eula_view.h
index 1d12715..08c0da0f 100644
--- a/chrome/browser/chromeos/login/screens/eula_view.h
+++ b/chrome/browser/chromeos/login/screens/eula_view.h
@@ -9,7 +9,7 @@
 
 namespace chromeos {
 
-class EulaModel;
+class EulaScreen;
 
 // Interface between eula screen and its representation, either WebUI
 // or Views one. Note, do not forget to call OnViewDestroyed in the
@@ -20,7 +20,7 @@
 
   virtual void Show() = 0;
   virtual void Hide() = 0;
-  virtual void Bind(EulaModel& model) = 0;
+  virtual void Bind(EulaScreen* screen) = 0;
   virtual void Unbind() = 0;
   virtual void OnPasswordFetched(const std::string& tpm_password) = 0;
 };
diff --git a/chrome/browser/chromeos/login/screens/mock_eula_screen.cc b/chrome/browser/chromeos/login/screens/mock_eula_screen.cc
index 41f7508e..d5aebbc3 100644
--- a/chrome/browser/chromeos/login/screens/mock_eula_screen.cc
+++ b/chrome/browser/chromeos/login/screens/mock_eula_screen.cc
@@ -23,17 +23,17 @@
 }
 
 MockEulaView::~MockEulaView() {
-  if (model_)
-    model_->OnViewDestroyed(this);
+  if (screen_)
+    screen_->OnViewDestroyed(this);
 }
 
-void MockEulaView::Bind(EulaModel& model) {
-  model_ = &model;
-  MockBind(model);
+void MockEulaView::Bind(EulaScreen* screen) {
+  screen_ = screen;
+  MockBind(screen);
 }
 
 void MockEulaView::Unbind() {
-  model_ = nullptr;
+  screen_ = nullptr;
   MockUnbind();
 }
 
diff --git a/chrome/browser/chromeos/login/screens/mock_eula_screen.h b/chrome/browser/chromeos/login/screens/mock_eula_screen.h
index 27c4ba4..5d71201 100644
--- a/chrome/browser/chromeos/login/screens/mock_eula_screen.h
+++ b/chrome/browser/chromeos/login/screens/mock_eula_screen.h
@@ -25,18 +25,18 @@
   MockEulaView();
   ~MockEulaView() override;
 
-  void Bind(EulaModel& model) override;
+  void Bind(EulaScreen* screen) override;
   void Unbind() override;
 
   MOCK_METHOD0(Show, void());
   MOCK_METHOD0(Hide, void());
 
-  MOCK_METHOD1(MockBind, void(EulaModel& model));
+  MOCK_METHOD1(MockBind, void(EulaScreen* screen));
   MOCK_METHOD0(MockUnbind, void());
   MOCK_METHOD1(OnPasswordFetched, void(const std::string& tpm_password));
 
  private:
-  EulaModel* model_;
+  EulaScreen* screen_ = nullptr;
 };
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.cc b/chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.cc
index a6562915..077b3b9 100644
--- a/chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.cc
+++ b/chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.cc
@@ -8,9 +8,11 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string_util.h"
+#include "chromeos/chromeos_switches.h"
 #include "content/public/browser/browser_thread.h"
 #include "google_apis/gaia/gaia_auth_fetcher.h"
 #include "google_apis/gaia/gaia_constants.h"
@@ -164,6 +166,16 @@
 }
 
 void PolicyOAuth2TokenFetcherImpl::StartFetchingRefreshToken() {
+  // Don't fetch tokens for test.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          chromeos::switches::kDisableGaiaServices)) {
+    failed_ = true;
+    ForwardPolicyToken(
+        std::string(),
+        GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
+    return;
+  }
+
   if (auth_code_.empty()) {
     refresh_token_fetcher_.reset(new GaiaAuthFetcher(
         this, GaiaConstants::kChromeSource, auth_context_getter_.get()));
diff --git a/chrome/browser/domain_reliability/browsertest.cc b/chrome/browser/domain_reliability/browsertest.cc
index 1954b78c..c70aacd 100644
--- a/chrome/browser/domain_reliability/browsertest.cc
+++ b/chrome/browser/domain_reliability/browsertest.cc
@@ -82,7 +82,7 @@
   auto config = base::MakeUnique<DomainReliabilityConfig>();
   config->origin = GURL("https://localhost/");
   config->include_subdomains = false;
-  config->collectors.push_back(new GURL(
+  config->collectors.push_back(base::MakeUnique<GURL>(
       net::URLRequestFailedJob::GetMockHttpsUrl(net::ERR_IO_PENDING)));
   config->success_sample_rate = 1.0;
   config->failure_sample_rate = 1.0;
diff --git a/chrome/browser/resources/settings/controls/settings_input.html b/chrome/browser/resources/settings/controls/settings_input.html
index aec34aa..77254c5 100644
--- a/chrome/browser/resources/settings/controls/settings_input.html
+++ b/chrome/browser/resources/settings/controls/settings_input.html
@@ -1,3 +1,4 @@
+<link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/html/polymer.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_behavior.html">
@@ -18,7 +19,7 @@
           error-message="[[errorMessage]]" label="[[label]]"
           no-label-float="[[noLabelFloat]]" pattern="[[pattern]]"
           readonly$="[[readonly]]" required="[[required]]" type="[[type]]"
-          on-blur="onBlur_" disabled="[[isDisabled_(disabled, pref)]]"
+          on-change="onChange_" disabled="[[isDisabled_(disabled, pref)]]"
           stop-keyboard-event-propagation$="[[stopKeyboardEventPropagation]]"
           tabindex$="[[getTabindex_(canTab)]]">
       </paper-input>
diff --git a/chrome/browser/resources/settings/controls/settings_input.js b/chrome/browser/resources/settings/controls/settings_input.js
index 012a8bca..2652db17 100644
--- a/chrome/browser/resources/settings/controls/settings_input.js
+++ b/chrome/browser/resources/settings/controls/settings_input.js
@@ -90,10 +90,11 @@
   },
 
   /**
-   * Blur method for paper-input. Only update the pref value on a blur event.
+   * Change event handler for paper-input. Updates the pref value.
+   * settings-input uses the change event because it is fired by the Enter key.
    * @private
    */
-  onBlur_: function() {
+  onChange_: function() {
     if (!this.pref)
       return;
 
diff --git a/chrome/browser/resources/settings/site_settings/site_list.js b/chrome/browser/resources/settings/site_settings/site_list.js
index 8501b13..deb8f38c 100644
--- a/chrome/browser/resources/settings/site_settings/site_list.js
+++ b/chrome/browser/resources/settings/site_settings/site_list.js
@@ -223,11 +223,13 @@
     if (this.allSites) {
       this.getAllSitesList_().then(function(lists) {
         this.processExceptions_(lists);
+        this.closeActionMenu_();
       }.bind(this));
     } else {
       this.browserProxy_.getExceptionList(this.category).then(
         function(exceptionList) {
           this.processExceptions_([exceptionList]);
+          this.closeActionMenu_();
       }.bind(this));
     }
   },
@@ -465,7 +467,9 @@
   /** @private */
   closeActionMenu_: function() {
     this.actionMenuSite_ = null;
-    /** @type {!CrActionMenuElement} */ (
-        this.$$('dialog[is=cr-action-menu]')).close();
+    var actionMenu = /** @type {!CrActionMenuElement} */ (
+        this.$$('dialog[is=cr-action-menu]'));
+    if (actionMenu.open)
+      actionMenu.close();
   },
 });
diff --git a/chrome/browser/sync_file_system/drive_backend/conflict_resolver_unittest.cc b/chrome/browser/sync_file_system/drive_backend/conflict_resolver_unittest.cc
index 99d194d..e9582e3 100644
--- a/chrome/browser/sync_file_system/drive_backend/conflict_resolver_unittest.cc
+++ b/chrome/browser/sync_file_system/drive_backend/conflict_resolver_unittest.cc
@@ -237,10 +237,10 @@
     return status;
   }
 
-  ScopedVector<google_apis::FileResource>
+  std::vector<std::unique_ptr<google_apis::FileResource>>
   GetResourceEntriesForParentAndTitle(const std::string& parent_folder_id,
                                       const std::string& title) {
-    ScopedVector<google_apis::FileResource> entries;
+    std::vector<std::unique_ptr<google_apis::FileResource>> entries;
     EXPECT_EQ(google_apis::HTTP_SUCCESS,
               fake_drive_helper_->SearchByTitle(
                   parent_folder_id, title, &entries));
@@ -252,7 +252,7 @@
       const std::string& title,
       const std::string& primary_file_id,
       test_util::FileResourceKind kind) {
-    ScopedVector<google_apis::FileResource> entries;
+    std::vector<std::unique_ptr<google_apis::FileResource>> entries;
     EXPECT_EQ(google_apis::HTTP_SUCCESS,
               fake_drive_helper_->SearchByTitle(
                   parent_folder_id, title, &entries));
@@ -307,7 +307,7 @@
   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
   RunRemoteToLocalSyncerUntilIdle();
 
-  ScopedVector<google_apis::FileResource> entries =
+  std::vector<std::unique_ptr<google_apis::FileResource>> entries =
       GetResourceEntriesForParentAndTitle(app_root, kTitle);
   ASSERT_EQ(4u, entries.size());
 
@@ -333,7 +333,7 @@
   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
   RunRemoteToLocalSyncerUntilIdle();
 
-  ScopedVector<google_apis::FileResource> entries =
+  std::vector<std::unique_ptr<google_apis::FileResource>> entries =
       GetResourceEntriesForParentAndTitle(app_root, kTitle);
   ASSERT_EQ(4u, entries.size());
 
@@ -359,7 +359,7 @@
   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
   RunRemoteToLocalSyncerUntilIdle();
 
-  ScopedVector<google_apis::FileResource> entries =
+  std::vector<std::unique_ptr<google_apis::FileResource>> entries =
       GetResourceEntriesForParentAndTitle(app_root, kTitle);
   ASSERT_EQ(4u, entries.size());
 
@@ -391,7 +391,7 @@
   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
   RunRemoteToLocalSyncerUntilIdle();
 
-  ScopedVector<google_apis::FileResource> entries =
+  std::vector<std::unique_ptr<google_apis::FileResource>> entries =
       GetResourceEntriesForParentAndTitle(app_root, kTitle);
   ASSERT_EQ(2u, entries.size());
 
@@ -439,7 +439,7 @@
   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
   RunRemoteToLocalSyncerUntilIdle();
 
-  ScopedVector<google_apis::FileResource> entries =
+  std::vector<std::unique_ptr<google_apis::FileResource>> entries =
       GetResourceEntriesForParentAndTitle(app_root, kTitle);
   ASSERT_EQ(2u, entries.size());
 
diff --git a/chrome/browser/sync_file_system/drive_backend/drive_backend_sync_unittest.cc b/chrome/browser/sync_file_system/drive_backend/drive_backend_sync_unittest.cc
index 2ed5052..300fdc8 100644
--- a/chrome/browser/sync_file_system/drive_backend/drive_backend_sync_unittest.cc
+++ b/chrome/browser/sync_file_system/drive_backend/drive_backend_sync_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/stl_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/sync_file_system/drive_backend/callback_helper.h"
 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
@@ -412,18 +413,14 @@
     }
     EXPECT_EQ(google_apis::HTTP_SUCCESS, error);
 
-    ScopedVector<google_apis::FileResource> remote_entries;
+    std::vector<std::unique_ptr<google_apis::FileResource>> remote_entries;
     EXPECT_EQ(google_apis::HTTP_SUCCESS,
               fake_drive_service_helper_->ListFilesInFolder(
                   sync_root_folder_id, &remote_entries));
     std::map<std::string, const google_apis::FileResource*> app_root_by_title;
-    for (ScopedVector<google_apis::FileResource>::iterator itr =
-             remote_entries.begin();
-         itr != remote_entries.end();
-         ++itr) {
-      const google_apis::FileResource& remote_entry = **itr;
-      EXPECT_FALSE(base::ContainsKey(app_root_by_title, remote_entry.title()));
-      app_root_by_title[remote_entry.title()] = *itr;
+    for (const auto& remote_entry : remote_entries) {
+      EXPECT_FALSE(base::ContainsKey(app_root_by_title, remote_entry->title()));
+      app_root_by_title[remote_entry->title()] = remote_entry.get();
     }
 
     for (std::map<std::string, CannedSyncableFileSystem*>::const_iterator itr =
@@ -446,14 +443,14 @@
                                   CannedSyncableFileSystem* file_system) {
     SCOPED_TRACE(testing::Message() << "Verifying folder: " << path.value());
 
-    ScopedVector<google_apis::FileResource> remote_entries;
+    std::vector<std::unique_ptr<google_apis::FileResource>> remote_entries;
     EXPECT_EQ(google_apis::HTTP_SUCCESS,
               fake_drive_service_helper_->ListFilesInFolder(
                   folder_id, &remote_entries));
     std::map<std::string, const google_apis::FileResource*>
         remote_entry_by_title;
     for (size_t i = 0; i < remote_entries.size(); ++i) {
-      google_apis::FileResource* remote_entry = remote_entries[i];
+      google_apis::FileResource* remote_entry = remote_entries[i].get();
       EXPECT_FALSE(
           base::ContainsKey(remote_entry_by_title, remote_entry->title()))
           << "title: " << remote_entry->title();
diff --git a/chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.cc b/chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.cc
index 26d0a79..6975d04 100644
--- a/chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.cc
+++ b/chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.cc
@@ -217,12 +217,11 @@
   if (error != google_apis::HTTP_SUCCESS)
     return error;
 
-  const ScopedVector<FileResource>& items = resource_list->items();
-  for (ScopedVector<FileResource>::const_iterator itr = items.begin();
-       itr != items.end(); ++itr) {
-    const FileResource& item = **itr;
-    if (item.parents().empty()) {
-      *sync_root_folder_id = item.file_id();
+  const std::vector<std::unique_ptr<FileResource>>& items =
+      resource_list->items();
+  for (const auto& item : items) {
+    if (item->parents().empty()) {
+      *sync_root_folder_id = item->file_id();
       return google_apis::HTTP_SUCCESS;
     }
   }
@@ -231,7 +230,7 @@
 
 DriveApiErrorCode FakeDriveServiceHelper::ListFilesInFolder(
     const std::string& folder_id,
-    ScopedVector<FileResource>* entries) {
+    std::vector<std::unique_ptr<FileResource>>* entries) {
   DriveApiErrorCode error = google_apis::DRIVE_OTHER_ERROR;
   std::unique_ptr<FileList> list;
   fake_drive_service_->GetFileListInDirectory(
@@ -247,7 +246,7 @@
 DriveApiErrorCode FakeDriveServiceHelper::SearchByTitle(
     const std::string& folder_id,
     const std::string& title,
-    ScopedVector<FileResource>* entries) {
+    std::vector<std::unique_ptr<FileResource>>* entries) {
   DriveApiErrorCode error = google_apis::DRIVE_OTHER_ERROR;
   std::unique_ptr<FileList> list;
   fake_drive_service_->SearchByTitle(
@@ -316,15 +315,12 @@
 
 DriveApiErrorCode FakeDriveServiceHelper::CompleteListing(
     std::unique_ptr<FileList> list,
-    ScopedVector<FileResource>* entries) {
+    std::vector<std::unique_ptr<FileResource>>* entries) {
   while (true) {
     entries->reserve(entries->size() + list->items().size());
-    std::vector<FileResource*> tmp;
-    list->mutable_items()->release(&tmp);
-    for (std::vector<FileResource*>::const_iterator itr =
-             tmp.begin(); itr != tmp.end(); ++itr) {
-      entries->push_back(*itr);
-    }
+    std::move(list->mutable_items()->begin(), list->mutable_items()->end(),
+              std::back_inserter(*entries));
+    list->mutable_items()->clear();
 
     GURL next_feed = list->next_link();
     if (next_feed.is_empty())
diff --git a/chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.h b/chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.h
index 599417ec..36817aa 100644
--- a/chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.h
+++ b/chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.h
@@ -60,11 +60,11 @@
       std::string* sync_root_folder_id);
   google_apis::DriveApiErrorCode ListFilesInFolder(
       const std::string& folder_id,
-      ScopedVector<google_apis::FileResource>* entries);
+      std::vector<std::unique_ptr<google_apis::FileResource>>* entries);
   google_apis::DriveApiErrorCode SearchByTitle(
       const std::string& folder_id,
       const std::string& title,
-      ScopedVector<google_apis::FileResource>* entries);
+      std::vector<std::unique_ptr<google_apis::FileResource>>* entries);
 
   google_apis::DriveApiErrorCode GetFileResource(
       const std::string& file_id,
@@ -83,7 +83,7 @@
  private:
   google_apis::DriveApiErrorCode CompleteListing(
       std::unique_ptr<google_apis::FileList> list,
-      ScopedVector<google_apis::FileResource>* entries);
+      std::vector<std::unique_ptr<google_apis::FileResource>>* entries);
 
   void Initialize();
 
diff --git a/chrome/browser/sync_file_system/drive_backend/folder_creator.cc b/chrome/browser/sync_file_system/drive_backend/folder_creator.cc
index 8a7ec88..4d732bc 100644
--- a/chrome/browser/sync_file_system/drive_backend/folder_creator.cc
+++ b/chrome/browser/sync_file_system/drive_backend/folder_creator.cc
@@ -56,14 +56,16 @@
 
   drive_service_->SearchByTitle(
       title_, parent_folder_id_,
-      base::Bind(&FolderCreator::DidListFolders,
-                 weak_ptr_factory_.GetWeakPtr(), callback,
-                 base::Passed(ScopedVector<google_apis::FileResource>())));
+      base::Bind(
+          &FolderCreator::DidListFolders, weak_ptr_factory_.GetWeakPtr(),
+          callback,
+          base::Passed(
+              std::vector<std::unique_ptr<google_apis::FileResource>>())));
 }
 
 void FolderCreator::DidListFolders(
     const FileIDCallback& callback,
-    ScopedVector<google_apis::FileResource> candidates,
+    std::vector<std::unique_ptr<google_apis::FileResource>> candidates,
     google_apis::DriveApiErrorCode error,
     std::unique_ptr<google_apis::FileList> file_list) {
   SyncStatusCode status = DriveApiErrorCodeToSyncStatusCode(error);
@@ -79,10 +81,9 @@
   }
 
   candidates.reserve(candidates.size() + file_list->items().size());
-  candidates.insert(candidates.end(),
-                    file_list->items().begin(),
-                    file_list->items().end());
-  file_list->mutable_items()->weak_clear();
+  std::move(file_list->mutable_items()->begin(),
+            file_list->mutable_items()->end(), std::back_inserter(candidates));
+  file_list->mutable_items()->clear();
 
   if (!file_list->next_link().is_empty()) {
     drive_service_->GetRemainingFileList(
diff --git a/chrome/browser/sync_file_system/drive_backend/folder_creator.h b/chrome/browser/sync_file_system/drive_backend/folder_creator.h
index dca4186..53457df 100644
--- a/chrome/browser/sync_file_system/drive_backend/folder_creator.h
+++ b/chrome/browser/sync_file_system/drive_backend/folder_creator.h
@@ -9,7 +9,6 @@
 #include <string>
 
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/sync_file_system/sync_callbacks.h"
 #include "google_apis/drive/drive_api_error_codes.h"
@@ -45,10 +44,11 @@
   void DidCreateFolder(const FileIDCallback& callback,
                        google_apis::DriveApiErrorCode error,
                        std::unique_ptr<google_apis::FileResource> entry);
-  void DidListFolders(const FileIDCallback& callback,
-                      ScopedVector<google_apis::FileResource> candidates,
-                      google_apis::DriveApiErrorCode error,
-                      std::unique_ptr<google_apis::FileList> file_list);
+  void DidListFolders(
+      const FileIDCallback& callback,
+      std::vector<std::unique_ptr<google_apis::FileResource>> candidates,
+      google_apis::DriveApiErrorCode error,
+      std::unique_ptr<google_apis::FileList> file_list);
 
   drive::DriveServiceInterface* drive_service_;
   MetadataDatabase* metadata_database_;
diff --git a/chrome/browser/sync_file_system/drive_backend/list_changes_task.cc b/chrome/browser/sync_file_system/drive_backend/list_changes_task.cc
index 7d8d793..9991ff3c 100644
--- a/chrome/browser/sync_file_system/drive_backend/list_changes_task.cc
+++ b/chrome/browser/sync_file_system/drive_backend/list_changes_task.cc
@@ -75,12 +75,12 @@
     return;
   }
 
-  std::vector<google_apis::ChangeResource*> changes;
-  change_list->mutable_items()->release(&changes);
-
-  change_list_.reserve(change_list_.size() + changes.size());
-  for (size_t i = 0; i < changes.size(); ++i)
-    change_list_.push_back(changes[i]);
+  change_list_.reserve(change_list_.size() +
+                       change_list->mutable_items()->size());
+  std::move(change_list->mutable_items()->begin(),
+            change_list->mutable_items()->end(),
+            std::back_inserter(change_list_));
+  change_list->mutable_items()->clear();
 
   if (!change_list->next_link().is_empty()) {
     drive_service()->GetRemainingChangeList(
diff --git a/chrome/browser/sync_file_system/drive_backend/list_changes_task.h b/chrome/browser/sync_file_system/drive_backend/list_changes_task.h
index 9b47673e..220dcdd 100644
--- a/chrome/browser/sync_file_system/drive_backend/list_changes_task.h
+++ b/chrome/browser/sync_file_system/drive_backend/list_changes_task.h
@@ -10,7 +10,6 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/sync_file_system/drive_backend/sync_task.h"
 #include "google_apis/drive/drive_api_error_codes.h"
@@ -50,7 +49,7 @@
   drive::DriveServiceInterface* drive_service();
 
   SyncEngineContext* sync_context_;
-  ScopedVector<google_apis::ChangeResource> change_list_;
+  std::vector<std::unique_ptr<google_apis::ChangeResource>> change_list_;
 
   std::vector<std::string> file_ids_;
 
diff --git a/chrome/browser/sync_file_system/drive_backend/local_to_remote_syncer_unittest.cc b/chrome/browser/sync_file_system/drive_backend/local_to_remote_syncer_unittest.cc
index d08ac823..bb9ce1d 100644
--- a/chrome/browser/sync_file_system/drive_backend/local_to_remote_syncer_unittest.cc
+++ b/chrome/browser/sync_file_system/drive_backend/local_to_remote_syncer_unittest.cc
@@ -210,10 +210,10 @@
     return status;
   }
 
-  ScopedVector<google_apis::FileResource>
+  std::vector<std::unique_ptr<google_apis::FileResource>>
   GetResourceEntriesForParentAndTitle(const std::string& parent_folder_id,
                                       const std::string& title) {
-    ScopedVector<google_apis::FileResource> entries;
+    std::vector<std::unique_ptr<google_apis::FileResource>> entries;
     EXPECT_EQ(google_apis::HTTP_SUCCESS,
               fake_drive_helper_->SearchByTitle(
                   parent_folder_id, title, &entries));
@@ -222,7 +222,7 @@
 
   std::string GetFileIDForParentAndTitle(const std::string& parent_folder_id,
                                          const std::string& title) {
-    ScopedVector<google_apis::FileResource> entries =
+    std::vector<std::unique_ptr<google_apis::FileResource>> entries =
         GetResourceEntriesForParentAndTitle(parent_folder_id, title);
     if (entries.size() != 1)
       return std::string();
@@ -233,7 +233,7 @@
       const std::string& parent_folder_id,
       const std::string& title,
       test_util::FileResourceKind kind) {
-    ScopedVector<google_apis::FileResource> entries;
+    std::vector<std::unique_ptr<google_apis::FileResource>> entries;
     EXPECT_EQ(google_apis::HTTP_SUCCESS,
               fake_drive_helper_->SearchByTitle(
                   parent_folder_id, title, &entries));
@@ -243,7 +243,7 @@
 
   void VerifyFileDeletion(const std::string& parent_folder_id,
                           const std::string& title) {
-    ScopedVector<google_apis::FileResource> entries;
+    std::vector<std::unique_ptr<google_apis::FileResource>> entries;
     EXPECT_EQ(google_apis::HTTP_SUCCESS,
               fake_drive_helper_->SearchByTitle(
                   parent_folder_id, title, &entries));
@@ -377,7 +377,7 @@
       URL(kOrigin, "foo")));
 
   // There should exist both file and folder on remote.
-  ScopedVector<google_apis::FileResource> entries =
+  std::vector<std::unique_ptr<google_apis::FileResource>> entries =
       GetResourceEntriesForParentAndTitle(app_root, "foo");
   ASSERT_EQ(2u, entries.size());
   EXPECT_EQ(test_util::RESOURCE_KIND_FOLDER,
@@ -402,7 +402,7 @@
       URL(kOrigin, "foo")));
 
   // There should exist both file and folder on remote.
-  ScopedVector<google_apis::FileResource> entries =
+  std::vector<std::unique_ptr<google_apis::FileResource>> entries =
       GetResourceEntriesForParentAndTitle(app_root, "foo");
   ASSERT_EQ(2u, entries.size());
   EXPECT_EQ(test_util::RESOURCE_KIND_FILE,
@@ -427,7 +427,7 @@
       URL(kOrigin, "foo")));
 
   // There should exist both files on remote.
-  ScopedVector<google_apis::FileResource> entries =
+  std::vector<std::unique_ptr<google_apis::FileResource>> entries =
       GetResourceEntriesForParentAndTitle(app_root, "foo");
   ASSERT_EQ(2u, entries.size());
   EXPECT_EQ(test_util::RESOURCE_KIND_FILE,
@@ -459,7 +459,7 @@
                  SYNC_FILE_TYPE_FILE),
       URL(kOrigin, "foo")));
 
-  ScopedVector<google_apis::FileResource> entries =
+  std::vector<std::unique_ptr<google_apis::FileResource>> entries =
       GetResourceEntriesForParentAndTitle(app_root, "foo");
   ASSERT_EQ(1u, entries.size());
   EXPECT_EQ(test_util::RESOURCE_KIND_FILE,
@@ -489,7 +489,7 @@
                  SYNC_FILE_TYPE_FILE),
       URL(kOrigin, "foo")));
 
-  ScopedVector<google_apis::FileResource> entries =
+  std::vector<std::unique_ptr<google_apis::FileResource>> entries =
       GetResourceEntriesForParentAndTitle(app_root, "foo");
   ASSERT_EQ(1u, entries.size());
   EXPECT_EQ(test_util::RESOURCE_KIND_FILE,
@@ -512,7 +512,7 @@
                  SYNC_FILE_TYPE_DIRECTORY),
       URL(kOrigin, "foo")));
 
-  ScopedVector<google_apis::FileResource> entries =
+  std::vector<std::unique_ptr<google_apis::FileResource>> entries =
       GetResourceEntriesForParentAndTitle(app_root, "foo");
   ASSERT_EQ(2u, entries.size());
   EXPECT_EQ(test_util::RESOURCE_KIND_FOLDER,
diff --git a/chrome/browser/sync_file_system/drive_backend/metadata_database.cc b/chrome/browser/sync_file_system/drive_backend/metadata_database.cc
index 0b1e11e..84436d6 100644
--- a/chrome/browser/sync_file_system/drive_backend/metadata_database.cc
+++ b/chrome/browser/sync_file_system/drive_backend/metadata_database.cc
@@ -622,7 +622,8 @@
 SyncStatusCode MetadataDatabase::PopulateInitialData(
     int64_t largest_change_id,
     const google_apis::FileResource& sync_root_folder,
-    const ScopedVector<google_apis::FileResource>& app_root_folders) {
+    const std::vector<std::unique_ptr<google_apis::FileResource>>&
+        app_root_folders) {
   index_->SetLargestChangeID(largest_change_id);
   UpdateLargestKnownChangeID(largest_change_id);
 
@@ -889,7 +890,7 @@
 
 SyncStatusCode MetadataDatabase::UpdateByChangeList(
     int64_t largest_change_id,
-    ScopedVector<google_apis::ChangeResource> changes) {
+    std::vector<std::unique_ptr<google_apis::ChangeResource>> changes) {
   DCHECK_LE(index_->GetLargestChangeID(), largest_change_id);
 
   for (size_t i = 0; i < changes.size(); ++i) {
@@ -918,7 +919,7 @@
 }
 
 SyncStatusCode MetadataDatabase::UpdateByFileResourceList(
-    ScopedVector<google_apis::FileResource> resources) {
+    std::vector<std::unique_ptr<google_apis::FileResource>> resources) {
   for (size_t i = 0; i < resources.size(); ++i) {
     std::unique_ptr<FileMetadata> metadata(CreateFileMetadataFromFileResource(
         GetLargestKnownChangeID(), *resources[i]));
diff --git a/chrome/browser/sync_file_system/drive_backend/metadata_database.h b/chrome/browser/sync_file_system/drive_backend/metadata_database.h
index dc89685..e47eca7 100644
--- a/chrome/browser/sync_file_system/drive_backend/metadata_database.h
+++ b/chrome/browser/sync_file_system/drive_backend/metadata_database.h
@@ -17,7 +17,6 @@
 #include "base/containers/hash_tables.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "base/values.h"
@@ -183,7 +182,8 @@
   SyncStatusCode PopulateInitialData(
       int64_t largest_change_id,
       const google_apis::FileResource& sync_root_folder,
-      const ScopedVector<google_apis::FileResource>& app_root_folders);
+      const std::vector<std::unique_ptr<google_apis::FileResource>>&
+          app_root_folders);
 
   // Returns true if the folder associated to |app_id| is enabled.
   bool IsAppEnabled(const std::string& app_id) const;
@@ -261,7 +261,7 @@
   // needed.
   SyncStatusCode UpdateByChangeList(
       int64_t largest_change_id,
-      ScopedVector<google_apis::ChangeResource> changes);
+      std::vector<std::unique_ptr<google_apis::ChangeResource>> changes);
 
   // Updates database by |resource|.
   // Marks each tracker for modified file as dirty and adds new trackers if
@@ -269,7 +269,7 @@
   SyncStatusCode UpdateByFileResource(
       const google_apis::FileResource& resource);
   SyncStatusCode UpdateByFileResourceList(
-      ScopedVector<google_apis::FileResource> resources);
+      std::vector<std::unique_ptr<google_apis::FileResource>> resources);
 
   SyncStatusCode UpdateByDeletedRemoteFile(const std::string& file_id);
   SyncStatusCode UpdateByDeletedRemoteFileList(const FileIDList& file_ids);
diff --git a/chrome/browser/sync_file_system/drive_backend/metadata_database_index.cc b/chrome/browser/sync_file_system/drive_backend/metadata_database_index.cc
index e2a092ee..c85fde8 100644
--- a/chrome/browser/sync_file_system/drive_backend/metadata_database_index.cc
+++ b/chrome/browser/sync_file_system/drive_backend/metadata_database_index.cc
@@ -98,7 +98,7 @@
         continue;
       }
 
-      contents->file_metadata.push_back(metadata.release());
+      contents->file_metadata.push_back(std::move(metadata));
       continue;
     }
 
@@ -117,7 +117,7 @@
                   "Failed to parse a Tracker");
         continue;
       }
-      contents->file_trackers.push_back(tracker.release());
+      contents->file_trackers.push_back(std::move(tracker));
       continue;
     }
   }
@@ -168,12 +168,11 @@
   }
 
   // Delete all unreachable trackers.
-  ScopedVector<FileTracker> reachable_trackers;
+  std::vector<std::unique_ptr<FileTracker>> reachable_trackers;
   for (size_t i = 0; i < contents->file_trackers.size(); ++i) {
-    FileTracker* tracker = contents->file_trackers[i];
+    std::unique_ptr<FileTracker>& tracker = contents->file_trackers[i];
     if (base::ContainsKey(visited_trackers, tracker->tracker_id())) {
-      reachable_trackers.push_back(tracker);
-      contents->file_trackers[i] = nullptr;
+      reachable_trackers.push_back(std::move(tracker));
     } else {
       PutFileTrackerDeletionToDB(tracker->tracker_id(), db);
     }
@@ -186,12 +185,11 @@
     referred_file_ids.insert(contents->file_trackers[i]->file_id());
 
   // Delete all unreferred metadata.
-  ScopedVector<FileMetadata> referred_file_metadata;
+  std::vector<std::unique_ptr<FileMetadata>> referred_file_metadata;
   for (size_t i = 0; i < contents->file_metadata.size(); ++i) {
-    FileMetadata* metadata = contents->file_metadata[i];
+    std::unique_ptr<FileMetadata>& metadata = contents->file_metadata[i];
     if (base::ContainsKey(referred_file_ids, metadata->file_id())) {
-      referred_file_metadata.push_back(metadata);
-      contents->file_metadata[i] = nullptr;
+      referred_file_metadata.push_back(std::move(metadata));
     } else {
       PutFileMetadataDeletionToDB(metadata->file_id(), db);
     }
@@ -238,12 +236,12 @@
   service_metadata_ = std::move(service_metadata);
 
   for (size_t i = 0; i < contents->file_metadata.size(); ++i)
-    StoreFileMetadata(base::WrapUnique(contents->file_metadata[i]));
-  contents->file_metadata.weak_clear();
+    StoreFileMetadata(std::move(contents->file_metadata[i]));
+  contents->file_metadata.clear();
 
   for (size_t i = 0; i < contents->file_trackers.size(); ++i)
-    StoreFileTracker(base::WrapUnique(contents->file_trackers[i]));
-  contents->file_trackers.weak_clear();
+    StoreFileTracker(std::move(contents->file_trackers[i]));
+  contents->file_trackers.clear();
 
   UMA_HISTOGRAM_COUNTS("SyncFileSystem.MetadataNumber", metadata_by_id_.size());
   UMA_HISTOGRAM_COUNTS("SyncFileSystem.TrackerNumber", tracker_by_id_.size());
diff --git a/chrome/browser/sync_file_system/drive_backend/metadata_database_index.h b/chrome/browser/sync_file_system/drive_backend/metadata_database_index.h
index 5c7aa62..38ff499 100644
--- a/chrome/browser/sync_file_system/drive_backend/metadata_database_index.h
+++ b/chrome/browser/sync_file_system/drive_backend/metadata_database_index.h
@@ -17,7 +17,6 @@
 #include "base/containers/hash_tables.h"
 #include "base/hash.h"
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "chrome/browser/sync_file_system/drive_backend/metadata_database_index_interface.h"
 #include "chrome/browser/sync_file_system/drive_backend/tracker_id_set.h"
 
@@ -49,8 +48,8 @@
 struct DatabaseContents {
   DatabaseContents();
   ~DatabaseContents();
-  ScopedVector<FileMetadata> file_metadata;
-  ScopedVector<FileTracker> file_trackers;
+  std::vector<std::unique_ptr<FileMetadata>> file_metadata;
+  std::vector<std::unique_ptr<FileTracker>> file_trackers;
 };
 
 // Maintains indexes of MetadataDatabase on memory.
diff --git a/chrome/browser/sync_file_system/drive_backend/metadata_database_index_unittest.cc b/chrome/browser/sync_file_system/drive_backend/metadata_database_index_unittest.cc
index 217b592c..6578079 100644
--- a/chrome/browser/sync_file_system/drive_backend/metadata_database_index_unittest.cc
+++ b/chrome/browser/sync_file_system/drive_backend/metadata_database_index_unittest.cc
@@ -54,13 +54,13 @@
       test_util::CreatePlaceholderTracker(
           "unsynced_file_id", kPlaceholderTrackerID, app_root_tracker.get());
 
-  contents->file_metadata.push_back(sync_root_metadata.release());
-  contents->file_trackers.push_back(sync_root_tracker.release());
-  contents->file_metadata.push_back(app_root_metadata.release());
-  contents->file_trackers.push_back(app_root_tracker.release());
-  contents->file_metadata.push_back(file_metadata.release());
-  contents->file_trackers.push_back(file_tracker.release());
-  contents->file_trackers.push_back(placeholder_tracker.release());
+  contents->file_metadata.push_back(std::move(sync_root_metadata));
+  contents->file_trackers.push_back(std::move(sync_root_tracker));
+  contents->file_metadata.push_back(std::move(app_root_metadata));
+  contents->file_trackers.push_back(std::move(app_root_tracker));
+  contents->file_metadata.push_back(std::move(file_metadata));
+  contents->file_trackers.push_back(std::move(file_tracker));
+  contents->file_trackers.push_back(std::move(placeholder_tracker));
   return contents;
 }
 
diff --git a/chrome/browser/sync_file_system/drive_backend/metadata_database_unittest.cc b/chrome/browser/sync_file_system/drive_backend/metadata_database_unittest.cc
index e456d4ce7..5d6d331f 100644
--- a/chrome/browser/sync_file_system/drive_backend/metadata_database_unittest.cc
+++ b/chrome/browser/sync_file_system/drive_backend/metadata_database_unittest.cc
@@ -462,9 +462,10 @@
     file->mutable_details()->set_change_id(++current_change_id_);
   }
 
-  void PushToChangeList(std::unique_ptr<google_apis::ChangeResource> change,
-                        ScopedVector<google_apis::ChangeResource>* changes) {
-    changes->push_back(change.release());
+  void PushToChangeList(
+      std::unique_ptr<google_apis::ChangeResource> change,
+      std::vector<std::unique_ptr<google_apis::ChangeResource>>* changes) {
+    changes->push_back(std::move(change));
   }
 
   leveldb::Status PutFileToDB(LevelDBWrapper* db, const FileMetadata& file) {
@@ -586,7 +587,7 @@
   }
 
   SyncStatusCode UpdateByChangeList(
-      ScopedVector<google_apis::ChangeResource> changes) {
+      std::vector<std::unique_ptr<google_apis::ChangeResource>> changes) {
     return metadata_database_->UpdateByChangeList(current_change_id_,
                                                   std::move(changes));
   }
@@ -605,7 +606,8 @@
   SyncStatusCode PopulateInitialData(
       int64_t largest_change_id,
       const google_apis::FileResource& sync_root_folder,
-      const ScopedVector<google_apis::FileResource>& app_root_folders) {
+      const std::vector<std::unique_ptr<google_apis::FileResource>>&
+          app_root_folders) {
     return metadata_database_->PopulateInitialData(
         largest_change_id, sync_root_folder, app_root_folders);
   }
@@ -915,7 +917,7 @@
   // Update change ID.
   ApplyNoopChangeToMetadata(&noop_file.metadata);
 
-  ScopedVector<google_apis::ChangeResource> changes;
+  std::vector<std::unique_ptr<google_apis::ChangeResource>> changes;
   PushToChangeList(
       CreateChangeResourceFromMetadata(renamed_file.metadata), &changes);
   PushToChangeList(
@@ -1119,8 +1121,8 @@
   std::unique_ptr<google_apis::FileResource> app_root_folder(
       CreateFileResourceFromMetadata(app_root.metadata));
 
-  ScopedVector<google_apis::FileResource> app_root_folders;
-  app_root_folders.push_back(app_root_folder.release());
+  std::vector<std::unique_ptr<google_apis::FileResource>> app_root_folders;
+  app_root_folders.push_back(std::move(app_root_folder));
 
   EXPECT_EQ(SYNC_STATUS_OK, InitializeMetadataDatabase());
   EXPECT_EQ(SYNC_STATUS_OK, PopulateInitialData(
diff --git a/chrome/browser/sync_file_system/drive_backend/register_app_task_unittest.cc b/chrome/browser/sync_file_system/drive_backend/register_app_task_unittest.cc
index 0bee87d..ffebda20 100644
--- a/chrome/browser/sync_file_system/drive_backend/register_app_task_unittest.cc
+++ b/chrome/browser/sync_file_system/drive_backend/register_app_task_unittest.cc
@@ -205,7 +205,7 @@
   }
 
   size_t CountRemoteFileInSyncRoot() {
-    ScopedVector<google_apis::FileResource> files;
+    std::vector<std::unique_ptr<google_apis::FileResource>> files;
     EXPECT_EQ(google_apis::HTTP_SUCCESS,
               fake_drive_service_helper_->ListFilesInFolder(
                   sync_root_folder_id_, &files));
diff --git a/chrome/browser/sync_file_system/drive_backend/remote_to_local_syncer.cc b/chrome/browser/sync_file_system/drive_backend/remote_to_local_syncer.cc
index f578c43a..b3af159 100644
--- a/chrome/browser/sync_file_system/drive_backend/remote_to_local_syncer.cc
+++ b/chrome/browser/sync_file_system/drive_backend/remote_to_local_syncer.cc
@@ -676,11 +676,8 @@
   }
 
   children->reserve(children->size() + file_list->items().size());
-  for (ScopedVector<google_apis::FileResource>::const_iterator itr =
-           file_list->items().begin();
-       itr != file_list->items().end();
-       ++itr) {
-    children->push_back((*itr)->file_id());
+  for (const auto& file_resource : file_list->items()) {
+    children->push_back(file_resource->file_id());
   }
 
   if (!file_list->next_link().is_empty()) {
diff --git a/chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.cc b/chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.cc
index 6115d81..4d1dd0f 100644
--- a/chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.cc
+++ b/chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.cc
@@ -180,11 +180,9 @@
     return;
   }
 
-  ScopedVector<google_apis::FileResource>* items = file_list->mutable_items();
-  for (ScopedVector<google_apis::FileResource>::iterator itr = items->begin();
-       itr != items->end(); ++itr) {
-    google_apis::FileResource* entry = *itr;
-
+  std::vector<std::unique_ptr<google_apis::FileResource>>* items =
+      file_list->mutable_items();
+  for (auto& entry : *items) {
     // Ignore deleted folder.
     if (entry->labels().is_trashed())
       continue;
@@ -199,8 +197,7 @@
       continue;
 
     if (!sync_root_folder_ || LessOnCreationTime(*entry, *sync_root_folder_)) {
-      sync_root_folder_.reset(entry);
-      *itr = nullptr;
+      sync_root_folder_ = std::move(entry);
     }
   }
 
@@ -321,11 +318,12 @@
     return;
   }
 
-  ScopedVector<google_apis::FileResource>* new_entries =
+  std::vector<std::unique_ptr<google_apis::FileResource>>* new_entries =
       file_list->mutable_items();
-  app_root_folders_.insert(app_root_folders_.end(),
-                           new_entries->begin(), new_entries->end());
-  new_entries->weak_clear();
+  app_root_folders_.reserve(app_root_folders_.size() + new_entries->size());
+  std::move(new_entries->begin(), new_entries->end(),
+            std::back_inserter(app_root_folders_));
+  new_entries->clear();
 
   set_used_network(true);
   if (!file_list->next_link().is_empty()) {
diff --git a/chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.h b/chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.h
index e720b40e..1fce116 100644
--- a/chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.h
+++ b/chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.h
@@ -12,7 +12,6 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/memory/scoped_vector.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/sync_file_system/drive_backend/sync_task.h"
 #include "chrome/browser/sync_file_system/sync_callbacks.h"
@@ -106,7 +105,7 @@
   int find_sync_root_retry_count_;
 
   std::unique_ptr<MetadataDatabase> metadata_database_;
-  ScopedVector<google_apis::FileResource> app_root_folders_;
+  std::vector<std::unique_ptr<google_apis::FileResource>> app_root_folders_;
 
   int64_t largest_change_id_;
   std::string root_folder_id_;
diff --git a/chrome/browser/sync_file_system/drive_backend/sync_engine_initializer_unittest.cc b/chrome/browser/sync_file_system/drive_backend/sync_engine_initializer_unittest.cc
index 7559a18..ec2f2ec 100644
--- a/chrome/browser/sync_file_system/drive_backend/sync_engine_initializer_unittest.cc
+++ b/chrome/browser/sync_file_system/drive_backend/sync_engine_initializer_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/bind.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
@@ -114,18 +115,24 @@
     if (status != SYNC_STATUS_OK)
       return status;
 
-    // |app_root_list| must not own the resources here. Be sure to call
-    // weak_clear later.
-    ScopedVector<google_apis::FileResource> app_root_list;
+    // |app_root_list| must not own the resources here. Be sure to release
+    // ownership of all elements later.
+    // TODO(leonhsl) Need follow up: having two owners for a pointer is a
+    // dangerous pattern that we don't want to keep around.
+    std::vector<std::unique_ptr<google_apis::FileResource>> app_root_list;
     for (size_t i = 0; i < app_roots_count; ++i) {
-      app_root_list.push_back(
-          const_cast<google_apis::FileResource*>(app_roots[i]));
+      app_root_list.push_back(base::WrapUnique(
+          const_cast<google_apis::FileResource*>(app_roots[i])));
     }
 
     status = database->PopulateInitialData(
         kInitialLargestChangeID, sync_root, app_root_list);
 
-    app_root_list.weak_clear();
+    for_each(app_root_list.begin(), app_root_list.end(),
+             [](std::unique_ptr<google_apis::FileResource>& entry) {
+               entry.release();
+             });
+    app_root_list.clear();
     return status;
   }
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index a8261f1..db7302c 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1508,6 +1508,8 @@
       "views/subtle_notification_view.h",
       "views/sync/bubble_sync_promo_view.cc",
       "views/sync/bubble_sync_promo_view.h",
+      "views/sync/profile_signin_confirmation_dialog_views.cc",
+      "views/sync/profile_signin_confirmation_dialog_views.h",
       "views/task_manager_view.cc",
       "views/task_manager_view.h",
       "views/website_settings/chosen_object_row.cc",
@@ -1559,8 +1561,6 @@
         "views/screen_capture_notification_ui_views.cc",
         "views/sync/one_click_signin_dialog_view.cc",
         "views/sync/one_click_signin_dialog_view.h",
-        "views/sync/profile_signin_confirmation_dialog_views.cc",
-        "views/sync/profile_signin_confirmation_dialog_views.h",
       ]
     }
 
diff --git a/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_dialog_cocoa.h b/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_dialog_cocoa.h
index d23724f..a596b90 100644
--- a/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_dialog_cocoa.h
+++ b/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_dialog_cocoa.h
@@ -38,16 +38,17 @@
       content::WebContents* web_contents,
       Profile* profile,
       const std::string& username,
-      ui::ProfileSigninConfirmationDelegate* delegate,
+      std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate,
       bool offer_profile_creation);
   virtual ~ProfileSigninConfirmationDialogCocoa();
 
   // Shows the dialog if needed.
-  static void Show(Browser* browser,
-                   content::WebContents* web_contents,
-                   Profile* profile,
-                   const std::string& username,
-                   ui::ProfileSigninConfirmationDelegate* delegate);
+  static void Show(
+      Browser* browser,
+      content::WebContents* web_contents,
+      Profile* profile,
+      const std::string& username,
+      std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate);
 
   // Closes the dialog, which deletes itself.
   void Close();
diff --git a/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_dialog_cocoa.mm b/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_dialog_cocoa.mm
index dbd031b..eab52ff 100644
--- a/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_dialog_cocoa.mm
+++ b/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_dialog_cocoa.mm
@@ -16,19 +16,15 @@
 namespace {
 
 // static
-void ShowDialog(
-    Browser* browser,
-    content::WebContents* web_contents,
-    Profile* profile,
-    const std::string& username,
-    ui::ProfileSigninConfirmationDelegate* delegate,
-    bool offer_profile_creation) {
+void ShowDialog(Browser* browser,
+                content::WebContents* web_contents,
+                Profile* profile,
+                const std::string& username,
+                std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate,
+                bool offer_profile_creation) {
   // The dialog owns itself.
-  new ProfileSigninConfirmationDialogCocoa(browser,
-                                           web_contents,
-                                           profile,
-                                           username,
-                                           delegate,
+  new ProfileSigninConfirmationDialogCocoa(browser, web_contents, profile,
+                                           username, std::move(delegate),
                                            offer_profile_creation);
 }
 
@@ -39,19 +35,18 @@
     content::WebContents* web_contents,
     Profile* profile,
     const std::string& username,
-    ui::ProfileSigninConfirmationDelegate* delegate,
+    std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate,
     bool offer_profile_creation) {
   // Setup the dialog view controller.
   const base::Closure& closeDialogCallback =
       base::Bind(&ProfileSigninConfirmationDialogCocoa::Close,
                  base::Unretained(this));
-  controller_.reset(
-      [[ProfileSigninConfirmationViewController alloc]
-          initWithBrowser:browser
-                 username:username
-                 delegate:delegate
-      closeDialogCallback:closeDialogCallback
-     offerProfileCreation:offer_profile_creation]);
+  controller_.reset([[ProfileSigninConfirmationViewController alloc]
+           initWithBrowser:browser
+                  username:username
+                  delegate:std::move(delegate)
+       closeDialogCallback:closeDialogCallback
+      offerProfileCreation:offer_profile_creation]);
 
   // Setup the constrained window that will show the view.
   base::scoped_nsobject<NSWindow> window([[ConstrainedWindowCustomWindow alloc]
@@ -71,10 +66,10 @@
     content::WebContents* web_contents,
     Profile* profile,
     const std::string& username,
-    ui::ProfileSigninConfirmationDelegate* delegate) {
+    std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate) {
   ui::CheckShouldPromptForNewProfile(
       profile, base::Bind(ShowDialog, browser, web_contents, profile, username,
-                          delegate));
+                          base::Passed(std::move(delegate))));
 }
 
 void ProfileSigninConfirmationDialogCocoa::Close() {
diff --git a/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller.h b/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller.h
index 2a70b08..8e4a35c 100644
--- a/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller.h
+++ b/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_COCOA_PROFILES_PROFILE_SIGNIN_CONFIRMATION_VIEW_CONTROLLER_
 
 #import <Cocoa/Cocoa.h>
+#include <memory>
 #include <string>
 
 #include "base/callback.h"
@@ -33,7 +34,7 @@
   bool offerProfileCreation_;
 
   // Dialog button callbacks.
-  ui::ProfileSigninConfirmationDelegate* delegate_;
+  std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate_;
   base::Closure closeDialogCallback_;
 
   // UI elements.
@@ -48,10 +49,12 @@
 }
 
 - (id)initWithBrowser:(Browser*)browser
-             username:(const std::string&)username
-             delegate:(ui::ProfileSigninConfirmationDelegate*)delegate
-  closeDialogCallback:(const base::Closure&)closeDialogCallback
- offerProfileCreation:(bool)offer;
+                username:(const std::string&)username
+                delegate:
+                    (std::unique_ptr<ui::ProfileSigninConfirmationDelegate>)
+                        delegate
+     closeDialogCallback:(const base::Closure&)closeDialogCallback
+    offerProfileCreation:(bool)offer;
 - (IBAction)cancel:(id)sender;
 - (IBAction)ok:(id)sender;
 - (IBAction)close:(id)sender;
@@ -59,12 +62,4 @@
 
 @end
 
-@interface ProfileSigninConfirmationViewController (TestingAPI)
-
-@property(readonly, nonatomic) ui::ProfileSigninConfirmationDelegate* delegate;
-@property(readonly, nonatomic) NSButton* createProfileButton;
-@property(readonly, nonatomic) NSTextView* explanationField;
-
-@end
-
 #endif  // CHROME_BROWSER_UI_COCOA_PROFILES_PROFILE_SIGNIN_CONFIRMATION_VIEW_CONTROLLER_
diff --git a/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller.mm b/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller.mm
index 5a7480f..28f0873 100644
--- a/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller.mm
+++ b/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller.mm
@@ -142,14 +142,16 @@
 @implementation ProfileSigninConfirmationViewController
 
 - (id)initWithBrowser:(Browser*)browser
-             username:(const std::string&)username
-             delegate:(ui::ProfileSigninConfirmationDelegate*)delegate
-  closeDialogCallback:(const base::Closure&)closeDialogCallback
- offerProfileCreation:(bool)offer {
+                username:(const std::string&)username
+                delegate:
+                    (std::unique_ptr<ui::ProfileSigninConfirmationDelegate>)
+                        delegate
+     closeDialogCallback:(const base::Closure&)closeDialogCallback
+    offerProfileCreation:(bool)offer {
   if ((self = [super initWithNibName:nil bundle:nil])) {
     browser_ = browser;
     username_ = username;
-    delegate_ = delegate;
+    delegate_ = std::move(delegate);
     closeDialogCallback_ = closeDialogCallback;
     offerProfileCreation_ = offer;
   }
@@ -376,7 +378,7 @@
 - (IBAction)cancel:(id)sender {
   if (delegate_) {
     delegate_->OnCancelSignin();
-    delegate_ = NULL;
+    delegate_ = nullptr;
     closeDialogCallback_.Run();
   }
 }
@@ -384,7 +386,7 @@
 - (IBAction)ok:(id)sender {
   if (delegate_) {
     delegate_->OnContinueSignin();
-    delegate_ = NULL;
+    delegate_ = nullptr;
     closeDialogCallback_.Run();
   }
 }
@@ -392,7 +394,7 @@
 - (IBAction)close:(id)sender {
   if (delegate_) {
     delegate_->OnCancelSignin();
-    delegate_ = NULL;
+    delegate_ = nullptr;
   }
   closeDialogCallback_.Run();
 }
@@ -400,7 +402,7 @@
 - (IBAction)createProfile:(id)sender {
   if (delegate_) {
     delegate_->OnSigninWithNewProfile();
-    delegate_ = NULL;
+    delegate_ = nullptr;
     closeDialogCallback_.Run();
   }
 }
@@ -440,19 +442,3 @@
 }
 
 @end
-
-@implementation ProfileSigninConfirmationViewController (TestingAPI)
-
-- (ui::ProfileSigninConfirmationDelegate*)delegate {
-  return delegate_;
-}
-
-- (NSButton*)createProfileButton {
-  return createProfileButton_.get();
-}
-
-- (NSTextView*)explanationField {
-  return explanationField_.get();
-}
-
-@end
diff --git a/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller_browsertest.mm b/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller_browsertest.mm
index 6380427..4311f676 100644
--- a/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller_browsertest.mm
+++ b/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller_browsertest.mm
@@ -18,10 +18,32 @@
 #import "testing/gtest_mac.h"
 #include "ui/base/l10n/l10n_util.h"
 
-class ProfileSigninConfirmationViewControllerTest
-  : public InProcessBrowserTest,
-    public ui::ProfileSigninConfirmationDelegate {
+@interface ProfileSigninConfirmationViewController (TestingAPI)
 
+@property(readonly, nonatomic) ui::ProfileSigninConfirmationDelegate* delegate;
+@property(readonly, nonatomic) NSButton* createProfileButton;
+@property(readonly, nonatomic) NSTextView* explanationField;
+
+@end
+
+@implementation ProfileSigninConfirmationViewController (TestingAPI)
+
+- (ui::ProfileSigninConfirmationDelegate*)delegate {
+  return delegate_.get();
+}
+
+- (NSButton*)createProfileButton {
+  return createProfileButton_.get();
+}
+
+- (NSTextView*)explanationField {
+  return explanationField_.get();
+}
+
+@end
+
+class ProfileSigninConfirmationViewControllerTest
+    : public InProcessBrowserTest {
  public:
   ProfileSigninConfirmationViewControllerTest()
     : window_(nil),
@@ -44,11 +66,11 @@
         &ProfileSigninConfirmationViewControllerTest::OnClose,
         base::Unretained(this));
     controller_.reset([[ProfileSigninConfirmationViewController alloc]
-                        initWithBrowser:browser()
-                               username:username()
-                               delegate:this
-                    closeDialogCallback:close
-                   offerProfileCreation:offerProfileCreation]);
+             initWithBrowser:browser()
+                    username:username()
+                    delegate:base::MakeUnique<TestSigninDelegate>(this)
+         closeDialogCallback:close
+        offerProfileCreation:offerProfileCreation]);
     [[window_ contentView] addSubview:[controller_ view]];
     [window_ makeKeyAndOrderFront:NSApp];
     ASSERT_TRUE([window_ isVisible]);
@@ -63,10 +85,6 @@
         IDS_ENTERPRISE_SIGNIN_PROFILE_LINK_LEARN_MORE);
   }
 
-  // ui::ProfileSigninConfirmationDelegate:
-  void OnContinueSignin() override { continued_ = true; }
-  void OnCancelSignin() override { cancelled_ = true; }
-  void OnSigninWithNewProfile() override { created_ = true; }
   void OnClose() { closed_ = true; }
 
   // The window containing the dialog.
@@ -82,6 +100,23 @@
   bool closed_;
 
  private:
+  class TestSigninDelegate : public ui::ProfileSigninConfirmationDelegate {
+   public:
+    explicit TestSigninDelegate(
+        ProfileSigninConfirmationViewControllerTest* client)
+        : client_(client) {}
+
+    // ui::ProfileSigninConfirmationDelegate:
+    void OnContinueSignin() override { client_->continued_ = true; }
+    void OnCancelSignin() override { client_->cancelled_ = true; }
+    void OnSigninWithNewProfile() override { client_->created_ = true; }
+
+   private:
+    ProfileSigninConfirmationViewControllerTest* client_;
+
+    DISALLOW_COPY_AND_ASSIGN(TestSigninDelegate);
+  };
+
   DISALLOW_COPY_AND_ASSIGN(ProfileSigninConfirmationViewControllerTest);
 };
 
diff --git a/chrome/browser/ui/cocoa/tab_dialogs_cocoa.h b/chrome/browser/ui/cocoa/tab_dialogs_cocoa.h
index 1eb2bd5..4d81b16 100644
--- a/chrome/browser/ui/cocoa/tab_dialogs_cocoa.h
+++ b/chrome/browser/ui/cocoa/tab_dialogs_cocoa.h
@@ -24,7 +24,7 @@
       Browser* browser,
       Profile* profile,
       const std::string& username,
-      ui::ProfileSigninConfirmationDelegate* delegate) override;
+      std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate) override;
   void ShowManagePasswordsBubble(bool user_action) override;
   void HideManagePasswordsBubble() override;
   base::WeakPtr<ValidationMessageBubble> ShowValidationMessage(
diff --git a/chrome/browser/ui/cocoa/tab_dialogs_cocoa.mm b/chrome/browser/ui/cocoa/tab_dialogs_cocoa.mm
index bc20ecf..a66b2ee 100644
--- a/chrome/browser/ui/cocoa/tab_dialogs_cocoa.mm
+++ b/chrome/browser/ui/cocoa/tab_dialogs_cocoa.mm
@@ -11,6 +11,7 @@
 #import "chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_dialog_cocoa.h"
 #include "chrome/browser/ui/cocoa/tab_dialogs_views_mac.h"
 #import "chrome/browser/ui/cocoa/validation_message_bubble_cocoa.h"
+#include "chrome/browser/ui/sync/profile_signin_confirmation_helper.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/base/material_design/material_design_controller.h"
 
@@ -64,9 +65,9 @@
     Browser* browser,
     Profile* profile,
     const std::string& username,
-    ui::ProfileSigninConfirmationDelegate* delegate) {
-  ProfileSigninConfirmationDialogCocoa::Show(
-      browser, web_contents_, profile, username, delegate);
+    std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate) {
+  ProfileSigninConfirmationDialogCocoa::Show(browser, web_contents_, profile,
+                                             username, std::move(delegate));
 }
 
 void TabDialogsCocoa::ShowManagePasswordsBubble(bool user_action) {
diff --git a/chrome/browser/ui/cocoa/tab_dialogs_views_mac.h b/chrome/browser/ui/cocoa/tab_dialogs_views_mac.h
index 622777c..980201b 100644
--- a/chrome/browser/ui/cocoa/tab_dialogs_views_mac.h
+++ b/chrome/browser/ui/cocoa/tab_dialogs_views_mac.h
@@ -15,6 +15,11 @@
 
   // TabDialogs:
   void ShowCollectedCookies() override;
+  void ShowProfileSigninConfirmation(
+      Browser* browser,
+      Profile* profile,
+      const std::string& username,
+      std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TabDialogsViewsMac);
diff --git a/chrome/browser/ui/cocoa/tab_dialogs_views_mac.mm b/chrome/browser/ui/cocoa/tab_dialogs_views_mac.mm
index 6187e48..d71c1f2 100644
--- a/chrome/browser/ui/cocoa/tab_dialogs_views_mac.mm
+++ b/chrome/browser/ui/cocoa/tab_dialogs_views_mac.mm
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/cocoa/tab_dialogs_views_mac.h"
 
 #include "chrome/browser/ui/views/collected_cookies_views.h"
+#include "chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h"
 
 TabDialogsViewsMac::TabDialogsViewsMac(content::WebContents* contents)
     : TabDialogsCocoa(contents) {}
@@ -15,3 +16,12 @@
   // Deletes itself on close.
   new CollectedCookiesViews(web_contents());
 }
+
+void TabDialogsViewsMac::ShowProfileSigninConfirmation(
+    Browser* browser,
+    Profile* profile,
+    const std::string& username,
+    std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate) {
+  ProfileSigninConfirmationDialogViews::ShowDialog(browser, profile, username,
+                                                   std::move(delegate));
+}
diff --git a/chrome/browser/ui/collected_cookies_browsertest.cc b/chrome/browser/ui/collected_cookies_browsertest.cc
index 0316265..e946733 100644
--- a/chrome/browser/ui/collected_cookies_browsertest.cc
+++ b/chrome/browser/ui/collected_cookies_browsertest.cc
@@ -22,7 +22,7 @@
  public:
   CollectedCookiesTest() {}
 
-  // TestDialogInterface:
+  // DialogBrowserTest:
   void ShowDialog(const std::string& name) override {
     ASSERT_TRUE(embedded_test_server()->Start());
 
diff --git a/chrome/browser/ui/libgtkui/gtk_util.cc b/chrome/browser/ui/libgtkui/gtk_util.cc
index 56f565b9..9ecf876 100644
--- a/chrome/browser/ui/libgtkui/gtk_util.cc
+++ b/chrome/browser/ui/libgtkui/gtk_util.cc
@@ -372,8 +372,11 @@
       "border-radius: 0px;"
       "border-width: 0px;"
       "border-image-width: 0px;"
+      "box-shadow: none;"
       "padding: 0px;"
       "margin: 0px;"
+      "outline: none;"
+      "outline-width: 0px;"
       "}");
   ApplyCssToContext(context, provider);
 }
@@ -384,8 +387,11 @@
       "border-style: solid;"
       "border-radius: 0px;"
       "border-width: 1px;"
+      "box-shadow: none;"
       "padding: 0px;"
       "margin: 0px;"
+      "outline: none;"
+      "outline-width: 0px;"
       "}");
   ApplyCssToContext(context, provider);
 }
diff --git a/chrome/browser/ui/sync/one_click_signin_sync_starter.cc b/chrome/browser/ui/sync/one_click_signin_sync_starter.cc
index 75b3376..672c4c3 100644
--- a/chrome/browser/ui/sync/one_click_signin_sync_starter.cc
+++ b/chrome/browser/ui/sync/one_click_signin_sync_starter.cc
@@ -242,11 +242,11 @@
 
   content::RecordAction(
       base::UserMetricsAction("Signin_Show_EnterpriseAccountPrompt"));
-  TabDialogs::FromWebContents(web_contents)->ShowProfileSigninConfirmation(
-      browser_,
-      profile_,
-      signin->GetUsernameForAuthInProgress(),
-      new SigninDialogDelegate(weak_pointer_factory_.GetWeakPtr()));
+  TabDialogs::FromWebContents(web_contents)
+      ->ShowProfileSigninConfirmation(browser_, profile_,
+                                      signin->GetUsernameForAuthInProgress(),
+                                      base::MakeUnique<SigninDialogDelegate>(
+                                          weak_pointer_factory_.GetWeakPtr()));
 }
 
 void OneClickSigninSyncStarter::LoadPolicyWithCachedCredentials() {
diff --git a/chrome/browser/ui/tab_dialogs.h b/chrome/browser/ui/tab_dialogs.h
index ca3fec18b..41ac3c1 100644
--- a/chrome/browser/ui/tab_dialogs.h
+++ b/chrome/browser/ui/tab_dialogs.h
@@ -59,7 +59,7 @@
       Browser* browser,
       Profile* profile,
       const std::string& username,
-      ui::ProfileSigninConfirmationDelegate* delegate) = 0;
+      std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate) = 0;
 
   // Shows or hides the ManagePasswords bubble.
   // Pass true for |user_action| if this is a user initiated action.
diff --git a/chrome/browser/ui/test/test_browser_dialog.cc b/chrome/browser/ui/test/test_browser_dialog.cc
index 5faa2fa..18099e7 100644
--- a/chrome/browser/ui/test/test_browser_dialog.cc
+++ b/chrome/browser/ui/test/test_browser_dialog.cc
@@ -88,11 +88,11 @@
 
   gfx::NativeView parent = platform_util::GetViewForWindow(DialogParent());
   views::Widget::Widgets widgets_before;
-  views::Widget::GetAllChildWidgets(parent, &widgets_before);
+  views::Widget::GetAllOwnedWidgets(parent, &widgets_before);
 
   ShowDialog(NameFromTestCase());
   views::Widget::Widgets widgets_after;
-  views::Widget::GetAllChildWidgets(parent, &widgets_after);
+  views::Widget::GetAllOwnedWidgets(parent, &widgets_after);
 
   auto added = base::STLSetDifference<std::vector<views::Widget*>>(
       widgets_after, widgets_before);
diff --git a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
index 1ee37be..774b6095 100644
--- a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
+++ b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/views/profiles/profile_chooser_view.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/constrained_window/constrained_window_views.h"
@@ -22,6 +21,7 @@
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/ui_features.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gfx/range/range.h"
@@ -36,13 +36,17 @@
 #include "ui/views/widget/widget.h"
 #include "ui/views/window/dialog_client_view.h"
 
+#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
+#include "chrome/browser/ui/views/profiles/profile_chooser_view.h"
+#endif
+
 ProfileSigninConfirmationDialogViews::ProfileSigninConfirmationDialogViews(
     Browser* browser,
     const std::string& username,
-    ui::ProfileSigninConfirmationDelegate* delegate)
+    std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate)
     : browser_(browser),
       username_(username),
-      delegate_(delegate),
+      delegate_(std::move(delegate)),
       prompt_for_new_profile_(true) {}
 
 ProfileSigninConfirmationDialogViews::~ProfileSigninConfirmationDialogViews() {}
@@ -52,7 +56,8 @@
     Browser* browser,
     Profile* profile,
     const std::string& username,
-    ui::ProfileSigninConfirmationDelegate* delegate) {
+    std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate) {
+#if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
   // Hides the new avatar bubble if it is currently shown. The new avatar bubble
   // should be automatically closed when it loses focus. However on windows the
   // profile signin confirmation dialog is not modal yet thus it does not take
@@ -61,10 +66,11 @@
   // TODO(guohui): removes the workaround once the profile confirmation dialog
   // is fixed.
   ProfileChooserView::Hide();
+#endif
 
   ProfileSigninConfirmationDialogViews* dialog =
-      new ProfileSigninConfirmationDialogViews(
-          browser, username, delegate);
+      new ProfileSigninConfirmationDialogViews(browser, username,
+                                               std::move(delegate));
   ui::CheckShouldPromptForNewProfile(
       profile,
       // This callback is guaranteed to be invoked, and once it is, the dialog
@@ -117,7 +123,7 @@
       delegate_->OnSigninWithNewProfile();
     else
       delegate_->OnContinueSignin();
-    delegate_ = NULL;
+    delegate_ = nullptr;
   }
   return true;
 }
@@ -125,7 +131,7 @@
 bool ProfileSigninConfirmationDialogViews::Cancel() {
   if (delegate_) {
     delegate_->OnCancelSignin();
-    delegate_ = NULL;
+    delegate_ = nullptr;
   }
   return true;
 }
@@ -246,7 +252,7 @@
   DCHECK(prompt_for_new_profile_);
   if (delegate_) {
     delegate_->OnContinueSignin();
-    delegate_ = NULL;
+    delegate_ = nullptr;
   }
   GetWidget()->Close();
 }
diff --git a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h
index ffa5f579..a466abd 100644
--- a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h
+++ b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_SYNC_PROFILE_SIGNIN_CONFIRMATION_DIALOG_VIEWS_H_
 #define CHROME_BROWSER_UI_VIEWS_SYNC_PROFILE_SIGNIN_CONFIRMATION_DIALOG_VIEWS_H_
 
+#include <memory>
+
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "chrome/browser/ui/sync/profile_signin_confirmation_helper.h"
@@ -23,16 +25,17 @@
                                              public views::ButtonListener {
  public:
   // Create and show the dialog, which owns itself.
-  static void ShowDialog(Browser* browser,
-                         Profile* profile,
-                         const std::string& username,
-                         ui::ProfileSigninConfirmationDelegate* delegate);
+  static void ShowDialog(
+      Browser* browser,
+      Profile* profile,
+      const std::string& username,
+      std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate);
 
  private:
   ProfileSigninConfirmationDialogViews(
       Browser* browser,
       const std::string& username,
-      ui::ProfileSigninConfirmationDelegate* delegate);
+      std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate);
   ~ProfileSigninConfirmationDialogViews() override;
 
   // views::DialogDelegateView:
@@ -69,7 +72,7 @@
   std::string username_;
 
   // Dialog button handler.
-  ui::ProfileSigninConfirmationDelegate* delegate_;
+  std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate_;
 
   // Whether the user should be prompted to create a new profile.
   bool prompt_for_new_profile_;
diff --git a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views_browsertest.cc b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views_browsertest.cc
new file mode 100644
index 0000000..0837f0e
--- /dev/null
+++ b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views_browsertest.cc
@@ -0,0 +1,73 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.h"
+
+#include "base/command_line.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/bookmarks/bookmark_model_factory.h"
+#include "chrome/browser/ui/sync/profile_signin_confirmation_helper.h"
+#include "chrome/browser/ui/tab_dialogs.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/test/test_browser_dialog.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "ui/base/ui_base_switches.h"
+
+namespace {
+
+// Test delegate passed to the confirmation dialog to report back the result.
+class TestSigninDialogDelegate : public ui::ProfileSigninConfirmationDelegate {
+ public:
+  TestSigninDialogDelegate() {}
+  virtual ~TestSigninDialogDelegate() {}
+
+  void OnCancelSignin() override {}
+  void OnContinueSignin() override {}
+  void OnSigninWithNewProfile() override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestSigninDialogDelegate);
+};
+
+}  // namespace
+
+class ProfileSigninConfirmationDialogTest : public DialogBrowserTest {
+ public:
+  ProfileSigninConfirmationDialogTest() {}
+
+  // content::BrowserTestBase:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(switches::kExtendMdToSecondaryUi);
+  }
+
+  // DialogBrowserTest:
+  void ShowDialog(const std::string& name) override {
+    Profile* profile = browser()->profile();
+
+    // Add a bookmark to ensure CheckShouldPromptForNewProfile() returns true.
+    bookmarks::BookmarkModel* bookmarks =
+        BookmarkModelFactory::GetForBrowserContext(profile);
+    bookmarks->AddURL(bookmarks->bookmark_bar_node(), 0,
+                      base::ASCIIToUTF16("title"),
+                      GURL("http://www.example.com"));
+
+    content::WebContents* web_contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    TabDialogs::FromWebContents(web_contents)
+        ->ShowProfileSigninConfirmation(
+            browser(), profile, "username@example.com",
+            base::MakeUnique<TestSigninDialogDelegate>());
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ProfileSigninConfirmationDialogTest);
+};
+
+// Test that calls ShowDialog("default"). Interactive when run via
+// browser_tests --gtest_filter=BrowserDialogTest.Invoke --interactive
+// --dialog=ProfileSigninConfirmationDialogTest.InvokeDialog_default
+IN_PROC_BROWSER_TEST_F(ProfileSigninConfirmationDialogTest,
+                       InvokeDialog_default) {
+  RunDialog();
+}
diff --git a/chrome/browser/ui/views/tab_dialogs_views.cc b/chrome/browser/ui/views/tab_dialogs_views.cc
index 46177c1..fa36944f 100644
--- a/chrome/browser/ui/views/tab_dialogs_views.cc
+++ b/chrome/browser/ui/views/tab_dialogs_views.cc
@@ -53,10 +53,10 @@
     Browser* browser,
     Profile* profile,
     const std::string& username,
-    ui::ProfileSigninConfirmationDelegate* delegate) {
+    std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate) {
 #if !defined(OS_CHROMEOS)
-  ProfileSigninConfirmationDialogViews::ShowDialog(
-      browser, profile, username, delegate);
+  ProfileSigninConfirmationDialogViews::ShowDialog(browser, profile, username,
+                                                   std::move(delegate));
 #else
   NOTREACHED();
 #endif
diff --git a/chrome/browser/ui/views/tab_dialogs_views.h b/chrome/browser/ui/views/tab_dialogs_views.h
index 83f4e473..c1bc6fc 100644
--- a/chrome/browser/ui/views/tab_dialogs_views.h
+++ b/chrome/browser/ui/views/tab_dialogs_views.h
@@ -24,7 +24,7 @@
       Browser* browser,
       Profile* profile,
       const std::string& username,
-      ui::ProfileSigninConfirmationDelegate* delegate) override;
+      std::unique_ptr<ui::ProfileSigninConfirmationDelegate> delegate) override;
   void ShowManagePasswordsBubble(bool user_action) override;
   void HideManagePasswordsBubble() override;
   base::WeakPtr<ValidationMessageBubble> ShowValidationMessage(
diff --git a/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc b/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
index 71f4295..6aa065b 100644
--- a/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
@@ -353,7 +353,7 @@
 
   base::ListValue* items = new base::ListValue();
   for (size_t i = 0; i < parsed_app_list->items().size(); ++i) {
-    const google_apis::AppResource* app = parsed_app_list->items()[i];
+    const google_apis::AppResource* app = parsed_app_list->items()[i].get();
     auto app_data = base::MakeUnique<base::DictionaryValue>();
     app_data->SetString("name", app->name());
     app_data->SetString("application_id", app->application_id());
diff --git a/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc
index cf2aa27b..925b0ba 100644
--- a/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc
@@ -12,7 +12,7 @@
 #include "chrome/browser/chromeos/login/helper.h"
 #include "chrome/browser/chromeos/login/oobe_screen.h"
 #include "chrome/browser/chromeos/login/screens/core_oobe_actor.h"
-#include "chrome/browser/chromeos/login/screens/eula_model.h"
+#include "chrome/browser/chromeos/login/screens/eula_screen.h"
 #include "chrome/browser/chromeos/login/ui/login_web_dialog.h"
 #include "chrome/browser/chromeos/login/ui/webui_login_display.h"
 #include "chrome/browser/profiles/profile.h"
@@ -84,14 +84,12 @@
 
 EulaScreenHandler::EulaScreenHandler(CoreOobeActor* core_oobe_actor)
     : BaseScreenHandler(kJsScreenPath),
-      model_(NULL),
-      core_oobe_actor_(core_oobe_actor),
-      show_on_init_(false) {
+      core_oobe_actor_(core_oobe_actor) {
 }
 
 EulaScreenHandler::~EulaScreenHandler() {
-  if (model_)
-    model_->OnViewDestroyed(this);
+  if (screen_)
+    screen_->OnViewDestroyed(this);
 }
 
 void EulaScreenHandler::Show() {
@@ -105,15 +103,15 @@
 void EulaScreenHandler::Hide() {
 }
 
-void EulaScreenHandler::Bind(EulaModel& model) {
-  model_ = &model;
-  BaseScreenHandler::SetBaseScreen(model_);
+void EulaScreenHandler::Bind(EulaScreen* screen) {
+  screen_ = screen;
+  BaseScreenHandler::SetBaseScreen(screen_);
   if (page_is_ready())
     Initialize();
 }
 
 void EulaScreenHandler::Unbind() {
-  model_ = nullptr;
+  screen_ = nullptr;
   BaseScreenHandler::SetBaseScreen(nullptr);
 }
 
@@ -170,14 +168,14 @@
 }
 
 void EulaScreenHandler::Initialize() {
-  if (!page_is_ready() || !model_)
+  if (!page_is_ready() || !screen_)
     return;
 
-  core_oobe_actor_->SetUsageStats(model_->IsUsageStatsEnabled());
+  core_oobe_actor_->SetUsageStats(screen_->IsUsageStatsEnabled());
 
   // This OEM EULA is a file:// URL which we're unable to load in iframe.
   // Instead if it's defined we use chrome://terms/oem that will load same file.
-  if (!model_->GetOemEulaUrl().is_empty())
+  if (!screen_->GetOemEulaUrl().is_empty())
     core_oobe_actor_->SetOemEulaUrl(chrome::kChromeUITermsOemURL);
 
   if (show_on_init_) {
@@ -215,8 +213,8 @@
 }
 
 void EulaScreenHandler::HandleOnInstallationSettingsPopupOpened() {
-  if (model_)
-    model_->InitiatePasswordFetch();
+  if (screen_)
+    screen_->InitiatePasswordFetch();
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.h
index b09cae9..a2031ea 100644
--- a/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.h
@@ -34,7 +34,7 @@
   // EulaView implementation:
   void Show() override;
   void Hide() override;
-  void Bind(EulaModel& model) override;
+  void Bind(EulaScreen* screen) override;
   void Unbind() override;
   void OnPasswordFetched(const std::string& tpm_password) override;
 
@@ -52,14 +52,14 @@
   void HandleOnChromeOSCredits();
   void HandleOnInstallationSettingsPopupOpened();
 
-  EulaModel* model_;
-  CoreOobeActor* core_oobe_actor_;
+  EulaScreen* screen_ = nullptr;
+  CoreOobeActor* core_oobe_actor_ = nullptr;
 
   // Help application used for help dialogs.
   scoped_refptr<HelpAppLauncher> help_app_;
 
   // Keeps whether screen should be shown right after initialization.
-  bool show_on_init_;
+  bool show_on_init_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(EulaScreenHandler);
 };
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 00ea574..f7c7431c 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2119,6 +2119,7 @@
         "../browser/ui/views/frame/browser_non_client_frame_view_browsertest_win.cc",
         "../browser/ui/views/frame/browser_window_property_manager_browsertest_win.cc",
         "../browser/ui/views/select_file_dialog_extension_browsertest.cc",
+        "../browser/ui/views/sync/profile_signin_confirmation_dialog_views_browsertest.cc",
       ]
       deps += [ "//ui/views" ]
       if (!is_chromeos && (!is_mac || mac_views_browser)) {
@@ -2349,6 +2350,7 @@
         "../browser/service_process/service_process_control_browsertest.cc",
 
         # inline login UI is disabled on chromeos
+        "../browser/ui/views/sync/profile_signin_confirmation_dialog_views_browsertest.cc",
         "../browser/ui/webui/signin/inline_login_ui_browsertest.cc",
 
         # chromeos does not use the desktop user manager
diff --git a/chrome/test/data/webui/settings/site_list_tests.js b/chrome/test/data/webui/settings/site_list_tests.js
index a8e60e97..95bb6b6 100644
--- a/chrome/test/data/webui/settings/site_list_tests.js
+++ b/chrome/test/data/webui/settings/site_list_tests.js
@@ -462,6 +462,28 @@
             });
       });
 
+      test('action menu closes when list changes', function() {
+        setUpCategory(settings.ContentSettingsTypes.GEOLOCATION,
+            settings.PermissionValues.ALLOW, prefs);
+        var actionMenu = testElement.$$('dialog[is=cr-action-menu]');
+        return browserProxy.whenCalled('getExceptionList').then(
+            function(contentType) {
+              Polymer.dom.flush();  // Populates action menu.
+              openActionMenu(0);
+              assertTrue(actionMenu.open);
+
+              browserProxy.resetResolver('getExceptionList');
+              // Simulate a change in the underlying model.
+              cr.webUIListenerCallback(
+                  'contentSettingSitePermissionChanged',
+                  settings.ContentSettingsTypes.GEOLOCATION);
+              return browserProxy.whenCalled('getExceptionList');
+            }).then(function() {
+              // Check that the action menu was closed.
+              assertFalse(actionMenu.open);
+            });
+      });
+
       test('exceptions are not reordered in non-ALL_SITES', function() {
         setUpCategory(settings.ContentSettingsTypes.GEOLOCATION,
             settings.PermissionValues.BLOCK, prefsMixedProvider);
diff --git a/components/domain_reliability/beacon.cc b/components/domain_reliability/beacon.cc
index 8c51e8fb..8a8a549f 100644
--- a/components/domain_reliability/beacon.cc
+++ b/components/domain_reliability/beacon.cc
@@ -24,7 +24,7 @@
     base::TimeTicks upload_time,
     base::TimeTicks last_network_change_time,
     const GURL& collector_url,
-    const ScopedVector<std::string>& path_prefixes) const {
+    const std::vector<std::unique_ptr<std::string>>& path_prefixes) const {
   std::unique_ptr<DictionaryValue> beacon_value(new DictionaryValue());
   DCHECK(url.is_valid());
   GURL sanitized_url = SanitizeURLForReport(url, collector_url, path_prefixes);
diff --git a/components/domain_reliability/beacon.h b/components/domain_reliability/beacon.h
index 2162b58f..8d352b9 100644
--- a/components/domain_reliability/beacon.h
+++ b/components/domain_reliability/beacon.h
@@ -8,7 +8,6 @@
 #include <memory>
 #include <string>
 
-#include "base/memory/scoped_vector.h"
 #include "base/time/time.h"
 #include "components/domain_reliability/domain_reliability_export.h"
 #include "net/base/net_error_details.h"
@@ -40,7 +39,7 @@
       base::TimeTicks upload_time,
       base::TimeTicks last_network_change_time,
       const GURL& collector_url,
-      const ScopedVector<std::string>& path_prefixes) const;
+      const std::vector<std::unique_ptr<std::string>>& path_prefixes) const;
 
   // The URL that the beacon is reporting on, if included.
   GURL url;
diff --git a/components/domain_reliability/config.cc b/components/domain_reliability/config.cc
index 765e0fa4..21c93cc1 100644
--- a/components/domain_reliability/config.cc
+++ b/components/domain_reliability/config.cc
@@ -13,7 +13,6 @@
 #include <utility>
 
 #include "base/json/json_reader.h"
-#include "base/json/json_value_converter.h"
 #include "base/profiler/scoped_tracker.h"
 #include "base/strings/pattern.h"
 #include "base/strings/string_util.h"
@@ -70,7 +69,7 @@
     return false;
   }
 
-  for (const auto* url : collectors) {
+  for (const auto& url : collectors) {
     if (!url->is_valid())
       return false;
   }
diff --git a/components/domain_reliability/config.h b/components/domain_reliability/config.h
index 533d891..9f4ebb0 100644
--- a/components/domain_reliability/config.h
+++ b/components/domain_reliability/config.h
@@ -42,11 +42,11 @@
 
   GURL origin;
   bool include_subdomains;
-  ScopedVector<GURL> collectors;
+  std::vector<std::unique_ptr<GURL>> collectors;
 
   double success_sample_rate;
   double failure_sample_rate;
-  ScopedVector<std::string> path_prefixes;
+  std::vector<std::unique_ptr<std::string>> path_prefixes;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(DomainReliabilityConfig);
diff --git a/components/domain_reliability/config_unittest.cc b/components/domain_reliability/config_unittest.cc
index d8c7f6d..be701b0 100644
--- a/components/domain_reliability/config_unittest.cc
+++ b/components/domain_reliability/config_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <string>
 
+#include "base/memory/ptr_util.h"
 #include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -17,7 +18,8 @@
   DomainReliabilityConfig* config = new DomainReliabilityConfig();
   config->origin = GURL("https://example/");
   config->include_subdomains = false;
-  config->collectors.push_back(new GURL("https://example/upload"));
+  config->collectors.push_back(
+      base::MakeUnique<GURL>("https://example/upload"));
   config->failure_sample_rate = 1.0;
   config->success_sample_rate = 0.0;
   EXPECT_TRUE(config->IsValid());
@@ -26,8 +28,8 @@
 
 std::unique_ptr<DomainReliabilityConfig> MakeSampleConfig() {
   std::unique_ptr<DomainReliabilityConfig> config(MakeBaseConfig());
-  config->path_prefixes.push_back(new std::string("/css/"));
-  config->path_prefixes.push_back(new std::string("/js/"));
+  config->path_prefixes.push_back(base::MakeUnique<std::string>("/css/"));
+  config->path_prefixes.push_back(base::MakeUnique<std::string>("/js/"));
   EXPECT_TRUE(config->IsValid());
   return config;
 }
@@ -49,8 +51,7 @@
   EXPECT_FALSE(config->IsValid());
 
   config = MakeSampleConfig();
-  delete config->collectors[0];
-  config->collectors[0] = new GURL();
+  config->collectors[0] = base::MakeUnique<GURL>();
   EXPECT_FALSE(config->IsValid());
 
   config = MakeSampleConfig();
diff --git a/components/domain_reliability/google_configs.cc b/components/domain_reliability/google_configs.cc
index 00f55d22..d61c9c2c 100644
--- a/components/domain_reliability/google_configs.cc
+++ b/components/domain_reliability/google_configs.cc
@@ -551,10 +551,11 @@
     GURL::Replacements replacements;
     replacements.SetPathStr(kGoogleOriginSpecificCollectorPathString);
     config->collectors.push_back(
-        new GURL(config->origin.ReplaceComponents(replacements)));
+        base::MakeUnique<GURL>(config->origin.ReplaceComponents(replacements)));
   }
   for (size_t i = 0; i < arraysize(kGoogleStandardCollectors); i++)
-    config->collectors.push_back(new GURL(kGoogleStandardCollectors[i]));
+    config->collectors.push_back(
+        base::MakeUnique<GURL>(kGoogleStandardCollectors[i]));
   config->success_sample_rate = 0.05;
   config->failure_sample_rate = 1.00;
   config->path_prefixes.clear();
diff --git a/components/domain_reliability/header.cc b/components/domain_reliability/header.cc
index 1e9cb6a..05a6379 100644
--- a/components/domain_reliability/header.cc
+++ b/components/domain_reliability/header.cc
@@ -7,7 +7,6 @@
 #include <stdint.h>
 
 #include <string>
-
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_tokenizer.h"
@@ -158,7 +157,7 @@
 }
 
 bool ParseReportUri(const std::vector<base::StringPiece> in,
-                    ScopedVector<GURL>* out) {
+                    std::vector<std::unique_ptr<GURL>>* out) {
   if (in.size() < 1u)
     return false;
 
@@ -170,7 +169,7 @@
     GURL url(unquoted);
     if (!url.is_valid() || !content::IsOriginSecure(url))
       return false;
-    out->push_back(new GURL(url));
+    out->push_back(base::MakeUnique<GURL>(url));
   }
 
   return true;
@@ -201,7 +200,7 @@
 // static
 std::unique_ptr<DomainReliabilityHeader> DomainReliabilityHeader::Parse(
     base::StringPiece value) {
-  ScopedVector<GURL> report_uri;
+  std::vector<std::unique_ptr<GURL>> report_uri;
   base::TimeDelta max_age;
   bool include_subdomains = false;
 
@@ -284,7 +283,7 @@
     DCHECK_EQ(0, max_age_s);
   } else {
     string += "report-uri=";
-    for (const auto* uri : config_->collectors)
+    for (const auto& uri : config_->collectors)
       string += uri->spec() + " ";
     // Remove trailing space.
     string.erase(string.length() - 1, 1);
diff --git a/components/domain_reliability/header_unittest.cc b/components/domain_reliability/header_unittest.cc
index 0ba21c3..cbb0b1f 100644
--- a/components/domain_reliability/header_unittest.cc
+++ b/components/domain_reliability/header_unittest.cc
@@ -28,8 +28,9 @@
   std::unique_ptr<DomainReliabilityHeader> parsed_;
 };
 
-bool CheckReportUris(const char* pipe_separated_expected_report_uris,
-                     const ScopedVector<GURL>& actual_report_uris) {
+bool CheckReportUris(
+    const char* pipe_separated_expected_report_uris,
+    const std::vector<std::unique_ptr<GURL>>& actual_report_uris) {
   if (!pipe_separated_expected_report_uris)
     return actual_report_uris.empty();
 
diff --git a/components/domain_reliability/test_util.cc b/components/domain_reliability/test_util.cc
index bb060fa0..5bfbb2e 100644
--- a/components/domain_reliability/test_util.cc
+++ b/components/domain_reliability/test_util.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/memory/ptr_util.h"
 #include "components/domain_reliability/scheduler.h"
 #include "net/url_request/url_request_status.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -169,7 +170,8 @@
     const GURL& origin) {
   DomainReliabilityConfig* config = new DomainReliabilityConfig();
   config->origin = origin;
-  config->collectors.push_back(new GURL("https://exampleuploader/upload"));
+  config->collectors.push_back(
+      base::MakeUnique<GURL>("https://exampleuploader/upload"));
   config->failure_sample_rate = 1.0;
   config->success_sample_rate = 0.0;
 
diff --git a/components/domain_reliability/util.cc b/components/domain_reliability/util.cc
index f4af66913..20bf756 100644
--- a/components/domain_reliability/util.cc
+++ b/components/domain_reliability/util.cc
@@ -170,22 +170,23 @@
   return;
 }
 
-// N.B. This uses a ScopedVector because that's what JSONValueConverter uses
-// for repeated fields of any type, and Config uses JSONValueConverter to parse
-// JSON configs.
-GURL SanitizeURLForReport(const GURL& beacon_url,
-                          const GURL& collector_url,
-                          const ScopedVector<std::string>& path_prefixes) {
+// N.B. This uses a std::vector<std::unique_ptr<>> because that's what
+// JSONValueConverter uses for repeated fields of any type, and Config uses
+// JSONValueConverter to parse JSON configs.
+GURL SanitizeURLForReport(
+    const GURL& beacon_url,
+    const GURL& collector_url,
+    const std::vector<std::unique_ptr<std::string>>& path_prefixes) {
   if (CanReportFullBeaconURLToCollector(beacon_url, collector_url))
     return beacon_url.GetAsReferrer();
 
   std::string path = beacon_url.path();
   const std::string empty_path;
   const std::string* longest_path_prefix = &empty_path;
-  for (const std::string* path_prefix : path_prefixes) {
+  for (const auto& path_prefix : path_prefixes) {
     if (path.substr(0, path_prefix->length()) == *path_prefix &&
         path_prefix->length() > longest_path_prefix->length()) {
-      longest_path_prefix = path_prefix;
+      longest_path_prefix = path_prefix.get();
     }
   }
 
diff --git a/components/domain_reliability/util.h b/components/domain_reliability/util.h
index f8bb2040..3ef2e75 100644
--- a/components/domain_reliability/util.h
+++ b/components/domain_reliability/util.h
@@ -11,7 +11,6 @@
 #include "base/callback_forward.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/time/clock.h"
 #include "base/time/tick_clock.h"
 #include "base/time/time.h"
@@ -51,9 +50,10 @@
     base::TimeDelta retry_after,
     DomainReliabilityUploader::UploadResult* result);
 
-GURL SanitizeURLForReport(const GURL& beacon_url,
-                          const GURL& collector_url,
-                          const ScopedVector<std::string>& path_prefixes);
+GURL SanitizeURLForReport(
+    const GURL& beacon_url,
+    const GURL& collector_url,
+    const std::vector<std::unique_ptr<std::string>>& path_prefixes);
 
 // Mockable wrapper around TimeTicks::Now and Timer. Mock version is in
 // test_util.h.
diff --git a/components/drive/chromeos/change_list_processor.cc b/components/drive/chromeos/change_list_processor.cc
index ab2f3bd..833b64f 100644
--- a/components/drive/chromeos/change_list_processor.cc
+++ b/components/drive/chromeos/change_list_processor.cc
@@ -5,7 +5,10 @@
 #include "components/drive/chromeos/change_list_processor.h"
 
 #include <stddef.h>
+
+#include <memory>
 #include <utility>
+#include <vector>
 
 #include "base/metrics/histogram.h"
 #include "base/strings/string_number_conversions.h"
@@ -71,7 +74,8 @@
 ChangeList::ChangeList(const google_apis::ChangeList& change_list)
     : next_url_(change_list.next_link()),
       largest_changestamp_(change_list.largest_change_id()) {
-  const ScopedVector<google_apis::ChangeResource>& items = change_list.items();
+  const std::vector<std::unique_ptr<google_apis::ChangeResource>>& items =
+      change_list.items();
   entries_.resize(items.size());
   parent_resource_ids_.resize(items.size());
   size_t entries_index = 0;
@@ -90,7 +94,8 @@
 ChangeList::ChangeList(const google_apis::FileList& file_list)
     : next_url_(file_list.next_link()),
       largest_changestamp_(0) {
-  const ScopedVector<google_apis::FileResource>& items = file_list.items();
+  const std::vector<std::unique_ptr<google_apis::FileResource>>& items =
+      file_list.items();
   entries_.resize(items.size());
   parent_resource_ids_.resize(items.size());
   size_t entries_index = 0;
diff --git a/components/drive/chromeos/fake_file_system.cc b/components/drive/chromeos/fake_file_system.cc
index a7ef97f..69b7a28 100644
--- a/components/drive/chromeos/fake_file_system.cc
+++ b/components/drive/chromeos/fake_file_system.cc
@@ -5,7 +5,10 @@
 #include "components/drive/chromeos/fake_file_system.h"
 
 #include <stddef.h>
+
+#include <memory>
 #include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -404,7 +407,8 @@
   }
 
   DCHECK(file_list);
-  const ScopedVector<google_apis::FileResource>& entries = file_list->items();
+  const std::vector<std::unique_ptr<google_apis::FileResource>>& entries =
+      file_list->items();
   for (size_t i = 0; i < entries.size(); ++i) {
     std::unique_ptr<ResourceEntry> entry(new ResourceEntry);
     std::string parent_resource_id;
diff --git a/components/drive/chromeos/file_system/search_operation.cc b/components/drive/chromeos/file_system/search_operation.cc
index a2608c3..e2005caf 100644
--- a/components/drive/chromeos/file_system/search_operation.cc
+++ b/components/drive/chromeos/file_system/search_operation.cc
@@ -5,6 +5,8 @@
 #include "components/drive/chromeos/file_system/search_operation.h"
 
 #include <stddef.h>
+
+#include <memory>
 #include <string>
 #include <utility>
 #include <vector>
@@ -37,7 +39,8 @@
   DCHECK(resource_metadata);
   DCHECK(result);
 
-  const ScopedVector<google_apis::FileResource>& entries = file_list->items();
+  const std::vector<std::unique_ptr<google_apis::FileResource>>& entries =
+      file_list->items();
   result->reserve(entries.size());
   for (size_t i = 0; i < entries.size(); ++i) {
     std::string local_id;
diff --git a/components/drive/drive_app_registry.cc b/components/drive/drive_app_registry.cc
index f6cbc502..37c976d 100644
--- a/components/drive/drive_app_registry.cc
+++ b/components/drive/drive_app_registry.cc
@@ -7,8 +7,10 @@
 #include <stddef.h>
 
 #include <algorithm>
+#include <memory>
 #include <set>
 #include <utility>
+#include <vector>
 
 #include "base/callback.h"
 #include "base/files/file_path.h"
@@ -20,9 +22,10 @@
 namespace {
 
 // Add {selector -> app_id} mapping to |map|.
-void AddAppSelectorList(const ScopedVector<std::string>& selectors,
-                        const std::string& app_id,
-                        std::multimap<std::string, std::string>* map) {
+void AddAppSelectorList(
+    const std::vector<std::unique_ptr<std::string>>& selectors,
+    const std::string& app_id,
+    std::multimap<std::string, std::string>* map) {
   for (size_t i = 0; i < selectors.size(); ++i)
     map->insert(std::make_pair(*selectors[i], app_id));
 }
diff --git a/components/drive/service/fake_drive_service.cc b/components/drive/service/fake_drive_service.cc
index 26b23f3..c7ae70d 100644
--- a/components/drive/service/fake_drive_service.cc
+++ b/components/drive/service/fake_drive_service.cc
@@ -6,8 +6,10 @@
 
 #include <stddef.h>
 
+#include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/files/file_util.h"
 #include "base/json/json_string_value_serializer.h"
@@ -132,7 +134,8 @@
   for (size_t i = 0; i < change_list->items().size(); ++i) {
     const ChangeResource& entry = *change_list->items()[i];
     if (entry.file())
-      file_list->mutable_items()->push_back(new FileResource(*entry.file()));
+      file_list->mutable_items()->push_back(
+          base::MakeUnique<FileResource>(*entry.file()));
   }
   callback.Run(error, std::move(file_list));
 }
@@ -1662,7 +1665,7 @@
 
   // Filter out entries per parameters like |directory_resource_id| and
   // |search_query|.
-  ScopedVector<ChangeResource> entries;
+  std::vector<std::unique_ptr<ChangeResource>> entries;
   int num_entries_matched = 0;
   for (auto it = entries_.begin(); it != entries_.end(); ++it) {
     const ChangeResource& entry = it->second->change_resource;
@@ -1720,7 +1723,7 @@
         entry_copied->set_file(base::MakeUnique<FileResource>(*entry.file()));
       }
       entry_copied->set_modification_date(entry.modification_date());
-      entries.push_back(entry_copied.release());
+      entries.push_back(std::move(entry_copied));
     }
   }
 
diff --git a/components/error_page/renderer/net_error_helper_core.cc b/components/error_page/renderer/net_error_helper_core.cc
index 3451b7e..f040f97f 100644
--- a/components/error_page/renderer/net_error_helper_core.cc
+++ b/components/error_page/renderer/net_error_helper_core.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 
+#include <memory>
 #include <set>
 #include <string>
 #include <utility>
@@ -20,7 +21,6 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/sparse_histogram.h"
 #include "base/strings/string16.h"
@@ -103,7 +103,7 @@
 struct NavigationCorrectionResponse {
   std::string event_id;
   std::string fingerprint;
-  ScopedVector<NavigationCorrection> corrections;
+  std::vector<std::unique_ptr<NavigationCorrection>> corrections;
 
   static void RegisterJSONConverter(
       base::JSONValueConverter<NavigationCorrectionResponse>* converter) {
@@ -302,9 +302,8 @@
   std::unique_ptr<ErrorPageParams> params(new ErrorPageParams());
   params->override_suggestions.reset(new base::ListValue());
   std::unique_ptr<base::ListValue> parsed_corrections(new base::ListValue());
-  for (ScopedVector<NavigationCorrection>::const_iterator it =
-           response.corrections.begin();
-       it != response.corrections.end(); ++it) {
+  for (auto it = response.corrections.begin(); it != response.corrections.end();
+       ++it) {
     // Doesn't seem like a good idea to show these.
     if ((*it)->is_porn || (*it)->is_soft_porn)
       continue;
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index 65e8540..b1da402 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -180,7 +180,11 @@
   SecurityExploitBrowserTest() {}
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    ASSERT_TRUE(embedded_test_server()->Start());
+    // EmbeddedTestServer::InitializeAndListen() initializes its |base_url_|
+    // which is required below. This cannot invoke Start() however as that kicks
+    // off the "EmbeddedTestServer IO Thread" which then races with
+    // initialization in ContentBrowserTest::SetUp(), http://crbug.com/674545.
+    ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
 
     // Add a host resolver rule to map all outgoing requests to the test server.
     // This allows us to use "real" hostnames in URLs, which we can use to
@@ -194,6 +198,10 @@
   }
 
   void SetUpOnMainThread() override {
+    // Complete the manual Start() after ContentBrowserTest's own
+    // initialization, ref. comment on InitializeAndListen() above.
+    embedded_test_server()->StartAcceptingConnections();
+
     BrowserThread::PostTask(
         BrowserThread::IO, FROM_HERE,
         base::Bind(&net::URLRequestSlowDownloadJob::AddUrlHandler));
diff --git a/content/child/site_isolation_stats_gatherer_browsertest.cc b/content/child/site_isolation_stats_gatherer_browsertest.cc
index 852ddab..9150a97a 100644
--- a/content/child/site_isolation_stats_gatherer_browsertest.cc
+++ b/content/child/site_isolation_stats_gatherer_browsertest.cc
@@ -29,7 +29,12 @@
   ~SiteIsolationStatsGathererBrowserTest() override {}
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    ASSERT_TRUE(embedded_test_server()->Start());
+    // EmbeddedTestServer::InitializeAndListen() initializes its |base_url_|
+    // which is required below. This cannot invoke Start() however as that kicks
+    // off the "EmbeddedTestServer IO Thread" which then races with
+    // initialization in ContentBrowserTest::SetUp(), http://crbug.com/674545.
+    ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
+
     // Add a host resolver rule to map all outgoing requests to the test server.
     // This allows us to use "real" hostnames in URLs, which we can use to
     // create arbitrary SiteInstances.
@@ -43,6 +48,12 @@
     command_line->AppendSwitch(switches::kDisableWebSecurity);
   }
 
+  void SetUpOnMainThread() override {
+    // Complete the manual Start() after ContentBrowserTest's own
+    // initialization, ref. comment on InitializeAndListen() above.
+    embedded_test_server()->StartAcceptingConnections();
+  }
+
   void InspectHistograms(const base::HistogramTester& histograms,
                          bool should_be_blocked,
                          const std::string& resource_name) {
diff --git a/content/renderer/media/android/webmediaplayer_android.cc b/content/renderer/media/android/webmediaplayer_android.cc
index d3a458c..8bbb6b3 100644
--- a/content/renderer/media/android/webmediaplayer_android.cc
+++ b/content/renderer/media/android/webmediaplayer_android.cc
@@ -182,8 +182,10 @@
 
   DCHECK(main_thread_checker_.CalledOnValidThread());
 
-  if (delegate_)
+  if (delegate_) {
     delegate_id_ = delegate_->AddObserver(this);
+    delegate_->SetIdle(delegate_id_, true);
+  }
 
   player_id_ = player_manager_->RegisterMediaPlayer(this);
 
@@ -295,8 +297,8 @@
     bool can_video_play_in_background =
         base::CommandLine::ForCurrentProcess()->HasSwitch(
             switches::kDisableMediaSuspend) ||
-        (IsBackgroundVideoCandidate() &&
-            delegate_ && delegate_->IsPlayingBackgroundVideo());
+        (IsBackgroundVideoCandidate() && delegate_ &&
+         delegate_->IsBackgroundVideoPlaybackUnlocked());
     if (!can_video_play_in_background) {
       is_play_pending_ = true;
       return;
@@ -828,8 +830,10 @@
     // If we're paused after we receive metadata for the first time, tell the
     // delegate we can now be safely suspended due to inactivity if a subsequent
     // play event does not occur.
-    if (paused() && delegate_)
-      delegate_->DidPause(delegate_id_, false);
+    if (paused() && delegate_) {
+      delegate_->DidPause(delegate_id_);
+      delegate_->SetIdle(delegate_id_, true);
+    }
   }
 }
 
@@ -1204,19 +1208,28 @@
       // be known at this point -- there are no video only containers, so only
       // send audio if we know for sure its audio.  The browser side player will
       // fill in the correct value later for media sessions.
-      delegate_->DidPlay(delegate_id_, hasVideo(), !hasVideo(), isRemote(),
-                         media::DurationToMediaContentType(duration_));
+      if (isRemote()) {
+        delegate_->PlayerGone(delegate_id_);
+      } else {
+        delegate_->DidPlay(delegate_id_, hasVideo(), !hasVideo(),
+                           media::DurationToMediaContentType(duration_));
+      }
+      delegate_->SetIdle(delegate_id_, false);
     } else {
       // Even if OnPlaybackComplete() has not been called yet, Blink may have
       // already fired the ended event based on current time relative to
       // duration -- so we need to check both possibilities here.
-      delegate_->DidPause(delegate_id_,
-                          playback_completed_ || currentTime() >= duration());
+      if (playback_completed_ || currentTime() >= duration()) {
+        delegate_->PlayerGone(delegate_id_);
+      } else {
+        delegate_->DidPause(delegate_id_);
+      }
+      delegate_->SetIdle(delegate_id_, true);
     }
   }
 }
 
-void WebMediaPlayerAndroid::OnHidden() {
+void WebMediaPlayerAndroid::OnFrameHidden() {
   // Pause audible video preserving its session.
   if (hasVideo() && IsBackgroundVideoCandidate() && !paused()) {
     Pause(false);
@@ -1224,29 +1237,30 @@
     return;
   }
 
-  OnSuspendRequested(false);
+  OnIdleTimeout();
 }
 
-void WebMediaPlayerAndroid::OnShown() {
+void WebMediaPlayerAndroid::OnFrameClosed() {
+  SuspendAndReleaseResources();
+}
+
+void WebMediaPlayerAndroid::OnFrameShown() {
   if (is_play_pending_)
     play();
 }
 
-bool WebMediaPlayerAndroid::OnSuspendRequested(bool must_suspend) {
-  if (!must_suspend &&
-      base::CommandLine::ForCurrentProcess()->HasSwitch(
+void WebMediaPlayerAndroid::OnIdleTimeout() {
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kDisableMediaSuspend)) {
-    return true;
+    return;
   }
 
-  // If we're idle or playing video, pause and release resources; audio only
-  // players are allowed to continue unless indicated otherwise by the call.
-  if (must_suspend || (paused() && playback_completed_) ||
-      (hasVideo() && !IsBackgroundVideoCandidate())) {
+  // If we're playing video or ended, pause and release resources; audio only
+  // players are allowed to continue.
+  if ((hasVideo() && !IsBackgroundVideoCandidate()) ||
+      (paused() && playback_completed_)) {
     SuspendAndReleaseResources();
   }
-
-  return true;
 }
 
 void WebMediaPlayerAndroid::OnPlay() {
@@ -1327,7 +1341,8 @@
   }
 
   return base::FeatureList::IsEnabled(media::kResumeBackgroundVideo) &&
-      hasAudio() && !isRemote() && delegate_ && delegate_->IsHidden();
+         hasAudio() && !isRemote() && delegate_ && delegate_->IsFrameHidden() &&
+         !delegate_->IsFrameClosed();
 }
 
 }  // namespace content
diff --git a/content/renderer/media/android/webmediaplayer_android.h b/content/renderer/media/android/webmediaplayer_android.h
index 3d84ff0..10c095c 100644
--- a/content/renderer/media/android/webmediaplayer_android.h
+++ b/content/renderer/media/android/webmediaplayer_android.h
@@ -212,9 +212,10 @@
   void SuspendAndReleaseResources() override;
 
   // WebMediaPlayerDelegate::Observer implementation.
-  void OnHidden() override;
-  void OnShown() override;
-  bool OnSuspendRequested(bool must_suspend) override;
+  void OnFrameHidden() override;
+  void OnFrameClosed() override;
+  void OnFrameShown() override;
+  void OnIdleTimeout() override;
   void OnPlay() override;
   void OnPause() override;
   void OnVolumeMultiplierUpdate(double multiplier) override;
diff --git a/content/renderer/media/media_recorder_handler.cc b/content/renderer/media/media_recorder_handler.cc
index d505226b..d798c82f 100644
--- a/content/renderer/media/media_recorder_handler.cc
+++ b/content/renderer/media/media_recorder_handler.cc
@@ -274,8 +274,11 @@
   DCHECK(main_render_thread_checker_.CalledOnValidThread());
   if (!webm_muxer_)
     return;
-  webm_muxer_->OnEncodedVideo(params, std::move(encoded_data), timestamp,
-                              is_key_frame);
+  if (!webm_muxer_->OnEncodedVideo(params, std::move(encoded_data), timestamp,
+                                   is_key_frame)) {
+    DLOG(ERROR) << "Error muxing video data";
+    client_->onError("Error muxing video data");
+  }
 }
 
 void MediaRecorderHandler::OnEncodedAudio(
@@ -283,8 +286,13 @@
     std::unique_ptr<std::string> encoded_data,
     base::TimeTicks timestamp) {
   DCHECK(main_render_thread_checker_.CalledOnValidThread());
-  if (webm_muxer_)
-    webm_muxer_->OnEncodedAudio(params, std::move(encoded_data), timestamp);
+  if (!webm_muxer_)
+    return;
+  if (!webm_muxer_->OnEncodedAudio(params, std::move(encoded_data),
+                                   timestamp)) {
+    DLOG(ERROR) << "Error muxing audio data";
+    client_->onError("Error muxing audio data");
+  }
 }
 
 void MediaRecorderHandler::WriteData(base::StringPiece data) {
diff --git a/content/renderer/media/media_recorder_handler_unittest.cc b/content/renderer/media/media_recorder_handler_unittest.cc
index 8f968d69..6d6d6d0 100644
--- a/content/renderer/media/media_recorder_handler_unittest.cc
+++ b/content/renderer/media/media_recorder_handler_unittest.cc
@@ -116,6 +116,10 @@
       registry_.AddAudioTrack(kTestAudioTrackId);
   }
 
+  void ForceOneErrorInWebmMuxer() {
+    media_recorder_handler_->webm_muxer_->ForceOneLibWebmErrorForTesting();
+  }
+
   std::unique_ptr<media::AudioBus> NextAudioBus() {
     std::unique_ptr<media::AudioBus> bus(media::AudioBus::Create(
         kTestAudioChannels,
@@ -339,4 +343,53 @@
   media_recorder_handler_.reset();
 }
 
+// Starts up recording and forces a WebmMuxer's libwebm error.
+TEST_P(MediaRecorderHandlerTest, WebmMuxerErrorWhileEncoding) {
+  // Video-only test: Audio would be very similar.
+  if (GetParam().has_audio)
+    return;
+
+  AddTracks();
+
+  const WebString mime_type(base::UTF8ToUTF16(GetParam().mime_type));
+  const WebString codecs(base::UTF8ToUTF16(GetParam().codecs));
+  EXPECT_TRUE(media_recorder_handler_->initialize(this, registry_.test_stream(),
+                                                  mime_type, codecs, 0, 0));
+  EXPECT_TRUE(media_recorder_handler_->start(0));
+
+  InSequence s;
+  const scoped_refptr<media::VideoFrame> video_frame =
+      media::VideoFrame::CreateBlackFrame(gfx::Size(160, 80));
+
+  {
+    const size_t kEncodedSizeThreshold = 16;
+    base::RunLoop run_loop;
+    base::Closure quit_closure = run_loop.QuitClosure();
+    EXPECT_CALL(*this, writeData(_, _, _, _)).Times(AtLeast(1));
+    EXPECT_CALL(*this, writeData(_, Gt(kEncodedSizeThreshold), _, _))
+        .Times(1)
+        .WillOnce(RunClosure(quit_closure));
+
+    OnVideoFrameForTesting(video_frame);
+    run_loop.Run();
+  }
+
+  ForceOneErrorInWebmMuxer();
+
+  {
+    base::RunLoop run_loop;
+    base::Closure quit_closure = run_loop.QuitClosure();
+    EXPECT_CALL(*this, writeData(_, _, _, _)).Times(0);
+    EXPECT_CALL(*this, onError(_)).Times(1).WillOnce(RunClosure(quit_closure));
+
+    OnVideoFrameForTesting(video_frame);
+    run_loop.Run();
+  }
+
+
+  // Expect a last call on destruction, with size 0 and |lastInSlice| true.
+  EXPECT_CALL(*this, writeData(nullptr, 0, true, _)).Times(1);
+  media_recorder_handler_.reset();
+}
+
 }  // namespace content
diff --git a/content/renderer/media/renderer_webmediaplayer_delegate.cc b/content/renderer/media/renderer_webmediaplayer_delegate.cc
index 5712a347..92d1140e 100644
--- a/content/renderer/media/renderer_webmediaplayer_delegate.cc
+++ b/content/renderer/media/renderer_webmediaplayer_delegate.cc
@@ -32,13 +32,12 @@
 RendererWebMediaPlayerDelegate::RendererWebMediaPlayerDelegate(
     content::RenderFrame* render_frame)
     : RenderFrameObserver(render_frame),
-      idle_cleanup_timer_(true, true),
       default_tick_clock_(new base::DefaultTickClock()),
       tick_clock_(default_tick_clock_.get()) {
   idle_cleanup_interval_ = base::TimeDelta::FromSeconds(5);
   idle_timeout_ = base::TimeDelta::FromSeconds(15);
 
-  // To conserve resources, cleanup idle players more often on low end devices.
+  // Idle players time out more aggressively on low end devices.
   is_low_end_device_ = base::SysInfo::IsLowEndDevice();
 
 #if defined(OS_ANDROID)
@@ -51,87 +50,146 @@
 
 RendererWebMediaPlayerDelegate::~RendererWebMediaPlayerDelegate() {}
 
-int RendererWebMediaPlayerDelegate::AddObserver(Observer* observer) {
-  const int delegate_id = id_map_.Add(observer);
-  // Start players in the idle state to ensure we capture players which are
-  // consuming resources, but which have never played.
-  AddIdleDelegate(delegate_id);
-  return delegate_id;
+bool RendererWebMediaPlayerDelegate::IsFrameHidden() {
+  if (is_frame_hidden_for_testing_)
+    return true;
+
+  return (render_frame() && render_frame()->IsHidden()) || is_frame_closed_;
 }
 
-void RendererWebMediaPlayerDelegate::RemoveObserver(int delegate_id) {
-  DCHECK(id_map_.Lookup(delegate_id));
-  id_map_.Remove(delegate_id);
-  RemoveIdleDelegate(delegate_id);
-  playing_videos_.erase(delegate_id);
+bool RendererWebMediaPlayerDelegate::IsFrameClosed() {
+  return is_frame_closed_;
+}
+
+bool RendererWebMediaPlayerDelegate::IsBackgroundVideoPlaybackUnlocked() {
+  // TODO(sandersd): Include a check for kResumeBackgroundVideo?
+  return background_video_allowed_;
+}
+
+int RendererWebMediaPlayerDelegate::AddObserver(Observer* observer) {
+  return id_map_.Add(observer);
+}
+
+void RendererWebMediaPlayerDelegate::RemoveObserver(int player_id) {
+  DCHECK(id_map_.Lookup(player_id));
+  id_map_.Remove(player_id);
+  idle_player_map_.erase(player_id);
+  stale_players_.erase(player_id);
+  playing_videos_.erase(player_id);
+
+  Send(
+      new MediaPlayerDelegateHostMsg_OnMediaDestroyed(routing_id(), player_id));
+
+  ScheduleUpdateTask();
 }
 
 void RendererWebMediaPlayerDelegate::DidPlay(
-    int delegate_id,
+    int player_id,
     bool has_video,
     bool has_audio,
-    bool is_remote,
     MediaContentType media_content_type) {
-  DCHECK(id_map_.Lookup(delegate_id));
-  has_played_media_ = true;
-  if (has_video && !is_remote)
-    playing_videos_.insert(delegate_id);
-  else
-    playing_videos_.erase(delegate_id);
-  RemoveIdleDelegate(delegate_id);
+  DVLOG(2) << __func__ << "(" << player_id << ", " << has_video << ", "
+           << has_audio << ", " << static_cast<int>(media_content_type) << ")";
+  DCHECK(id_map_.Lookup(player_id));
 
-  // Upon receipt of a playback request, suspend everything that's not used.
-  if (is_low_end_device_)
-    CleanupIdleDelegates(base::TimeDelta());
+  has_played_media_ = true;
+  if (has_video) {
+    if (!playing_videos_.count(player_id)) {
+      playing_videos_.insert(player_id);
+      has_played_video_ = true;
+    }
+  } else {
+    playing_videos_.erase(player_id);
+  }
 
   Send(new MediaPlayerDelegateHostMsg_OnMediaPlaying(
-      routing_id(), delegate_id, has_video, has_audio, is_remote,
+      routing_id(), player_id, has_video, has_audio, false,
       media_content_type));
+
+  ScheduleUpdateTask();
 }
 
-void RendererWebMediaPlayerDelegate::DidPause(int delegate_id,
-                                              bool reached_end_of_stream) {
-  DCHECK(id_map_.Lookup(delegate_id));
-  AddIdleDelegate(delegate_id);
-  if (reached_end_of_stream)
-    playing_videos_.erase(delegate_id);
-  Send(new MediaPlayerDelegateHostMsg_OnMediaPaused(routing_id(), delegate_id,
-                                                    reached_end_of_stream));
+void RendererWebMediaPlayerDelegate::DidPause(int player_id) {
+  DVLOG(2) << __func__ << "(" << player_id << ")";
+  DCHECK(id_map_.Lookup(player_id));
+  playing_videos_.erase(player_id);
+  Send(new MediaPlayerDelegateHostMsg_OnMediaPaused(routing_id(), player_id,
+                                                    false));
+
+  // Required to keep background playback statistics up to date.
+  ScheduleUpdateTask();
 }
 
-void RendererWebMediaPlayerDelegate::PlayerGone(int delegate_id) {
-  DCHECK(id_map_.Lookup(delegate_id));
-  playing_videos_.erase(delegate_id);
-  Send(new MediaPlayerDelegateHostMsg_OnMediaDestroyed(routing_id(),
-                                                       delegate_id));
+void RendererWebMediaPlayerDelegate::PlayerGone(int player_id) {
+  DVLOG(2) << __func__ << "(" << player_id << ")";
+  DCHECK(id_map_.Lookup(player_id));
+  playing_videos_.erase(player_id);
+  Send(
+      new MediaPlayerDelegateHostMsg_OnMediaDestroyed(routing_id(), player_id));
+
+  // Required to keep background playback statistics up to date.
+  ScheduleUpdateTask();
 }
 
-bool RendererWebMediaPlayerDelegate::IsHidden() {
-  return render_frame()->IsHidden();
+void RendererWebMediaPlayerDelegate::SetIdle(int player_id, bool is_idle) {
+  DVLOG(2) << __func__ << "(" << player_id << ", " << is_idle << ")";
+
+  if (is_idle == IsIdle(player_id))
+    return;
+
+  if (is_idle) {
+    idle_player_map_[player_id] = tick_clock_->NowTicks();
+  } else {
+    idle_player_map_.erase(player_id);
+    stale_players_.erase(player_id);
+  }
+
+  ScheduleUpdateTask();
 }
 
-bool RendererWebMediaPlayerDelegate::IsPlayingBackgroundVideo() {
-  return is_playing_background_video_;
+bool RendererWebMediaPlayerDelegate::IsIdle(int player_id) {
+  return idle_player_map_.count(player_id) || stale_players_.count(player_id);
+}
+
+void RendererWebMediaPlayerDelegate::ClearStaleFlag(int player_id) {
+  DVLOG(2) << __func__ << "(" << player_id << ")";
+
+  if (!stale_players_.erase(player_id))
+    return;
+
+  // Set the idle time such that the player will be considered stale the next
+  // time idle cleanup runs.
+  idle_player_map_[player_id] = tick_clock_->NowTicks() - idle_timeout_;
+
+  ScheduleUpdateTask();
+}
+
+bool RendererWebMediaPlayerDelegate::IsStale(int player_id) {
+  return stale_players_.count(player_id);
 }
 
 void RendererWebMediaPlayerDelegate::WasHidden() {
-  for (IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd(); it.Advance())
-    it.GetCurrentValue()->OnHidden();
-
   RecordAction(base::UserMetricsAction("Media.Hidden"));
+
+  for (IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd(); it.Advance())
+    it.GetCurrentValue()->OnFrameHidden();
+
+  ScheduleUpdateTask();
 }
 
 void RendererWebMediaPlayerDelegate::WasShown() {
-  SetIsPlayingBackgroundVideo(false);
-  for (IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd(); it.Advance())
-    it.GetCurrentValue()->OnShown();
-
   RecordAction(base::UserMetricsAction("Media.Shown"));
+  is_frame_closed_ = false;
+  background_video_allowed_ = false;
+
+  for (IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd(); it.Advance())
+    it.GetCurrentValue()->OnFrameShown();
+
+  ScheduleUpdateTask();
 }
 
 bool RendererWebMediaPlayerDelegate::OnMessageReceived(
     const IPC::Message& msg) {
-  bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(RendererWebMediaPlayerDelegate, msg)
     IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_Pause, OnMediaDelegatePause)
     IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_Play, OnMediaDelegatePlay)
@@ -139,9 +197,9 @@
                         OnMediaDelegateSuspendAllMediaPlayers)
     IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_UpdateVolumeMultiplier,
                         OnMediaDelegateVolumeMultiplierUpdate)
-    IPC_MESSAGE_UNHANDLED(handled = false)
+    IPC_MESSAGE_UNHANDLED(return false)
   IPC_END_MESSAGE_MAP()
-  return handled;
+  return true;
 }
 
 void RendererWebMediaPlayerDelegate::SetIdleCleanupParamsForTesting(
@@ -154,128 +212,158 @@
   is_low_end_device_ = is_low_end_device;
 }
 
-void RendererWebMediaPlayerDelegate::OnMediaDelegatePause(int delegate_id) {
-  Observer* observer = id_map_.Lookup(delegate_id);
-  if (observer) {
-    if (playing_videos_.find(delegate_id) != playing_videos_.end())
-      SetIsPlayingBackgroundVideo(false);
-    observer->OnPause();
-  }
-
-  RecordAction(base::UserMetricsAction("Media.Controls.RemotePause"));
+bool RendererWebMediaPlayerDelegate::IsIdleCleanupTimerRunningForTesting()
+    const {
+  return idle_cleanup_timer_.IsRunning();
 }
 
-void RendererWebMediaPlayerDelegate::OnMediaDelegatePlay(int delegate_id) {
-  Observer* observer = id_map_.Lookup(delegate_id);
-  if (observer) {
-    if (playing_videos_.find(delegate_id) != playing_videos_.end())
-      SetIsPlayingBackgroundVideo(IsHidden());
-    observer->OnPlay();
+void RendererWebMediaPlayerDelegate::SetFrameHiddenForTesting(bool is_hidden) {
+  if (is_hidden == is_frame_hidden_for_testing_)
+    return;
+
+  if (is_hidden) {
+    is_frame_hidden_for_testing_ = true;
+  } else {
+    is_frame_hidden_for_testing_ = false;
+    background_video_allowed_ = false;
   }
 
+  ScheduleUpdateTask();
+}
+
+void RendererWebMediaPlayerDelegate::OnMediaDelegatePause(int player_id) {
+  RecordAction(base::UserMetricsAction("Media.Controls.RemotePause"));
+
+  Observer* observer = id_map_.Lookup(player_id);
+  if (observer) {
+    background_video_allowed_ = false;
+    observer->OnPause();
+  }
+}
+
+void RendererWebMediaPlayerDelegate::OnMediaDelegatePlay(int player_id) {
   RecordAction(base::UserMetricsAction("Media.Controls.RemotePlay"));
+
+  Observer* observer = id_map_.Lookup(player_id);
+  if (observer) {
+    // TODO(sandersd): Ideally we would only set the flag if the player has
+    // video, but we don't reliably know if a paused player has video.
+    if (IsFrameHidden() && !IsFrameClosed())
+      background_video_allowed_ = true;
+    observer->OnPlay();
+  }
 }
 
 void RendererWebMediaPlayerDelegate::OnMediaDelegateSuspendAllMediaPlayers() {
+  is_frame_closed_ = true;
+
   for (IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd(); it.Advance())
-    it.GetCurrentValue()->OnSuspendRequested(true);
+    it.GetCurrentValue()->OnFrameClosed();
 }
 
 void RendererWebMediaPlayerDelegate::OnMediaDelegateVolumeMultiplierUpdate(
-    int delegate_id,
+    int player_id,
     double multiplier) {
-  Observer* observer = id_map_.Lookup(delegate_id);
+  Observer* observer = id_map_.Lookup(player_id);
   if (observer)
     observer->OnVolumeMultiplierUpdate(multiplier);
 }
 
-void RendererWebMediaPlayerDelegate::AddIdleDelegate(int delegate_id) {
-  idle_delegate_map_[delegate_id] = tick_clock_->NowTicks();
-  if (!idle_cleanup_timer_.IsRunning()) {
+void RendererWebMediaPlayerDelegate::ScheduleUpdateTask() {
+  if (!pending_update_task_) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::Bind(&RendererWebMediaPlayerDelegate::UpdateTask, AsWeakPtr()));
+    pending_update_task_ = true;
+  }
+}
+
+void RendererWebMediaPlayerDelegate::UpdateTask() {
+  DVLOG(3) << __func__;
+  pending_update_task_ = false;
+
+  // Check whether a player was played since the last UpdateTask(). We basically
+  // treat this as a parameter to UpdateTask(), except that it can be changed
+  // between posting the task and UpdateTask() executing.
+  bool has_played_video_since_last_update_task = has_played_video_;
+  has_played_video_ = false;
+
+  // Record UMAs for background video playback.
+  RecordBackgroundVideoPlayback();
+
+  // Clean up idle players.
+  bool aggressive_cleanup = false;
+
+  // When we reach the maximum number of idle players, clean them up
+  // aggressively. Values chosen after testing on a Galaxy Nexus device for
+  // http://crbug.com/612909.
+  if (idle_player_map_.size() > (is_low_end_device_ ? 2u : 8u))
+    aggressive_cleanup = true;
+
+  // When a player plays on a low-end device, clean up idle players
+  // aggressively.
+  if (has_played_video_since_last_update_task && is_low_end_device_)
+    aggressive_cleanup = true;
+
+  CleanUpIdlePlayers(aggressive_cleanup ? base::TimeDelta() : idle_timeout_);
+
+  // If there are still idle players, schedule an attempt to clean them up.
+  // This construct ensures that the next callback is always
+  // |idle_cleanup_interval_| from now.
+  idle_cleanup_timer_.Stop();
+  if (!idle_player_map_.empty()) {
     idle_cleanup_timer_.Start(
         FROM_HERE, idle_cleanup_interval_,
-        base::Bind(&RendererWebMediaPlayerDelegate::CleanupIdleDelegates,
-                   base::Unretained(this), idle_timeout_));
+        base::Bind(&RendererWebMediaPlayerDelegate::UpdateTask,
+                   base::Unretained(this)));
   }
-
-  // When we reach the maximum number of idle players, aggressively suspend idle
-  // delegates to try and remain under the limit. Values chosen after testing on
-  // a Galaxy Nexus device for http://crbug.com/612909.
-  if (idle_delegate_map_.size() > (is_low_end_device_ ? 2u : 8u))
-    CleanupIdleDelegates(base::TimeDelta());
 }
 
-void RendererWebMediaPlayerDelegate::RemoveIdleDelegate(int delegate_id) {
-  // To avoid invalidating the iterator, just mark the delegate for deletion
-  // using a sentinel value of an empty TimeTicks.
-  if (idle_cleanup_running_) {
-    idle_delegate_map_[delegate_id] = base::TimeTicks();
-    return;
-  }
+void RendererWebMediaPlayerDelegate::RecordBackgroundVideoPlayback() {
+#if defined(OS_ANDROID)
+  // TODO(avayvod): This would be useful to collect on desktop too and express
+  // in actual media watch time vs. just elapsed time.
+  // See https://crbug.com/638726.
+  bool has_playing_background_video =
+      IsFrameHidden() && !IsFrameClosed() && !playing_videos_.empty();
 
-  idle_delegate_map_.erase(delegate_id);
-  if (idle_delegate_map_.empty())
-    idle_cleanup_timer_.Stop();
-}
+  if (has_playing_background_video != was_playing_background_video_) {
+    was_playing_background_video_ = has_playing_background_video;
 
-void RendererWebMediaPlayerDelegate::CleanupIdleDelegates(
-    base::TimeDelta timeout) {
-  // Drop reentrant cleanups which can occur during forced suspension when the
-  // number of idle delegates is too high for a given device.
-  if (idle_cleanup_running_)
-    return;
-
-  // Iterate over the delegates and suspend the idle ones. Note: The call to
-  // OnSuspendRequested() can trigger calls into RemoveIdleDelegate(), so for
-  // iterator validity we set |idle_cleanup_running_| to true and defer
-  // deletions.
-  DCHECK(!idle_cleanup_running_);
-  base::AutoReset<bool> scoper(&idle_cleanup_running_, true);
-  const base::TimeTicks now = tick_clock_->NowTicks();
-  for (auto& idle_delegate_entry : idle_delegate_map_) {
-    if (now - idle_delegate_entry.second > timeout) {
-      if (id_map_.Lookup(idle_delegate_entry.first)
-              ->OnSuspendRequested(false)) {
-        // If the player accepted the suspension, mark it for removal
-        // from future polls to avoid running the timer forever.
-        idle_delegate_entry.second = base::TimeTicks();
-      }
+    if (has_playing_background_video) {
+      RecordAction(base::UserMetricsAction("Media.Session.BackgroundResume"));
+      background_video_start_time_ = base::TimeTicks::Now();
+    } else {
+      RecordAction(base::UserMetricsAction("Media.Session.BackgroundSuspend"));
+      UMA_HISTOGRAM_CUSTOM_TIMES(
+          "Media.Android.BackgroundVideoTime",
+          base::TimeTicks::Now() - background_video_start_time_,
+          base::TimeDelta::FromSeconds(7), base::TimeDelta::FromHours(10), 50);
     }
   }
-
-  // Take care of any removals that happened during the above iteration.
-  for (auto it = idle_delegate_map_.begin(); it != idle_delegate_map_.end();) {
-    if (it->second.is_null())
-      it = idle_delegate_map_.erase(it);
-    else
-      ++it;
-  }
-
-  // Shutdown the timer if no delegates are left.
-  if (idle_delegate_map_.empty())
-    idle_cleanup_timer_.Stop();
+#endif  // OS_ANDROID
 }
 
-void RendererWebMediaPlayerDelegate::SetIsPlayingBackgroundVideo(
-    bool is_playing) {
-  if (is_playing_background_video_ == is_playing) return;
+void RendererWebMediaPlayerDelegate::CleanUpIdlePlayers(
+    base::TimeDelta timeout) {
+  const base::TimeTicks now = tick_clock_->NowTicks();
 
-// TODO(avayvod): This would be useful to collect on desktop too and express in
-// actual media watch time vs. just elapsed time. See https://crbug.com/638726.
-#if defined(OS_ANDROID)
-  if (is_playing_background_video_) {
-    UMA_HISTOGRAM_CUSTOM_TIMES(
-        "Media.Android.BackgroundVideoTime",
-        base::TimeTicks::Now() - background_video_playing_start_time_,
-        base::TimeDelta::FromSeconds(7), base::TimeDelta::FromHours(10), 50);
-    RecordAction(base::UserMetricsAction("Media.Session.BackgroundSuspend"));
-  } else {
-    background_video_playing_start_time_ = base::TimeTicks::Now();
-    RecordAction(base::UserMetricsAction("Media.Session.BackgroundResume"));
+  // Create a list of stale players before making any possibly reentrant calls
+  // to OnIdleTimeout().
+  std::vector<int> stale_players;
+  for (const auto& it : idle_player_map_) {
+    if (now - it.second >= timeout)
+      stale_players.push_back(it.first);
   }
-#endif  // OS_ANDROID
 
-  is_playing_background_video_ = is_playing;
+  // Notify stale players.
+  for (int player_id : stale_players) {
+    Observer* player = id_map_.Lookup(player_id);
+    if (player && idle_player_map_.erase(player_id)) {
+      stale_players_.insert(player_id);
+      player->OnIdleTimeout();
+    }
+  }
 }
 
 void RendererWebMediaPlayerDelegate::OnDestruct() {
diff --git a/content/renderer/media/renderer_webmediaplayer_delegate.h b/content/renderer/media/renderer_webmediaplayer_delegate.h
index a8aa0e1..d3f7155 100644
--- a/content/renderer/media/renderer_webmediaplayer_delegate.h
+++ b/content/renderer/media/renderer_webmediaplayer_delegate.h
@@ -11,7 +11,9 @@
 
 #include "base/id_map.h"
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
 #include "base/time/default_tick_clock.h"
 #include "base/timer/timer.h"
 #include "content/common/content_export.h"
@@ -26,8 +28,8 @@
 
 enum class MediaContentType;
 
-// An interface to allow a WebMediaPlayerImpl to communicate changes of state
-// to objects that need to know.
+// Standard implementation of WebMediaPlayerDelegate; communicates state to
+// the MediaPlayerDelegateHost.
 class CONTENT_EXPORT RendererWebMediaPlayerDelegate
     : public content::RenderFrameObserver,
       public NON_EXPORTED_BASE(WebMediaPlayerDelegate),
@@ -41,17 +43,21 @@
   bool has_played_media() const { return has_played_media_; }
 
   // WebMediaPlayerDelegate implementation.
+  bool IsFrameHidden() override;
+  bool IsFrameClosed() override;
+  bool IsBackgroundVideoPlaybackUnlocked() override;
   int AddObserver(Observer* observer) override;
-  void RemoveObserver(int delegate_id) override;
-  void DidPlay(int delegate_id,
+  void RemoveObserver(int player_id) override;
+  void DidPlay(int player_id,
                bool has_video,
                bool has_audio,
-               bool is_remote,
                MediaContentType media_content_type) override;
-  void DidPause(int delegate_id, bool reached_end_of_stream) override;
-  void PlayerGone(int delegate_id) override;
-  bool IsHidden() override;
-  bool IsPlayingBackgroundVideo() override;
+  void DidPause(int player_id) override;
+  void PlayerGone(int player_id) override;
+  void SetIdle(int player_id, bool is_idle) override;
+  bool IsIdle(int player_id) override;
+  void ClearStaleFlag(int player_id) override;
+  bool IsStale(int player_id) override;
 
   // content::RenderFrameObserver overrides.
   void WasHidden() override;
@@ -65,61 +71,71 @@
   void SetIdleCleanupParamsForTesting(base::TimeDelta idle_timeout,
                                       base::TickClock* tick_clock,
                                       bool is_low_end_device);
-  bool IsIdleCleanupTimerRunningForTesting() const {
-    return idle_cleanup_timer_.IsRunning();
-  }
+  bool IsIdleCleanupTimerRunningForTesting() const;
+
+  // Note: Does not call OnFrameHidden()/OnFrameShown().
+  void SetFrameHiddenForTesting(bool is_hidden);
 
   friend class RendererWebMediaPlayerDelegateTest;
 
  private:
-  void OnMediaDelegatePause(int delegate_id);
-  void OnMediaDelegatePlay(int delegate_id);
+  void OnMediaDelegatePause(int player_id);
+  void OnMediaDelegatePlay(int player_id);
   void OnMediaDelegateSuspendAllMediaPlayers();
-  void OnMediaDelegateVolumeMultiplierUpdate(int delegate_id,
-                                             double multiplier);
+  void OnMediaDelegateVolumeMultiplierUpdate(int player_id, double multiplier);
 
-  // Adds or removes a delegate from |idle_delegate_map_|. The first insertion
-  // or last removal will start or stop |idle_cleanup_timer_| respectively.
-  void AddIdleDelegate(int delegate_id);
-  void RemoveIdleDelegate(int delegate_id);
+  // Schedules UpdateTask() to run soon.
+  void ScheduleUpdateTask();
 
-  // Runs periodically to suspend idle delegates in |idle_delegate_map_| which
+  // Processes state changes, dispatches CleanupIdlePlayers().
+  void UpdateTask();
+
+  // Records UMAs about background playback.
+  void RecordBackgroundVideoPlayback();
+
+  // Runs periodically to notify stale players in |idle_player_map_| which
   // have been idle for longer than |timeout|.
-  void CleanupIdleDelegates(base::TimeDelta timeout);
+  void CleanUpIdlePlayers(base::TimeDelta timeout);
 
-  // Setter for |is_playing_background_video_| that updates the metrics.
-  void SetIsPlayingBackgroundVideo(bool is_playing);
-
+  // True if any media has ever been played in this render frame. Affects
+  // autoplay logic in RenderFrameImpl.
   bool has_played_media_ = false;
+
+  bool background_video_allowed_ = false;
+  bool is_frame_closed_ = false;
+  bool is_frame_hidden_for_testing_ = false;
+
+  // State related to scheduling UpdateTask(). These are cleared each time
+  // UpdateTask() runs.
+  bool has_played_video_ = false;
+  bool pending_update_task_ = false;
+
   IDMap<Observer*> id_map_;
 
-  // Tracks which delegates have entered an idle state. After some period of
-  // inactivity these players will be suspended to release unused resources.
-  bool idle_cleanup_running_ = false;
-  std::map<int, base::TimeTicks> idle_delegate_map_;
-  base::Timer idle_cleanup_timer_;
+  // Tracks which players have entered an idle state. After some period of
+  // inactivity these players will be notified and become stale.
+  std::map<int, base::TimeTicks> idle_player_map_;
+  std::set<int> stale_players_;
+  base::OneShotTimer idle_cleanup_timer_;
 
-  // Amount of time allowed to elapse after a delegate enters the paused before
-  // the delegate is suspended.
+  // Amount of time allowed to elapse after a player becomes idle before
+  // it can transition to stale.
   base::TimeDelta idle_timeout_;
 
-  // The polling interval used for checking the delegates to see if any have
-  // exceeded |idle_timeout_| since their last pause state.
+  // The polling interval used for checking the players to see if any have
+  // exceeded |idle_timeout_| since becoming idle.
   base::TimeDelta idle_cleanup_interval_;
 
-  // Clock used for calculating when delegates have expired. May be overridden
-  // for testing.
+  // Clock used for calculating when players have become stale. May be
+  // overridden for testing.
   std::unique_ptr<base::DefaultTickClock> default_tick_clock_;
   base::TickClock* tick_clock_;
 
-  // If a video is playing in the background. Set when user resumes a video
-  // allowing it to play and reset when either user pauses it or it goes
-  // foreground.
-  bool is_playing_background_video_ = false;
-
 #if defined(OS_ANDROID)
+  bool was_playing_background_video_ = false;
+
   // Keeps track of when the background video playback started for metrics.
-  base::TimeTicks background_video_playing_start_time_;
+  base::TimeTicks background_video_start_time_;
 #endif  // OS_ANDROID
 
   // The currently playing local videos. Used to determine whether
diff --git a/content/renderer/media/renderer_webmediaplayer_delegate_browsertest.cc b/content/renderer/media/renderer_webmediaplayer_delegate_browsertest.cc
index 30fc19df..98610244 100644
--- a/content/renderer/media/renderer_webmediaplayer_delegate_browsertest.cc
+++ b/content/renderer/media/renderer_webmediaplayer_delegate_browsertest.cc
@@ -30,11 +30,6 @@
 constexpr base::TimeDelta kIdleTimeout = base::TimeDelta::FromSeconds(1);
 }
 
-ACTION_P(RunClosure, closure) {
-  closure.Run();
-  return true;
-}
-
 class MockWebMediaPlayerDelegateObserver
     : public WebMediaPlayerDelegate::Observer {
  public:
@@ -42,9 +37,10 @@
   ~MockWebMediaPlayerDelegateObserver() {}
 
   // WebMediaPlayerDelegate::Observer implementation.
-  MOCK_METHOD0(OnHidden, void());
-  MOCK_METHOD0(OnShown, void());
-  MOCK_METHOD1(OnSuspendRequested, bool(bool));
+  MOCK_METHOD0(OnFrameHidden, void());
+  MOCK_METHOD0(OnFrameClosed, void());
+  MOCK_METHOD0(OnFrameShown, void());
+  MOCK_METHOD0(OnIdleTimeout, void());
   MOCK_METHOD0(OnPlay, void());
   MOCK_METHOD0(OnPause, void());
   MOCK_METHOD1(OnVolumeMultiplierUpdate, void(double));
@@ -73,12 +69,8 @@
  protected:
   IPC::TestSink& test_sink() { return render_thread_->sink(); }
 
-  bool HasPlayingVideo(int delegate_id) {
-    return delegate_manager_->playing_videos_.count(delegate_id);
-  }
-
-  void SetPlayingBackgroundVideo(bool is_playing) {
-    delegate_manager_->is_playing_background_video_ = is_playing;
+  void SetBackgroundVideoPlaybackUnlocked(bool is_unlocked) {
+    delegate_manager_->background_video_allowed_ = is_unlocked;
   }
 
   void CallOnMediaDelegatePlay(int delegate_id) {
@@ -94,6 +86,13 @@
                                                       &tick_clock_, true);
   }
 
+  void RunLoopOnce() {
+    base::RunLoop run_loop;
+    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                  run_loop.QuitClosure());
+    run_loop.Run();
+  }
+
   std::unique_ptr<RendererWebMediaPlayerDelegate> delegate_manager_;
   StrictMock<MockWebMediaPlayerDelegateObserver> observer_1_, observer_2_,
       observer_3_;
@@ -112,7 +111,7 @@
     const bool kHasVideo = true, kHasAudio = false, kIsRemote = false;
     const media::MediaContentType kMediaContentType =
         media::MediaContentType::Transient;
-    delegate_manager_->DidPlay(delegate_id, kHasVideo, kHasAudio, kIsRemote,
+    delegate_manager_->DidPlay(delegate_id, kHasVideo, kHasAudio,
                                kMediaContentType);
 
     const IPC::Message* msg = test_sink().GetUniqueMessageMatching(
@@ -131,8 +130,8 @@
   // Verify the paused message.
   {
     test_sink().ClearMessages();
-    const bool kReachedEndOfStream = true;
-    delegate_manager_->DidPause(delegate_id, kReachedEndOfStream);
+    const bool kReachedEndOfStream = false;
+    delegate_manager_->DidPause(delegate_id);
 
     const IPC::Message* msg = test_sink().GetUniqueMessageMatching(
         MediaPlayerDelegateHostMsg_OnMediaPaused::ID);
@@ -162,10 +161,10 @@
 TEST_F(RendererWebMediaPlayerDelegateTest, DeliversObserverNotifications) {
   const int delegate_id = delegate_manager_->AddObserver(&observer_1_);
 
-  EXPECT_CALL(observer_1_, OnHidden());
+  EXPECT_CALL(observer_1_, OnFrameHidden());
   delegate_manager_->WasHidden();
 
-  EXPECT_CALL(observer_1_, OnShown());
+  EXPECT_CALL(observer_1_, OnFrameShown());
   delegate_manager_->WasShown();
 
   EXPECT_CALL(observer_1_, OnPause());
@@ -182,7 +181,7 @@
                                                            kTestMultiplier);
   delegate_manager_->OnMessageReceived(volume_msg);
 
-  EXPECT_CALL(observer_1_, OnSuspendRequested(true));
+  EXPECT_CALL(observer_1_, OnFrameClosed());
   MediaPlayerDelegateMsg_SuspendAllMediaPlayers suspend_msg(0);
   delegate_manager_->OnMessageReceived(suspend_msg);
 }
@@ -191,21 +190,19 @@
   ASSERT_FALSE(delegate_manager_->IsIdleCleanupTimerRunningForTesting());
 }
 
-TEST_F(RendererWebMediaPlayerDelegateTest, AddingAnObserverStartsTheTimer) {
-  delegate_manager_->AddObserver(&observer_1_);
+TEST_F(RendererWebMediaPlayerDelegateTest, AddingAnIdleObserverStartsTheTimer) {
+  const int delegate_id_1 = delegate_manager_->AddObserver(&observer_1_);
+  delegate_manager_->SetIdle(delegate_id_1, true);
+  RunLoopOnce();
   ASSERT_TRUE(delegate_manager_->IsIdleCleanupTimerRunningForTesting());
 }
 
 TEST_F(RendererWebMediaPlayerDelegateTest, RemovingAllObserversStopsTheTimer) {
-  delegate_manager_->RemoveObserver(
-      delegate_manager_->AddObserver(&observer_1_));
-  ASSERT_FALSE(delegate_manager_->IsIdleCleanupTimerRunningForTesting());
-}
-
-TEST_F(RendererWebMediaPlayerDelegateTest, PlayingDelegatesAreNotIdle) {
   const int delegate_id_1 = delegate_manager_->AddObserver(&observer_1_);
-  delegate_manager_->DidPlay(delegate_id_1, true, true, false,
-                             media::MediaContentType::Persistent);
+  delegate_manager_->SetIdle(delegate_id_1, true);
+  RunLoopOnce();
+  delegate_manager_->RemoveObserver(delegate_id_1);
+  RunLoopOnce();
   ASSERT_FALSE(delegate_manager_->IsIdleCleanupTimerRunningForTesting());
 }
 
@@ -213,60 +210,44 @@
   SetIsLowEndDeviceForTesting();
 
   const int delegate_id_1 = delegate_manager_->AddObserver(&observer_1_);
-  delegate_manager_->AddObserver(&observer_2_);
+  delegate_manager_->SetIdle(delegate_id_1, true);
+  const int delegate_id_2 = delegate_manager_->AddObserver(&observer_2_);
+  delegate_manager_->SetIdle(delegate_id_2, true);
+  RunLoopOnce();
 
   // Calling play on the first player should suspend the other idle player.
-  EXPECT_CALL(observer_2_, OnSuspendRequested(false));
-  tick_clock_.Advance(base::TimeDelta::FromMicroseconds(1));
-  delegate_manager_->DidPlay(delegate_id_1, true, true, false,
+  EXPECT_CALL(observer_2_, OnIdleTimeout());
+  delegate_manager_->DidPlay(delegate_id_1, true, true,
                              media::MediaContentType::Persistent);
+  delegate_manager_->SetIdle(delegate_id_1, false);
+  tick_clock_.Advance(base::TimeDelta::FromMicroseconds(1));
+  RunLoopOnce();
 }
 
 TEST_F(RendererWebMediaPlayerDelegateTest, MaxLowEndIdleDelegates) {
   SetIsLowEndDeviceForTesting();
 
-  delegate_manager_->AddObserver(&observer_1_);
-  delegate_manager_->AddObserver(&observer_2_);
+  int delegate_id_1 = delegate_manager_->AddObserver(&observer_1_);
+  delegate_manager_->SetIdle(delegate_id_1, true);
+  int delegate_id_2 = delegate_manager_->AddObserver(&observer_2_);
+  delegate_manager_->SetIdle(delegate_id_2, true);
+  RunLoopOnce();
 
-  // Just adding a third idle observer should suspend the others.
-  EXPECT_CALL(observer_1_, OnSuspendRequested(false));
-  EXPECT_CALL(observer_2_, OnSuspendRequested(false));
+  // Just adding a third idle observer should suspend all idle players.
+  EXPECT_CALL(observer_1_, OnIdleTimeout());
+  EXPECT_CALL(observer_2_, OnIdleTimeout());
+  int delegate_id_3 = delegate_manager_->AddObserver(&observer_3_);
+  delegate_manager_->SetIdle(delegate_id_3, true);
+  EXPECT_CALL(observer_3_, OnIdleTimeout());
   tick_clock_.Advance(base::TimeDelta::FromMicroseconds(1));
-  delegate_manager_->AddObserver(&observer_3_);
-}
-
-// Make sure it's safe to call DidPause(), which modifies the idle delegate
-// list, from OnSuspendRequested(), which iterates over the idle delegate list.
-TEST_F(RendererWebMediaPlayerDelegateTest, ReentrantDelegateCallsAreSafe) {
-  const int delegate_id_1 = delegate_manager_->AddObserver(&observer_1_);
-  EXPECT_CALL(observer_1_, OnSuspendRequested(false))
-      .WillOnce(RunClosure(base::Bind(&RendererWebMediaPlayerDelegate::DidPause,
-                                      base::Unretained(delegate_manager_.get()),
-                                      delegate_id_1, false)));
-  // Run an idle cleanup.
-  tick_clock_.Advance(kIdleTimeout + base::TimeDelta::FromMicroseconds(1));
-  base::RunLoop().RunUntilIdle();
+  RunLoopOnce();
 }
 
 TEST_F(RendererWebMediaPlayerDelegateTest,
        SuspendRequestsAreOnlySentOnceIfHandled) {
-  delegate_manager_->AddObserver(&observer_1_);
-  // Return true from OnSuspendRequested() to indicate that it was handled. So
-  // even though the player did not call PlayerGone() it should be removed from
-  // future idle cleanup polls.
-  EXPECT_CALL(observer_1_, OnSuspendRequested(false)).WillOnce(Return(true));
-  tick_clock_.Advance(kIdleTimeout + base::TimeDelta::FromMicroseconds(1));
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_F(RendererWebMediaPlayerDelegateTest,
-       SuspendRequestsAreSentAgainIfNotHandled) {
-  delegate_manager_->AddObserver(&observer_1_);
-  // Return false from OnSuspendRequested() to indicate that it was not handled.
-  // The observer should get another OnSuspendRequested.
-  EXPECT_CALL(observer_1_, OnSuspendRequested(false))
-      .WillOnce(Return(false))
-      .WillOnce(Return(true));
+  int delegate_id_1 = delegate_manager_->AddObserver(&observer_1_);
+  delegate_manager_->SetIdle(delegate_id_1, true);
+  EXPECT_CALL(observer_1_, OnIdleTimeout());
   tick_clock_.Advance(kIdleTimeout + base::TimeDelta::FromMicroseconds(1));
   base::RunLoop().RunUntilIdle();
 }
@@ -274,135 +255,59 @@
 TEST_F(RendererWebMediaPlayerDelegateTest, IdleDelegatesAreSuspended) {
   // Add one non-idle observer and one idle observer.
   const int delegate_id_1 = delegate_manager_->AddObserver(&observer_1_);
-  delegate_manager_->DidPlay(delegate_id_1, true, true, false,
-                             media::MediaContentType::Persistent);
-  delegate_manager_->AddObserver(&observer_2_);
+  const int delegate_id_2 = delegate_manager_->AddObserver(&observer_2_);
+  delegate_manager_->SetIdle(delegate_id_2, true);
 
   // The idle cleanup task should suspend the second delegate while the first is
   // kept alive.
   {
-    EXPECT_CALL(observer_2_, OnSuspendRequested(false)).WillOnce(Return(true));
+    EXPECT_CALL(observer_2_, OnIdleTimeout());
     tick_clock_.Advance(kIdleTimeout + base::TimeDelta::FromMicroseconds(1));
-    base::RunLoop().RunUntilIdle();
+    RunLoopOnce();
   }
 
-  // Pausing should count as idle if playback didn't reach end of stream, but
-  // in this case the player will not remove the MediaSession.
-  delegate_manager_->DidPause(delegate_id_1, false /* reached_end_of_stream */);
-  const int delegate_id_3 = delegate_manager_->AddObserver(&observer_3_);
-  delegate_manager_->DidPlay(delegate_id_3, true, true, false,
-                             media::MediaContentType::Persistent);
-
-  // Adding the observer should instantly queue the timeout task. Once run only
-  // the first player should be suspended.
+  // Once the player is idle, it should be suspended after |kIdleTimeout|.
+  delegate_manager_->SetIdle(delegate_id_1, true);
   {
-    EXPECT_CALL(observer_1_, OnSuspendRequested(false)).WillOnce(Return(true));
-    base::RunLoop run_loop;
-    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                                  run_loop.QuitClosure());
+    EXPECT_CALL(observer_1_, OnIdleTimeout());
     tick_clock_.Advance(kIdleTimeout + base::TimeDelta::FromMicroseconds(1));
-    run_loop.Run();
-  }
-
-  delegate_manager_->DidPlay(delegate_id_1, true, true, false,
-                             media::MediaContentType::Persistent);
-
-  // Pausing after reaching end of stream should count as idle.
-  delegate_manager_->DidPause(delegate_id_1, true /* reached_end_of_stream */);
-
-  // Once the timeout task runs the first delegate should be suspended while the
-  // third is kept alive.
-  {
-    EXPECT_CALL(observer_1_, OnSuspendRequested(false)).WillOnce(Return(true));
-    base::RunLoop run_loop;
-    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                                  run_loop.QuitClosure());
-    tick_clock_.Advance(kIdleTimeout + base::TimeDelta::FromMicroseconds(1));
-    run_loop.Run();
+    RunLoopOnce();
   }
 }
 
-TEST_F(RendererWebMediaPlayerDelegateTest, PlayingVideosSet) {
-  int delegate_id = delegate_manager_->AddObserver(&observer_1_);
-  EXPECT_FALSE(HasPlayingVideo(delegate_id));
-
-  // Playing a local video adds it to the set.
-  delegate_manager_->DidPlay(delegate_id, true, true, false,
-                             MediaContentType::Persistent);
-  EXPECT_TRUE(HasPlayingVideo(delegate_id));
-
-  // Pause doesn't remove the video from the set.
-  delegate_manager_->DidPause(delegate_id, false);
-  EXPECT_TRUE(HasPlayingVideo(delegate_id));
-
-  // Reaching the end removes the video from the set.
-  delegate_manager_->DidPause(delegate_id, true);
-  EXPECT_FALSE(HasPlayingVideo(delegate_id));
-
-  // Removing the player removes the video from the set.
-  delegate_manager_->DidPlay(delegate_id, true, true, false,
-                             MediaContentType::Persistent);
-  delegate_manager_->PlayerGone(delegate_id);
-  EXPECT_FALSE(HasPlayingVideo(delegate_id));
-
-  // Playing a remote video removes it from the set.
-  delegate_manager_->DidPlay(delegate_id, true, true, false,
-                             MediaContentType::Persistent);
-  delegate_manager_->DidPlay(delegate_id, true, true, true,
-                             MediaContentType::Persistent);
-  EXPECT_FALSE(HasPlayingVideo(delegate_id));
-
-  // Playing a local video without audio adds it to the set (because of WMPA).
-  delegate_manager_->DidPlay(delegate_id, true, false, false,
-                             MediaContentType::Persistent);
-  EXPECT_TRUE(HasPlayingVideo(delegate_id));
-
-  // Playing a local audio removes it from the set.
-  delegate_manager_->DidPlay(delegate_id, false, true, false,
-                             MediaContentType::Persistent);
-  EXPECT_FALSE(HasPlayingVideo(delegate_id));
-
-  // Removing the observer also removes the video from the set.
-  delegate_manager_->DidPlay(delegate_id, true, true, false,
-                             MediaContentType::Persistent);
-  delegate_manager_->RemoveObserver(delegate_id);
-  EXPECT_FALSE(HasPlayingVideo(delegate_id));
-}
-
-TEST_F(RendererWebMediaPlayerDelegateTest, IsPlayingBackgroundVideo) {
+TEST_F(RendererWebMediaPlayerDelegateTest, IsBackgroundVideoPlaybackUnlocked) {
   NiceMock<MockWebMediaPlayerDelegateObserver> observer;
   int delegate_id = delegate_manager_->AddObserver(&observer);
-  EXPECT_FALSE(delegate_manager_->IsPlayingBackgroundVideo());
+  EXPECT_FALSE(delegate_manager_->IsBackgroundVideoPlaybackUnlocked());
 
   // Showing the frame always clears the flag.
-  SetPlayingBackgroundVideo(true);
+  SetBackgroundVideoPlaybackUnlocked(true);
   delegate_manager_->WasShown();
-  EXPECT_FALSE(delegate_manager_->IsPlayingBackgroundVideo());
-
-  // Pausing anything other than a local playing video doesn't affect the flag.
-  SetPlayingBackgroundVideo(true);
-  CallOnMediaDelegatePause(delegate_id);
-  EXPECT_TRUE(delegate_manager_->IsPlayingBackgroundVideo());
+  EXPECT_FALSE(delegate_manager_->IsBackgroundVideoPlaybackUnlocked());
 
   // Pausing a currently playing video clears the flag.
-  delegate_manager_->DidPlay(delegate_id, true, true, false,
+  delegate_manager_->DidPlay(delegate_id, true, true,
                              MediaContentType::Persistent);
+  SetBackgroundVideoPlaybackUnlocked(true);
   CallOnMediaDelegatePause(delegate_id);
-  EXPECT_FALSE(delegate_manager_->IsPlayingBackgroundVideo());
+  EXPECT_FALSE(delegate_manager_->IsBackgroundVideoPlaybackUnlocked());
 
-  // TODO(avayvod): this test can't mock the IsHidden() method.
+  // TODO(avayvod): this test can't mock the IsFrameHidden() method.
   // Just test that the value changes or doesn't depending on whether the video
   // is currently playing.
-  bool old_value = !delegate_manager_->IsHidden();
-  SetPlayingBackgroundVideo(old_value);
-  delegate_manager_->DidPause(delegate_id, true);
-  CallOnMediaDelegatePlay(delegate_id);
-  EXPECT_EQ(old_value, delegate_manager_->IsPlayingBackgroundVideo());
-
-  delegate_manager_->DidPlay(delegate_id, true, true, false,
-                             MediaContentType::Persistent);
-  CallOnMediaDelegatePlay(delegate_id);
-  EXPECT_NE(old_value, delegate_manager_->IsPlayingBackgroundVideo());
+  if (delegate_manager_->IsFrameHidden()) {
+    SetBackgroundVideoPlaybackUnlocked(false);
+    CallOnMediaDelegatePlay(delegate_id);
+    EXPECT_TRUE(delegate_manager_->IsBackgroundVideoPlaybackUnlocked());
+    CallOnMediaDelegatePause(delegate_id);
+    EXPECT_FALSE(delegate_manager_->IsBackgroundVideoPlaybackUnlocked());
+  } else {
+    SetBackgroundVideoPlaybackUnlocked(false);
+    CallOnMediaDelegatePlay(delegate_id);
+    EXPECT_FALSE(delegate_manager_->IsBackgroundVideoPlaybackUnlocked());
+    CallOnMediaDelegatePause(delegate_id);
+    EXPECT_FALSE(delegate_manager_->IsBackgroundVideoPlaybackUnlocked());
+  }
 }
 
 #if defined(OS_ANDROID)
@@ -413,30 +318,30 @@
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectTotalCount("Media.Android.BackgroundVideoTime", 0);
 
-  // Pausing or showing doesn't record anything as background playback
-  // hasn't started yet.
-  delegate_manager_->DidPlay(delegate_id, true, true, false,
+  // Play/pause while not hidden doesn't record anything.
+  delegate_manager_->DidPlay(delegate_id, true, true,
                              MediaContentType::Persistent);
-  CallOnMediaDelegatePause(delegate_id);
+  RunLoopOnce();
+  delegate_manager_->DidPause(delegate_id);
+  RunLoopOnce();
   histogram_tester.ExpectTotalCount("Media.Android.BackgroundVideoTime", 0);
 
-  delegate_manager_->DidPlay(delegate_id, true, true, false,
+  // Play/pause while hidden does.
+  delegate_manager_->SetFrameHiddenForTesting(true);
+  delegate_manager_->DidPlay(delegate_id, true, true,
                              MediaContentType::Persistent);
-  delegate_manager_->WasShown();
-  histogram_tester.ExpectTotalCount("Media.Android.BackgroundVideoTime", 0);
-
-  // Doing this things after the background playback has started should record
-  // the time.
-  delegate_manager_->DidPlay(delegate_id, true, true, false,
-                             MediaContentType::Persistent);
-  SetPlayingBackgroundVideo(true);
-  CallOnMediaDelegatePause(delegate_id);
+  RunLoopOnce();
+  delegate_manager_->DidPause(delegate_id);
+  RunLoopOnce();
   histogram_tester.ExpectTotalCount("Media.Android.BackgroundVideoTime", 1);
 
-  delegate_manager_->DidPlay(delegate_id, true, true, false,
+  // As does ending background playback by becoming visible.
+  delegate_manager_->SetFrameHiddenForTesting(true);
+  delegate_manager_->DidPlay(delegate_id, true, true,
                              MediaContentType::Persistent);
-  SetPlayingBackgroundVideo(true);
-  delegate_manager_->WasShown();
+  RunLoopOnce();
+  delegate_manager_->SetFrameHiddenForTesting(false);
+  RunLoopOnce();
   histogram_tester.ExpectTotalCount("Media.Android.BackgroundVideoTime", 2);
 }
 
diff --git a/content/renderer/media/webmediaplayer_ms.cc b/content/renderer/media/webmediaplayer_ms.cc
index 96efe619..6ee3c0d 100644
--- a/content/renderer/media/webmediaplayer_ms.cc
+++ b/content/renderer/media/webmediaplayer_ms.cc
@@ -295,8 +295,9 @@
     // TODO(perkj, magjed): We use OneShot focus type here so that it takes
     // audio focus once it starts, and then will not respond to further audio
     // focus changes. See http://crbug.com/596516 for more details.
-    delegate_->DidPlay(delegate_id_, hasVideo(), hasAudio(), false,
+    delegate_->DidPlay(delegate_id_, hasVideo(), hasAudio(),
                        media::MediaContentType::OneShot);
+    delegate_->SetIdle(delegate_id_, false);
   }
 
   paused_ = false;
@@ -320,8 +321,10 @@
   if (audio_renderer_)
     audio_renderer_->Pause();
 
-  if (delegate_)
-    delegate_->DidPause(delegate_id_, false);
+  if (delegate_) {
+    delegate_->DidPause(delegate_id_);
+    delegate_->SetIdle(delegate_id_, true);
+  }
 
   paused_ = true;
 }
@@ -503,7 +506,7 @@
   return 0;
 }
 
-void WebMediaPlayerMS::OnHidden() {
+void WebMediaPlayerMS::OnFrameHidden() {
 #if defined(OS_ANDROID)
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -524,7 +527,25 @@
 #endif  // defined(OS_ANDROID)
 }
 
-void WebMediaPlayerMS::OnShown() {
+void WebMediaPlayerMS::OnFrameClosed() {
+#if defined(OS_ANDROID)
+  if (!paused_) {
+    pause();
+    should_play_upon_shown_ = true;
+  }
+
+  if (delegate_)
+    delegate_->PlayerGone(delegate_id_);
+
+  if (frame_deliverer_) {
+    io_task_runner_->PostTask(
+        FROM_HERE, base::Bind(&FrameDeliverer::SetRenderFrameSuspended,
+                              base::Unretained(frame_deliverer_.get()), true));
+  }
+#endif  // defined(OS_ANDROID)
+}
+
+void WebMediaPlayerMS::OnFrameShown() {
 #if defined(OS_ANDROID)
   DCHECK(thread_checker_.CalledOnValidThread());
 
@@ -540,27 +561,7 @@
 #endif  // defined(OS_ANDROID)
 }
 
-bool WebMediaPlayerMS::OnSuspendRequested(bool must_suspend) {
-#if defined(OS_ANDROID)
-  if (!must_suspend)
-    return false;
-
-  if (!paused_) {
-    pause();
-    should_play_upon_shown_ = true;
-  }
-
-  if (delegate_)
-    delegate_->PlayerGone(delegate_id_);
-
-  if (frame_deliverer_) {
-    io_task_runner_->PostTask(
-        FROM_HERE, base::Bind(&FrameDeliverer::SetRenderFrameSuspended,
-                              base::Unretained(frame_deliverer_.get()), true));
-  }
-#endif  // defined(OS_ANDROID)
-  return true;
-}
+void WebMediaPlayerMS::OnIdleTimeout() {}
 
 void WebMediaPlayerMS::OnPlay() {
   // TODO(perkj, magjed): It's not clear how WebRTC should work with an
diff --git a/content/renderer/media/webmediaplayer_ms.h b/content/renderer/media/webmediaplayer_ms.h
index bbc8d2e..a4b032f 100644
--- a/content/renderer/media/webmediaplayer_ms.h
+++ b/content/renderer/media/webmediaplayer_ms.h
@@ -143,9 +143,10 @@
   size_t videoDecodedByteCount() const override;
 
   // WebMediaPlayerDelegate::Observer implementation.
-  void OnHidden() override;
-  void OnShown() override;
-  bool OnSuspendRequested(bool must_suspend) override;
+  void OnFrameHidden() override;
+  void OnFrameClosed() override;
+  void OnFrameShown() override;
+  void OnIdleTimeout() override;
   void OnPlay() override;
   void OnPause() override;
   void OnVolumeMultiplierUpdate(double multiplier) override;
diff --git a/content/renderer/media/webmediaplayer_ms_unittest.cc b/content/renderer/media/webmediaplayer_ms_unittest.cc
index bed8922..66f6cff 100644
--- a/content/renderer/media/webmediaplayer_ms_unittest.cc
+++ b/content/renderer/media/webmediaplayer_ms_unittest.cc
@@ -55,15 +55,15 @@
   void DidPlay(int delegate_id,
                bool has_video,
                bool has_audio,
-               bool is_remote,
                media::MediaContentType type) override {
     EXPECT_EQ(delegate_id_, delegate_id);
     EXPECT_FALSE(playing_);
     playing_ = true;
+    has_video_ = has_video;
     is_gone_ = false;
   }
 
-  void DidPause(int delegate_id, bool reached_end_of_stream) override {
+  void DidPause(int delegate_id) override {
     EXPECT_EQ(delegate_id_, delegate_id);
     EXPECT_TRUE(playing_);
     EXPECT_FALSE(is_gone_);
@@ -75,9 +75,28 @@
     is_gone_ = true;
   }
 
-  bool IsHidden() override { return is_hidden_; }
+  void SetIdle(int delegate_id, bool is_idle) override {
+    EXPECT_EQ(delegate_id_, delegate_id);
+    is_idle_ = is_idle;
+  }
 
-  bool IsPlayingBackgroundVideo() override { return false; }
+  bool IsIdle(int delegate_id) override {
+    EXPECT_EQ(delegate_id_, delegate_id);
+    return is_idle_;
+  }
+
+  void ClearStaleFlag(int delegate_id) override {
+    EXPECT_EQ(delegate_id_, delegate_id);
+  }
+
+  bool IsStale(int delegate_id) override {
+    EXPECT_EQ(delegate_id_, delegate_id);
+    return false;
+  }
+
+  bool IsFrameHidden() override { return is_hidden_; }
+  bool IsFrameClosed() override { return false; }
+  bool IsBackgroundVideoPlaybackUnlocked() override { return false; }
 
   void set_hidden(bool is_hidden) { is_hidden_ = is_hidden; }
 
@@ -85,8 +104,10 @@
   int delegate_id_ = 1234;
   Observer* observer_ = nullptr;
   bool playing_ = false;
+  bool has_video_ = false;
   bool is_hidden_ = false;
   bool is_gone_ = true;
+  bool is_idle_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(FakeWebMediaPlayerDelegate);
 };
@@ -907,18 +928,18 @@
 
   // A hidden player should start still be playing upon shown.
   delegate_.set_hidden(false);
-  player_->OnShown();
+  player_->OnFrameShown();
   EXPECT_FALSE(player_->paused());
 
   // A hidden event should not pause the player.
   delegate_.set_hidden(true);
-  player_->OnHidden();
+  player_->OnFrameHidden();
   EXPECT_FALSE(player_->paused());
 
   // A user generated pause() should clear the automatic resumption.
   player_->pause();
   delegate_.set_hidden(false);
-  player_->OnShown();
+  player_->OnFrameShown();
   EXPECT_TRUE(player_->paused());
 
   // A user generated play() should start playback.
@@ -926,15 +947,15 @@
   EXPECT_FALSE(player_->paused());
 
   // An OnSuspendRequested() without forced suspension should do nothing.
-  player_->OnSuspendRequested(false);
+  player_->OnIdleTimeout();
   EXPECT_FALSE(player_->paused());
 
   // An OnSuspendRequested() with forced suspension should pause playback.
-  player_->OnSuspendRequested(true);
+  player_->OnFrameClosed();
   EXPECT_TRUE(player_->paused());
 
   // OnShown() should restart after a forced suspension.
-  player_->OnShown();
+  player_->OnFrameShown();
   EXPECT_FALSE(player_->paused());
   EXPECT_CALL(*this, DoSetWebLayer(false));
 
diff --git a/google_apis/drive/drive_api_parser.h b/google_apis/drive/drive_api_parser.h
index 75ff0e7..43fa4f1 100644
--- a/google_apis/drive/drive_api_parser.h
+++ b/google_apis/drive/drive_api_parser.h
@@ -10,11 +10,11 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/compiler_specific.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/strings/string_piece.h"
 #include "base/time/time.h"
 #include "url/gurl.h"
@@ -189,35 +189,37 @@
   // List of primary mime types supported by this WebApp. Primary status should
   // trigger this WebApp becoming the default handler of file instances that
   // have these mime types.
-  const ScopedVector<std::string>& primary_mimetypes() const {
+  const std::vector<std::unique_ptr<std::string>>& primary_mimetypes() const {
     return primary_mimetypes_;
   }
 
   // List of secondary mime types supported by this WebApp. Secondary status
   // should make this WebApp show up in "Open with..." pop-up menu of the
   // default action menu for file with matching mime types.
-  const ScopedVector<std::string>& secondary_mimetypes() const {
+  const std::vector<std::unique_ptr<std::string>>& secondary_mimetypes() const {
     return secondary_mimetypes_;
   }
 
   // List of primary file extensions supported by this WebApp. Primary status
   // should trigger this WebApp becoming the default handler of file instances
   // that match these extensions.
-  const ScopedVector<std::string>& primary_file_extensions() const {
+  const std::vector<std::unique_ptr<std::string>>& primary_file_extensions()
+      const {
     return primary_file_extensions_;
   }
 
   // List of secondary file extensions supported by this WebApp. Secondary
   // status should make this WebApp show up in "Open with..." pop-up menu of the
   // default action menu for file with matching extensions.
-  const ScopedVector<std::string>& secondary_file_extensions() const {
+  const std::vector<std::unique_ptr<std::string>>& secondary_file_extensions()
+      const {
     return secondary_file_extensions_;
   }
 
   // Returns Icons for this application.  An application can have multiple
   // icons for different purpose (application, document, shared document)
   // in several sizes.
-  const ScopedVector<DriveAppIcon>& icons() const {
+  const std::vector<std::unique_ptr<DriveAppIcon>>& icons() const {
     return icons_;
   }
 
@@ -234,22 +236,22 @@
   }
   void set_removable(bool removable) { removable_ = removable; }
   void set_primary_mimetypes(
-      ScopedVector<std::string> primary_mimetypes) {
+      std::vector<std::unique_ptr<std::string>> primary_mimetypes) {
     primary_mimetypes_ = std::move(primary_mimetypes);
   }
   void set_secondary_mimetypes(
-      ScopedVector<std::string> secondary_mimetypes) {
+      std::vector<std::unique_ptr<std::string>> secondary_mimetypes) {
     secondary_mimetypes_ = std::move(secondary_mimetypes);
   }
   void set_primary_file_extensions(
-      ScopedVector<std::string> primary_file_extensions) {
+      std::vector<std::unique_ptr<std::string>> primary_file_extensions) {
     primary_file_extensions_ = std::move(primary_file_extensions);
   }
   void set_secondary_file_extensions(
-      ScopedVector<std::string> secondary_file_extensions) {
+      std::vector<std::unique_ptr<std::string>> secondary_file_extensions) {
     secondary_file_extensions_ = std::move(secondary_file_extensions);
   }
-  void set_icons(ScopedVector<DriveAppIcon> icons) {
+  void set_icons(std::vector<std::unique_ptr<DriveAppIcon>> icons) {
     icons_ = std::move(icons);
   }
   void set_create_url(const GURL& url) {
@@ -271,11 +273,11 @@
   bool supports_create_;
   bool removable_;
   GURL create_url_;
-  ScopedVector<std::string> primary_mimetypes_;
-  ScopedVector<std::string> secondary_mimetypes_;
-  ScopedVector<std::string> primary_file_extensions_;
-  ScopedVector<std::string> secondary_file_extensions_;
-  ScopedVector<DriveAppIcon> icons_;
+  std::vector<std::unique_ptr<std::string>> primary_mimetypes_;
+  std::vector<std::unique_ptr<std::string>> secondary_mimetypes_;
+  std::vector<std::unique_ptr<std::string>> primary_file_extensions_;
+  std::vector<std::unique_ptr<std::string>> secondary_file_extensions_;
+  std::vector<std::unique_ptr<DriveAppIcon>> icons_;
 
   DISALLOW_COPY_AND_ASSIGN(AppResource);
 };
@@ -299,12 +301,16 @@
   const std::string& etag() const { return etag_; }
 
   // Returns a vector of applications.
-  const ScopedVector<AppResource>& items() const { return items_; }
+  const std::vector<std::unique_ptr<AppResource>>& items() const {
+    return items_;
+  }
 
   void set_etag(const std::string& etag) {
     etag_ = etag;
   }
-  void set_items(ScopedVector<AppResource> items) { items_ = std::move(items); }
+  void set_items(std::vector<std::unique_ptr<AppResource>> items) {
+    items_ = std::move(items);
+  }
 
  private:
   friend class DriveAPIParserTest;
@@ -315,7 +321,7 @@
   bool Parse(const base::Value& value);
 
   std::string etag_;
-  ScopedVector<AppResource> items_;
+  std::vector<std::unique_ptr<AppResource>> items_;
 
   DISALLOW_COPY_AND_ASSIGN(AppList);
 };
@@ -613,8 +619,12 @@
   const GURL& next_link() const { return next_link_; }
 
   // Returns a set of files in this list.
-  const ScopedVector<FileResource>& items() const { return items_; }
-  ScopedVector<FileResource>* mutable_items() { return &items_; }
+  const std::vector<std::unique_ptr<FileResource>>& items() const {
+    return items_;
+  }
+  std::vector<std::unique_ptr<FileResource>>* mutable_items() {
+    return &items_;
+  }
 
   void set_next_link(const GURL& next_link) {
     next_link_ = next_link;
@@ -629,7 +639,7 @@
   bool Parse(const base::Value& value);
 
   GURL next_link_;
-  ScopedVector<FileResource> items_;
+  std::vector<std::unique_ptr<FileResource>> items_;
 
   DISALLOW_COPY_AND_ASSIGN(FileList);
 };
@@ -721,8 +731,12 @@
   int64_t largest_change_id() const { return largest_change_id_; }
 
   // Returns a set of changes in this list.
-  const ScopedVector<ChangeResource>& items() const { return items_; }
-  ScopedVector<ChangeResource>* mutable_items() { return &items_; }
+  const std::vector<std::unique_ptr<ChangeResource>>& items() const {
+    return items_;
+  }
+  std::vector<std::unique_ptr<ChangeResource>>* mutable_items() {
+    return &items_;
+  }
 
   void set_next_link(const GURL& next_link) {
     next_link_ = next_link;
@@ -741,7 +755,7 @@
 
   GURL next_link_;
   int64_t largest_change_id_;
-  ScopedVector<ChangeResource> items_;
+  std::vector<std::unique_ptr<ChangeResource>> items_;
 
   DISALLOW_COPY_AND_ASSIGN(ChangeList);
 };
diff --git a/google_apis/drive/drive_api_requests.h b/google_apis/drive/drive_api_requests.h
index 1802d0d..9ccd9fa7 100644
--- a/google_apis/drive/drive_api_requests.h
+++ b/google_apis/drive/drive_api_requests.h
@@ -15,6 +15,7 @@
 #include "base/callback_forward.h"
 #include "base/location.h"
 #include "base/macros.h"
+#include "base/memory/scoped_vector.h"
 #include "base/sequenced_task_runner.h"
 #include "base/task_runner_util.h"
 #include "base/time/time.h"
diff --git a/ios/chrome/browser/payments/BUILD.gn b/ios/chrome/browser/payments/BUILD.gn
index 87f988e..19ebe31 100644
--- a/ios/chrome/browser/payments/BUILD.gn
+++ b/ios/chrome/browser/payments/BUILD.gn
@@ -30,8 +30,6 @@
     "payment_request_utils.mm",
     "payment_request_view_controller.h",
     "payment_request_view_controller.mm",
-    "payment_request_web_state_observer.h",
-    "payment_request_web_state_observer.mm",
     "shipping_address_selection_coordinator.h",
     "shipping_address_selection_coordinator.mm",
     "shipping_address_selection_view_controller.h",
diff --git a/ios/chrome/browser/payments/payment_request_manager.mm b/ios/chrome/browser/payments/payment_request_manager.mm
index 657183145..b3565d7 100644
--- a/ios/chrome/browser/payments/payment_request_manager.mm
+++ b/ios/chrome/browser/payments/payment_request_manager.mm
@@ -17,7 +17,6 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/payments/js_payment_request_manager.h"
 #import "ios/chrome/browser/payments/payment_request_coordinator.h"
-#import "ios/chrome/browser/payments/payment_request_web_state_observer.h"
 #include "ios/web/public/favicon_status.h"
 #include "ios/web/public/navigation_item.h"
 #include "ios/web/public/navigation_manager.h"
@@ -28,14 +27,15 @@
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
 #include "ios/web/public/web_state/url_verification_constants.h"
 #include "ios/web/public/web_state/web_state.h"
+#import "ios/web/public/web_state/web_state_observer_bridge.h"
 
 namespace {
 // Command prefix for injected JavaScript.
 const std::string kCommandPrefix = "paymentRequest";
 }  // namespace
 
-@interface PaymentRequestManager ()<PaymentRequestCoordinatorDelegate,
-                                    PaymentRequestWebStateDelegate> {
+@interface PaymentRequestManager ()<CRWWebStateObserver,
+                                    PaymentRequestCoordinatorDelegate> {
   // View controller used to present the PaymentRequest view controller.
   base::WeakNSObject<UIViewController> _baseViewController;
 
@@ -46,7 +46,7 @@
   web::WebState* _webState;
 
   // Observer for |_webState|.
-  std::unique_ptr<PaymentRequestWebStateObserver> _webStateObserver;
+  std::unique_ptr<web::WebStateObserverBridge> _webStateObserver;
 
   // Object that manages JavaScript injection into the web view.
   base::WeakNSObject<JSPaymentRequestManager> _paymentRequestJsManager;
@@ -101,10 +101,6 @@
     _personalDataManager =
         autofill::PersonalDataManagerFactory::GetForBrowserState(
             browserState->GetOriginalChromeBrowserState());
-
-    // Set up the web state observer. This lasts as long as this object does,
-    // but it will observe and un-observe the web tabs as it changes over time.
-    _webStateObserver.reset(new PaymentRequestWebStateObserver(self));
   }
   return self;
 }
@@ -122,7 +118,7 @@
             [webState->GetJSInjectionReceiver()
                 instanceOfClass:[JSPaymentRequestManager class]]));
     _webState = webState;
-    _webStateObserver->ObserveWebState(webState);
+    _webStateObserver.reset(new web::WebStateObserverBridge(webState, self));
     [self enableCurrentWebState];
   } else {
     _webState = nullptr;
@@ -171,7 +167,7 @@
     return;
   }
 
-  if (_enabled && [self webStateContentIsSecureHTML]) {
+  if (_enabled) {
     if (!_webStateEnabled) {
       base::WeakNSObject<PaymentRequestManager> weakSelf(self);
       auto callback =
@@ -186,8 +182,6 @@
 
       _webStateEnabled = YES;
     }
-
-    [self initializeWebViewForPaymentRequest];
   } else {
     [self disableCurrentWebState];
   }
@@ -203,7 +197,7 @@
 - (void)disconnectWebState {
   if (_webState) {
     _paymentRequestJsManager.reset();
-    _webStateObserver->ObserveWebState(nullptr);
+    _webStateObserver.reset();
     [self disableCurrentWebState];
   }
 }
@@ -211,10 +205,6 @@
 - (void)initializeWebViewForPaymentRequest {
   DCHECK(_webStateEnabled);
 
-  if (![self webStateContentIsSecureHTML]) {
-    return;
-  }
-
   [_paymentRequestJsManager inject];
   _isScriptInjected = YES;
 }
@@ -324,14 +314,14 @@
                                 completionHandler:nil];
 }
 
-#pragma mark - PaymentRequestWebStateDelegate methods
+#pragma mark - CRWWebStateObserver methods
 
-- (void)pageLoadedWithStatus:(web::PageLoadCompletionStatus)loadStatus {
-  if (loadStatus != web::PageLoadCompletionStatus::SUCCESS)
-    return;
-
-  [self dismissUI];
+- (void)webState:(web::WebState*)webState
+    didCommitNavigationWithDetails:
+        (const web::LoadCommittedDetails&)load_details {
   _isScriptInjected = NO;
+  [self dismissUI];
+  [self initializeWebViewForPaymentRequest];
   [self enableCurrentWebState];
 }
 
diff --git a/ios/chrome/browser/payments/payment_request_web_state_observer.h b/ios/chrome/browser/payments/payment_request_web_state_observer.h
deleted file mode 100644
index 7f2d9bf..0000000
--- a/ios/chrome/browser/payments/payment_request_web_state_observer.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_PAYMENTS_PAYMENT_REQUEST_WEB_STATE_OBSERVER_H_
-#define IOS_CHROME_BROWSER_PAYMENTS_PAYMENT_REQUEST_WEB_STATE_OBSERVER_H_
-
-#import <UIKit/UIKit.h>
-
-#include "ios/web/public/web_state/web_state_observer.h"
-
-@protocol PaymentRequestWebStateDelegate<NSObject>
-@optional
-- (void)pageLoadedWithStatus:(web::PageLoadCompletionStatus)loadStatus;
-
-@end
-
-class PaymentRequestWebStateObserver : public web::WebStateObserver {
- public:
-  explicit PaymentRequestWebStateObserver(
-      id<PaymentRequestWebStateDelegate> delegate);
-  ~PaymentRequestWebStateObserver() override;
-
-  void ObserveWebState(web::WebState* web_state);
-
- private:
-  void PageLoaded(
-      web::PageLoadCompletionStatus load_completion_status) override;
-
-  id<PaymentRequestWebStateDelegate> delegate_;
-};
-
-#endif  // IOS_CHROME_BROWSER_PAYMENTS_PAYMENT_REQUEST_WEB_STATE_OBSERVER_H_
diff --git a/ios/chrome/browser/payments/payment_request_web_state_observer.mm b/ios/chrome/browser/payments/payment_request_web_state_observer.mm
deleted file mode 100644
index f8c2b23..0000000
--- a/ios/chrome/browser/payments/payment_request_web_state_observer.mm
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/payments/payment_request_web_state_observer.h"
-
-PaymentRequestWebStateObserver::PaymentRequestWebStateObserver(
-    id<PaymentRequestWebStateDelegate> delegate)
-    : web::WebStateObserver(), delegate_(delegate) {}
-
-PaymentRequestWebStateObserver::~PaymentRequestWebStateObserver() {}
-
-void PaymentRequestWebStateObserver::ObserveWebState(web::WebState* web_state) {
-  Observe(web_state);
-}
-
-void PaymentRequestWebStateObserver::PageLoaded(
-    web::PageLoadCompletionStatus load_completion_status) {
-  if ([delegate_ respondsToSelector:@selector(pageLoadedWithStatus:)]) {
-    [delegate_ pageLoadedWithStatus:load_completion_status];
-  }
-}
diff --git a/media/audio/audio_output_proxy_unittest.cc b/media/audio/audio_output_proxy_unittest.cc
index 78588f9..06b2090 100644
--- a/media/audio/audio_output_proxy_unittest.cc
+++ b/media/audio/audio_output_proxy_unittest.cc
@@ -463,6 +463,23 @@
     proxy->Close();
   }
 
+  void DispatcherDestroyed_AfterStop(
+      std::unique_ptr<AudioOutputDispatcher> dispatcher) {
+    MockAudioOutputStream stream(&manager_, params_);
+    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+        .WillOnce(Return(&stream));
+    EXPECT_CALL(stream, Open()).WillOnce(Return(true));
+    EXPECT_CALL(stream, Close()).Times(1);
+    EXPECT_CALL(stream, SetVolume(_)).Times(1);
+
+    AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
+    EXPECT_TRUE(proxy->Open());
+    proxy->Start(&callback_);
+    proxy->Stop();
+    dispatcher.reset();
+    proxy->Close();
+  }
+
   base::MessageLoop message_loop_;
   std::unique_ptr<AudioOutputDispatcherImpl> dispatcher_impl_;
   MockAudioManager manager_;
@@ -609,6 +626,14 @@
   DispatcherDestroyed_BeforeStop(std::move(resampler_));
 }
 
+TEST_F(AudioOutputProxyTest, DispatcherDestroyed_AfterStop) {
+  DispatcherDestroyed_AfterStop(std::move(dispatcher_impl_));
+}
+
+TEST_F(AudioOutputResamplerTest, DispatcherDestroyed_AfterStop) {
+  DispatcherDestroyed_AfterStop(std::move(resampler_));
+}
+
 // Simulate AudioOutputStream::Create() failure with a low latency stream and
 // ensure AudioOutputResampler falls back to the high latency path.
 TEST_F(AudioOutputResamplerTest, LowLatencyCreateFailedFallback) {
diff --git a/media/audio/audio_output_resampler.cc b/media/audio/audio_output_resampler.cc
index 5dbba5d..9439a63 100644
--- a/media/audio/audio_output_resampler.cc
+++ b/media/audio/audio_output_resampler.cc
@@ -242,8 +242,9 @@
 }
 
 AudioOutputResampler::~AudioOutputResampler() {
-  for (auto& iter : callbacks_) {
-    StopStream(iter.first);
+  for (const auto& item : callbacks_) {
+    if (item.second->started())
+      StopStreamInternal(item);
   }
 }
 
@@ -362,25 +363,15 @@
 
 void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) {
   DCHECK(task_runner_->BelongsToCurrentThread());
-  dispatcher_->StopStream(stream_proxy);
 
-  // Now that StopStream() has completed the underlying physical stream should
-  // be stopped and no longer calling OnMoreData(), making it safe to Stop() the
-  // OnMoreDataConverter.
   CallbackMap::iterator it = callbacks_.find(stream_proxy);
-  if (it != callbacks_.end()) {
-    it->second->Stop();
-
-    // Destroy idle streams if any errors occurred during output; this ensures
-    // bad streams will not be reused.  Note: Errors may occur during the Stop()
-    // call above.
-    if (it->second->error_occurred())
-      dispatcher_->CloseAllIdleStreams();
-  }
+  DCHECK(it != callbacks_.end());
+  StopStreamInternal(*it);
 }
 
 void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) {
   DCHECK(task_runner_->BelongsToCurrentThread());
+
   dispatcher_->CloseStream(stream_proxy);
 
   // We assume that StopStream() is always called prior to CloseStream(), so
@@ -396,6 +387,27 @@
   }
 }
 
+void AudioOutputResampler::StopStreamInternal(
+    const CallbackMap::value_type& item) {
+  AudioOutputProxy* stream_proxy = item.first;
+  OnMoreDataConverter* callback = item.second.get();
+  DCHECK(callback->started());
+
+  // Stop the underlying physical stream.
+  dispatcher_->StopStream(stream_proxy);
+
+  // Now that StopStream() has completed the underlying physical stream should
+  // be stopped and no longer calling OnMoreData(), making it safe to Stop() the
+  // OnMoreDataConverter.
+  callback->Stop();
+
+  // Destroy idle streams if any errors occurred during output; this ensures
+  // bad streams will not be reused.  Note: Errors may occur during the Stop()
+  // call above.
+  if (callback->error_occurred())
+    dispatcher_->CloseAllIdleStreams();
+}
+
 OnMoreDataConverter::OnMoreDataConverter(const AudioParameters& input_params,
                                          const AudioParameters& output_params)
     : io_ratio_(static_cast<double>(input_params.GetBytesPerSecond()) /
diff --git a/media/audio/audio_output_resampler.h b/media/audio/audio_output_resampler.h
index cbd9c9e1..da512e0 100644
--- a/media/audio/audio_output_resampler.h
+++ b/media/audio/audio_output_resampler.h
@@ -48,6 +48,9 @@
   void CloseStream(AudioOutputProxy* stream_proxy) override;
 
  private:
+  using CallbackMap =
+      std::map<AudioOutputProxy*, std::unique_ptr<OnMoreDataConverter>>;
+
   // Converts low latency based output parameters into high latency
   // appropriate output parameters in error situations.
   void SetupFallbackParams();
@@ -58,13 +61,14 @@
   // Used to initialize |dispatcher_|.
   void Initialize();
 
+  // Stops the stream corresponding to the |item| in |callbacks_|.
+  void StopStreamInternal(const CallbackMap::value_type& item);
+
   // Dispatcher to proxy all AudioOutputDispatcher calls too.
   std::unique_ptr<AudioOutputDispatcherImpl> dispatcher_;
 
   // Map of outstanding OnMoreDataConverter objects.  A new object is created
   // on every StartStream() call and destroyed on CloseStream().
-  typedef std::map<AudioOutputProxy*, std::unique_ptr<OnMoreDataConverter>>
-      CallbackMap;
   CallbackMap callbacks_;
 
   // Used by AudioOutputDispatcherImpl; kept so we can reinitialize on the fly.
diff --git a/media/base/audio_renderer_mixer_unittest.cc b/media/base/audio_renderer_mixer_unittest.cc
index f0912f4..9a1bd79 100644
--- a/media/base/audio_renderer_mixer_unittest.cc
+++ b/media/base/audio_renderer_mixer_unittest.cc
@@ -553,7 +553,7 @@
 // support single item lists and we don't want these test cases to run for every
 // parameter set.
 INSTANTIATE_TEST_CASE_P(
-    AudioRendererMixerBehavioralTest,
+    /* no prefix */,
     AudioRendererMixerBehavioralTest,
     testing::ValuesIn(std::vector<AudioRendererMixerTestData>(
         1,
diff --git a/media/blink/webmediaplayer_delegate.h b/media/blink/webmediaplayer_delegate.h
index 62c93bc..0795c8d 100644
--- a/media/blink/webmediaplayer_delegate.h
+++ b/media/blink/webmediaplayer_delegate.h
@@ -12,73 +12,113 @@
 
 enum class MediaContentType;
 
-// An interface to allow a WebMediaPlayer to communicate changes of state to
-// objects that need to know.
+// An interface to collect WebMediaPlayer state changes and to fan out commands
+// from the browser.
 class WebMediaPlayerDelegate {
  public:
+  // Note: WebMediaPlayerDelegate implementations should not call an Observer
+  // method on a stack that includes a call from the player.
+  // Note: It is likely that players will call WebMediaPlayerDelegate methods
+  // from within Observer callbacks.
   class Observer {
    public:
-    // Called when the WebMediaPlayer enters the background or foreground
-    // respectively. Note: Some implementations will stop playback when hidden,
-    // and thus subsequently call WebMediaPlayerDelegate::PlayerGone().
-    virtual void OnHidden() = 0;
-    virtual void OnShown() = 0;
+    // Called when the host frame is hidden (usually by tab switching).
+    // Note: OnFrameHidden() is not called when the frame is closed, even though
+    // IsFrameHidden() will start returning true.
+    virtual void OnFrameHidden() = 0;
 
-    // Requests a WebMediaPlayer instance to release all idle resources. If
-    // |must_suspend| is true, the player must stop playback, release all idle
-    // resources, and finally call WebMediaPlayerDelegate::PlayerGone(). If
-    // |must_suspend| is false, the player may ignore the request. Optionally,
-    // it may do some or all of the same actions as when |must_suspend| is true.
-    // To be clear, the player is not required to call PlayerGone() when
-    // |must_suspend| is false.
-    // Return false to reject the request and indicate that further calls to
-    // OnSuspendRequested() are required. Otherwise the Observer is removed
-    // from the idle list.
-    virtual bool OnSuspendRequested(bool must_suspend) = 0;
+    // Called when the host frame is closed.
+    // Note: It is possible for a closed frame to be shown again. (Android only;
+    // other platforms tear down players when the host frame is closed.) There
+    // is no callback for frame opening, observers are expected to wait until
+    // OnFrameShown().
+    // TODO(sandersd): Experiment to verify exactly what gets called when
+    // restoring a closed tab on Android.
+    virtual void OnFrameClosed() = 0;
 
+    // Called when the host frame is shown (usually by tab switching).
+    virtual void OnFrameShown() = 0;
+
+    // Called when an idle player has become stale, usually interpreted to mean
+    // that it is unlikely to be interacted with in the near future.
+    //
+    // Players should typically respond by releasing resources, for example by
+    // discarding their decoders.
+    virtual void OnIdleTimeout() = 0;
+
+    // Called when external controls are activated.
     virtual void OnPlay() = 0;
     virtual void OnPause() = 0;
 
-    // Playout volume should be set to current_volume * multiplier. The range is
-    // [0, 1] and is typically 1.
+    // Called to control audio ducking. Output volume should be set to
+    // |player_volume| * |multiplier|. The range of |multiplier| is [0, 1],
+    // where 1 indicates normal (non-ducked) playback.
     virtual void OnVolumeMultiplierUpdate(double multiplier) = 0;
   };
 
-  WebMediaPlayerDelegate() {}
+  // Returns true if the host frame is hidden or closed.
+  virtual bool IsFrameHidden() = 0;
 
-  // Subscribe or unsubscribe from observer callbacks respectively. A client
-  // must use the delegate id returned by AddObserver() for all other calls.
+  // Returns true if the host frame is closed.
+  virtual bool IsFrameClosed() = 0;
+
+  // Returns |true| if background video playback permission has been granted,
+  // for example by a media session 'play' command.
+  virtual bool IsBackgroundVideoPlaybackUnlocked() = 0;
+
+  // Subscribe to observer callbacks. A player must use the returned |player_id|
+  // for the rest of the calls below.
   virtual int AddObserver(Observer* observer) = 0;
-  virtual void RemoveObserver(int delegate_id) = 0;
 
-  // The specified player started playing media.
-  virtual void DidPlay(int delegate_id,
+  // Unsubscribe from observer callbacks.
+  virtual void RemoveObserver(int player_id) = 0;
+
+  // Notify playback started. This will request appropriate wake locks and, if
+  // applicable, show a pause button in external controls.
+  //
+  // DidPlay() should not be called for remote playback.
+  virtual void DidPlay(int player_id,
                        bool has_video,
                        bool has_audio,
-                       bool is_remote,
                        media::MediaContentType media_content_type) = 0;
 
-  // The specified player stopped playing media. This may be called at any time
-  // with or without a DidPlay() having previously occurred. Calling this will
-  // cause the delegate to be registered for idle suspension. I.e., after some
-  // time elapses without a DidPlay(), OnSuspendRequested() will be issued.
-  virtual void DidPause(int delegate_id, bool reached_end_of_stream) = 0;
+  // Notify that playback is paused. This will drop wake locks and, if
+  // applicable, show a play button in external controls.
+  // TODO(sandersd): It may be helpful to get |has_audio| and |has_video| here,
+  // so that we can do the right thing with media that starts paused.
+  virtual void DidPause(int player_id) = 0;
 
-  // The specified player was destroyed or suspended and will no longer accept
-  // Observer::OnPlay() or Observer::OnPause() calls. This may be called
-  // multiple times in row. Note: Clients must still call RemoveObserver() to
-  // unsubscribe from callbacks.
-  virtual void PlayerGone(int delegate_id) = 0;
+  // Notify that playback is stopped. This will drop wake locks and remove any
+  // external controls.
+  //
+  // Clients must still call RemoveObserver() to unsubscribe from observer
+  // callbacks.
+  virtual void PlayerGone(int player_id) = 0;
 
-  // Returns whether the render frame is currently hidden.
-  virtual bool IsHidden() = 0;
+  // Set the player's idle state. While idle, a player may recieve an
+  // OnIdleTimeout() callback.
+  // TODO(sandersd): Merge this into DidPlay()/DidPause()/PlayerGone().
+  virtual void SetIdle(int player_id, bool is_idle) = 0;
 
-  // Returns whether there's a video playing in background within the render
-  // frame.
-  virtual bool IsPlayingBackgroundVideo() = 0;
+  // Get the player's idle state. A stale player is considered idle.
+  // TODO(sandersd): Remove this. It is only used in tests and in one special
+  // case in WMPI.
+  virtual bool IsIdle(int player_id) = 0;
+
+  // Returns a stale player to an idle state, and resumes OnIdleTimeout() calls
+  // without an additional idle timeout.
+  // TODO(sandersd): This exists only to support WMPI's didLoadingProgress()
+  // workaround. A better option may be to take a 'minimum idle' duration in
+  // SetIdle().
+  virtual void ClearStaleFlag(int player_id) = 0;
+
+  // Returns |true| if the player is stale; that is that OnIdleTimeout() was
+  // called and returned |true|.
+  virtual bool IsStale(int player_id) = 0;
 
  protected:
-  virtual ~WebMediaPlayerDelegate() {}
+  WebMediaPlayerDelegate() = default;
+  virtual ~WebMediaPlayerDelegate() = default;
 };
 
 }  // namespace media
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index b0e7d1c..aabb040 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -149,9 +149,9 @@
   return base::TimeDelta::FromSecondsD(p_this->currentTime());
 }
 
-// How much time must have elapsed since loading last progressed before the
-// player is eligible for idle suspension.
-constexpr base::TimeDelta kLoadingToIdleTimeout =
+// How much time must have elapsed since loading last progressed before we
+// assume that the decoder will have had time to complete preroll.
+constexpr base::TimeDelta kPrerollAttemptTimeout =
     base::TimeDelta::FromSeconds(3);
 
 }  // namespace
@@ -182,8 +182,7 @@
     const WebMediaPlayerParams& params)
     : frame_(frame),
       delegate_state_(DelegateState::GONE),
-      is_idle_(false),
-      must_suspend_(false),
+      delegate_has_audio_(false),
       network_state_(WebMediaPlayer::NetworkStateEmpty),
       ready_state_(WebMediaPlayer::ReadyStateHaveNothing),
       highest_ready_state_(WebMediaPlayer::ReadyStateHaveNothing),
@@ -243,6 +242,7 @@
       use_fallback_path_(false),
       is_encrypted_(false),
       underflow_count_(0),
+      preroll_attempt_pending_(false),
       observer_(params.media_observer()) {
   DCHECK(!adjust_allocated_memory_cb_.is_null());
   DCHECK(renderer_factory_);
@@ -253,8 +253,10 @@
   force_video_overlays_ = base::CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kForceVideoOverlays);
 
-  if (delegate_)
+  if (delegate_) {
     delegate_id_ = delegate_->AddObserver(this);
+    delegate_->SetIdle(delegate_id_, true);
+  }
 
   media_log_->AddEvent(
       media_log_->CreateEvent(MediaLogEvent::WEBMEDIAPLAYER_CREATED));
@@ -425,8 +427,10 @@
     return;
   }
 #endif
+  // TODO(sandersd): Do we want to reset the idle timer here?
+  if (delegate_)
+    delegate_->SetIdle(delegate_id_, false);
   paused_ = false;
-  is_idle_ = false;
   pipeline_.SetPlaybackRate(playback_rate_);
   background_pause_timer_.Stop();
 
@@ -531,11 +535,11 @@
   if (watch_time_reporter_)
     watch_time_reporter_->OnSeeking();
 
-  // TODO(sandersd): Ideally we would not clear the idle state if
-  // |pipeline_controller_| can elide the seek.
-  is_idle_ = false;
+  // TODO(sandersd): Move |seeking_| to PipelineController.
+  // TODO(sandersd): Do we want to reset the idle timer here?
+  if (delegate_)
+    delegate_->SetIdle(delegate_id_, false);
   ended_ = false;
-
   seeking_ = true;
   seek_time_ = time;
   if (paused_)
@@ -815,6 +819,26 @@
   return blink::WebTimeRanges(&seekable_range, 1);
 }
 
+bool WebMediaPlayerImpl::IsPrerollAttemptNeeded() {
+  // TODO(sandersd): Replace with |highest_ready_state_since_seek_| if we need
+  // to ensure that preroll always gets a chance to complete.
+  // See http://crbug.com/671525.
+  if (highest_ready_state_ >= ReadyState::ReadyStateHaveFutureData)
+    return false;
+
+  if (preroll_attempt_pending_)
+    return true;
+
+  // Freshly initialized; there has never been any loading progress. (Otherwise
+  // |preroll_attempt_pending_| would be true when the start time is null.)
+  if (preroll_attempt_start_time_.is_null())
+    return false;
+
+  base::TimeDelta preroll_attempt_duration =
+      tick_clock_->NowTicks() - preroll_attempt_start_time_;
+  return preroll_attempt_duration < kPrerollAttemptTimeout;
+}
+
 bool WebMediaPlayerImpl::didLoadingProgress() {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
 
@@ -823,19 +847,22 @@
   const bool data_progress = buffered_data_source_host_.DidLoadingProgress();
   const bool did_loading_progress = pipeline_progress || data_progress;
 
-  // If we've idle suspended before reaching kHaveFutureData and loading has
-  // progressed we need to spin up the renderer and figure out if we have enough
-  // data yet; |client_| may be waiting on this signal to trigger playback. The
-  // idle timeout is long enough that this is a low-cost operation.
-  if (highest_ready_state_ < ReadyState::ReadyStateHaveFutureData &&
-      pipeline_controller_.IsSuspended() && did_loading_progress && is_idle_) {
-    is_idle_ = false;
+  if (did_loading_progress &&
+      highest_ready_state_ < ReadyState::ReadyStateHaveFutureData) {
+    // Reset the preroll attempt clock.
+    preroll_attempt_pending_ = true;
+    preroll_attempt_start_time_ = base::TimeTicks();
+
+    // Clear any 'stale' flag and give the pipeline a chance to resume. If we
+    // are already resumed, this will cause |preroll_attempt_start_time_| to be
+    // set.
+    // TODO(sandersd): Should this be on the same stack? It might be surprising
+    // that didLoadingProgress() can synchronously change state.
+    if (delegate_)
+      delegate_->ClearStaleFlag(delegate_id_);
     UpdatePlayState();
   }
 
-  if (did_loading_progress)
-    last_time_loading_progressed_ = tick_clock_->NowTicks();
-
   return did_loading_progress;
 }
 
@@ -1253,8 +1280,10 @@
       data_source_->OnBufferingHaveEnough(false);
 
     // Blink expects a timeChanged() in response to a seek().
-    if (should_notify_time_changed_)
+    if (should_notify_time_changed_) {
+      should_notify_time_changed_ = false;
       client_->timeChanged();
+    }
 
     // Once we have enough, start reporting the total memory usage. We'll also
     // report once playback starts.
@@ -1375,7 +1404,7 @@
     video_weblayer_->layer()->SetContentsOpaque(opaque_);
 }
 
-void WebMediaPlayerImpl::OnHidden() {
+void WebMediaPlayerImpl::OnFrameHidden() {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
 
   if (watch_time_reporter_)
@@ -1400,8 +1429,15 @@
   ScheduleIdlePauseTimer();
 }
 
-void WebMediaPlayerImpl::OnShown() {
+void WebMediaPlayerImpl::OnFrameClosed() {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
+  UpdatePlayState();
+}
+
+void WebMediaPlayerImpl::OnFrameShown() {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+  background_pause_timer_.Stop();
+
   if (watch_time_reporter_)
     watch_time_reporter_->OnShown();
 
@@ -1410,9 +1446,6 @@
       base::Bind(&VideoFrameCompositor::SetForegroundTime,
                  base::Unretained(compositor_), base::TimeTicks::Now()));
 
-  must_suspend_ = false;
-  background_pause_timer_.Stop();
-
   if (paused_when_hidden_) {
     paused_when_hidden_ = false;
     OnPlay();  // Calls UpdatePlayState() so return afterwards.
@@ -1424,37 +1457,17 @@
   UpdatePlayState();
 }
 
-bool WebMediaPlayerImpl::OnSuspendRequested(bool must_suspend) {
+void WebMediaPlayerImpl::OnIdleTimeout() {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
 
-  if (must_suspend) {
-    must_suspend_ = true;
-    UpdatePlayState();
-    return true;
+  // If we are attempting preroll, clear the stale flag.
+  if (IsPrerollAttemptNeeded()) {
+    if (delegate_)
+      delegate_->ClearStaleFlag(delegate_id_);
+    return;
   }
 
-  // If we're beyond HaveFutureData, we can safely suspend at any time.
-  if (highest_ready_state_ >= WebMediaPlayer::ReadyStateHaveFutureData) {
-    is_idle_ = true;
-    UpdatePlayState();
-    return true;
-  }
-
-  // Before HaveFutureData blink will not call play(), so we must be careful to
-  // only suspend if we'll eventually receive an event that will trigger a
-  // resume. If the last time loading progressed was a while ago, and we still
-  // haven't reached HaveFutureData, we assume that we're waiting on more data
-  // to continue pre-rolling. When that data is loaded the pipeline will be
-  // resumed by didLoadingProgress().
-  if (last_time_loading_progressed_.is_null() ||
-      (tick_clock_->NowTicks() - last_time_loading_progressed_) >
-          kLoadingToIdleTimeout) {
-    is_idle_ = true;
-    UpdatePlayState();
-    return true;
-  }
-
-  return false;
+  UpdatePlayState();
 }
 
 void WebMediaPlayerImpl::OnPlay() {
@@ -1791,43 +1804,46 @@
   bool is_backgrounded = IsBackgroundedSuspendEnabled() && IsHidden();
   PlayState state = UpdatePlayState_ComputePlayState(
       is_remote, is_streaming, is_suspended, is_backgrounded);
-  SetDelegateState(state.delegate_state);
+  SetDelegateState(state.delegate_state, state.is_idle);
   SetMemoryReportingState(state.is_memory_reporting_enabled);
   SetSuspendState(state.is_suspended || pending_suspend_resume_cycle_);
 }
 
-void WebMediaPlayerImpl::SetDelegateState(DelegateState new_state) {
+void WebMediaPlayerImpl::SetDelegateState(DelegateState new_state,
+                                          bool is_idle) {
   if (!delegate_)
     return;
 
-  if (delegate_state_ == new_state) {
-    if (delegate_state_ != DelegateState::PLAYING ||
-        autoplay_muted_ == client_->isAutoplayingMuted()) {
-      return;
-    }
+  // Prevent duplicate delegate calls.
+  // TODO(sandersd): Move this deduplication into the delegate itself.
+  // TODO(sandersd): WebContentsObserverSanityChecker does not allow sending the
+  // 'playing' IPC more than once in a row, even if the metadata has changed.
+  // Figure out whether it should.
+  bool has_audio = hasAudio() && !client_->isAutoplayingMuted();
+  if (delegate_state_ == new_state &&
+      (delegate_state_ != DelegateState::PLAYING ||
+       delegate_has_audio_ == has_audio)) {
+    return;
   }
-
   delegate_state_ = new_state;
+  delegate_has_audio_ = has_audio;
 
-  switch (delegate_state_) {
+  switch (new_state) {
     case DelegateState::GONE:
       delegate_->PlayerGone(delegate_id_);
       break;
     case DelegateState::PLAYING: {
-      autoplay_muted_ = client_->isAutoplayingMuted();
-      bool has_audio = autoplay_muted_ ? false : hasAudio();
       delegate_->DidPlay(
-          delegate_id_, hasVideo(), has_audio, false,
+          delegate_id_, hasVideo(), has_audio,
           media::DurationToMediaContentType(pipeline_.GetMediaDuration()));
       break;
     }
     case DelegateState::PAUSED:
-      delegate_->DidPause(delegate_id_, false);
-      break;
-    case DelegateState::ENDED:
-      delegate_->DidPause(delegate_id_, true);
+      delegate_->DidPause(delegate_id_);
       break;
   }
+
+  delegate_->SetIdle(delegate_id_, is_idle);
 }
 
 void WebMediaPlayerImpl::SetMemoryReportingState(
@@ -1873,12 +1889,20 @@
   can_suspend_state_ = CanSuspendState::YES;
 #endif
 
-  if (can_suspend_state_ == CanSuspendState::NO)
-    return;
-
-  if (is_suspended) {
+  if (is_suspended && can_suspend_state_ != CanSuspendState::NO) {
+    // If we were not resumed for long enough to satisfy the preroll attempt,
+    // reset the clock.
+    if (!preroll_attempt_pending_ && IsPrerollAttemptNeeded()) {
+      preroll_attempt_pending_ = true;
+      preroll_attempt_start_time_ = base::TimeTicks();
+    }
     pipeline_controller_.Suspend();
   } else {
+    // When resuming, start the preroll attempt clock.
+    if (preroll_attempt_pending_) {
+      preroll_attempt_pending_ = false;
+      preroll_attempt_start_time_ = tick_clock_->NowTicks();
+    }
     pipeline_controller_.Resume();
   }
 }
@@ -1890,6 +1914,9 @@
                                                      bool is_backgrounded) {
   PlayState result;
 
+  bool must_suspend = delegate_ && delegate_->IsFrameClosed();
+  bool is_stale = delegate_ && delegate_->IsStale(delegate_id_);
+
   // This includes both data source (before pipeline startup) and pipeline
   // errors.
   bool has_error = IsNetworkStateError(network_state_);
@@ -1910,7 +1937,7 @@
   bool can_play_backgrounded = is_backgrounded_video && !is_remote &&
                                hasAudio() && IsResumeBackgroundVideosEnabled();
   bool is_background_playing =
-      delegate_ && delegate_->IsPlayingBackgroundVideo();
+      delegate_ && delegate_->IsBackgroundVideoPlaybackUnlocked();
   bool background_suspended = !is_streaming && is_backgrounded_video &&
                               !(can_play_backgrounded && is_background_playing);
   bool background_pause_suspended =
@@ -1923,16 +1950,16 @@
   // TODO(sandersd): Make the delegate suspend idle players immediately when
   // hidden.
   bool idle_suspended =
-      !is_streaming && is_idle_ && paused_ && !seeking_ && !overlay_enabled_;
+      !is_streaming && is_stale && paused_ && !seeking_ && !overlay_enabled_;
 
   // If we're already suspended, see if we can wait for user interaction. Prior
-  // to HaveFutureData, we require |is_idle_| to remain suspended. |is_idle_|
+  // to HaveFutureData, we require |is_stale| to remain suspended. |is_stale|
   // will be cleared when we receive data which may take us to HaveFutureData.
   bool can_stay_suspended =
-      (is_idle_ || have_future_data) && is_suspended && paused_ && !seeking_;
+      (is_stale || have_future_data) && is_suspended && paused_ && !seeking_;
 
   // Combined suspend state.
-  result.is_suspended = is_remote || must_suspend_ || idle_suspended ||
+  result.is_suspended = is_remote || must_suspend || idle_suspended ||
                         background_suspended || background_pause_suspended ||
                         can_stay_suspended;
 
@@ -1956,30 +1983,34 @@
   // TODO(sandersd): If Blink told us the paused state sooner, we could create
   // the media session sooner.
   bool can_play = !has_error && !is_remote && have_future_data;
-  bool has_session_playing =
-      can_play && !must_suspend_ && !background_suspended;
+  bool has_session_playing = can_play && !must_suspend && !background_suspended;
 
   // |has_session_suspended| means the player is suspended from the media
   // element point of view but paused and can be resumed from the delegate point
   // of view. Therefore it behaves like |paused_| for the delegate.
-  bool has_session_suspended = can_play && !must_suspend_ &&
+  bool has_session_suspended = can_play && !must_suspend &&
                                background_suspended && can_play_backgrounded;
 
   bool has_session = has_session_playing || has_session_suspended;
 
   if (!has_session) {
     result.delegate_state = DelegateState::GONE;
+    result.is_idle = delegate_ && delegate_->IsIdle(delegate_id_);
   } else if (paused_ || has_session_suspended) {
+    // TODO(sandersd): Is it possible to have a suspended session, be ended,
+    // and not be paused? If so we should be in a PLAYING state.
     result.delegate_state =
-        ended_ ? DelegateState::ENDED : DelegateState::PAUSED;
+        ended_ ? DelegateState::GONE : DelegateState::PAUSED;
+    result.is_idle = !seeking_;
   } else {
     result.delegate_state = DelegateState::PLAYING;
+    result.is_idle = false;
   }
 
   // It's not critical if some cases where memory usage can change are missed,
   // since media memory changes are usually gradual.
   result.is_memory_reporting_enabled =
-      can_play && !result.is_suspended && !paused_;
+      can_play && !result.is_suspended && (!paused_ || seeking_);
 
   return result;
 }
@@ -2065,7 +2096,7 @@
       pipeline_metadata_.natural_size,
       base::Bind(&GetCurrentTimeInternal, this)));
   watch_time_reporter_->OnVolumeChange(volume_);
-  if (IsHidden())
+  if (delegate_ && delegate_->IsFrameHidden())
     watch_time_reporter_->OnHidden();
   else
     watch_time_reporter_->OnShown();
@@ -2074,7 +2105,7 @@
 bool WebMediaPlayerImpl::IsHidden() const {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
 
-  return delegate_ && delegate_->IsHidden();
+  return delegate_ && delegate_->IsFrameHidden() && !delegate_->IsFrameClosed();
 }
 
 bool WebMediaPlayerImpl::IsStreaming() const {
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index 1ce44b5..da1a062f 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -187,9 +187,10 @@
   void setPoster(const blink::WebURL& poster) override;
 
   // WebMediaPlayerDelegate::Observer implementation.
-  void OnHidden() override;
-  void OnShown() override;
-  bool OnSuspendRequested(bool must_suspend) override;
+  void OnFrameHidden() override;
+  void OnFrameClosed() override;
+  void OnFrameShown() override;
+  void OnIdleTimeout() override;
   void OnPlay() override;
   void OnPause() override;
   void OnVolumeMultiplierUpdate(double multiplier) override;
@@ -226,20 +227,18 @@
   // intersection.
   void ActivateViewportIntersectionMonitoring(bool activate);
 
-  // Distinct states that |delegate_| can be in.
-  // TODO(sandersd): This should move into WebMediaPlayerDelegate.
-  // (Public for testing.)
+  // Distinct states that |delegate_| can be in. (Public for testing.)
   enum class DelegateState {
     GONE,
     PLAYING,
     PAUSED,
-    ENDED,
   };
 
   // Playback state variables computed together in UpdatePlayState().
   // (Public for testing.)
   struct PlayState {
     DelegateState delegate_state;
+    bool is_idle;
     bool is_memory_reporting_enabled;
     bool is_suspended;
   };
@@ -346,7 +345,7 @@
                                              bool is_streaming,
                                              bool is_suspended,
                                              bool is_backgrounded);
-  void SetDelegateState(DelegateState new_state);
+  void SetDelegateState(DelegateState new_state, bool is_idle);
   void SetMemoryReportingState(bool is_memory_reporting_enabled);
   void SetSuspendState(bool is_suspended);
 
@@ -361,6 +360,15 @@
   // paused state after some idle timeout.
   void ScheduleIdlePauseTimer();
 
+  // Returns |true| before HaveFutureData whenever there has been loading
+  // progress and we have not been resumed for at least kLoadingToIdleTimeout
+  // since then.
+  //
+  // This is used to delay suspension long enough for preroll to complete, which
+  // is necessay because play() will not be called before HaveFutureData (and
+  // thus we think we are idle forever).
+  bool IsPrerollAttemptNeeded();
+
   void CreateWatchTimeReporter();
 
   // Returns true if the player is hidden.
@@ -406,14 +414,10 @@
   blink::WebLocalFrame* frame_;
 
   // The playback state last reported to |delegate_|, to avoid setting duplicate
-  // states. (Which can have undesired effects like resetting the idle timer.)
+  // states.
+  // TODO(sandersd): The delegate should be implementing deduplication.
   DelegateState delegate_state_;
-
-  // Set when OnSuspendRequested() is called with |must_suspend| unset.
-  bool is_idle_;
-
-  // Set when OnSuspendRequested() is called with |must_suspend| set.
-  bool must_suspend_;
+  bool delegate_has_audio_;
 
   blink::WebMediaPlayer::NetworkState network_state_;
   blink::WebMediaPlayer::ReadyState ready_state_;
@@ -608,8 +612,8 @@
   GURL fallback_url_;
   bool use_fallback_path_;
 
-  // Called some-time after OnHidden() if the media was suspended in a playing
-  // state as part of the call to OnHidden().
+  // Called sometime after the media is suspended in a playing state in
+  // OnFrameHidden(), causing the state to change to paused.
   base::OneShotTimer background_pause_timer_;
 
   // Monitors the watch time of the played content.
@@ -620,17 +624,18 @@
   int underflow_count_;
   std::unique_ptr<base::ElapsedTimer> underflow_timer_;
 
-  // The last time didLoadingProgress() returned true.
-  base::TimeTicks last_time_loading_progressed_;
+  // Used to track loading progress, used by IsPrerollAttemptNeeded().
+  // |preroll_attempt_pending_| indicates that the clock has been reset
+  // (awaiting a resume to start), while |preroll_attempt_start_time_| tracks
+  // when a preroll attempt began.
+  bool preroll_attempt_pending_;
+  base::TimeTicks preroll_attempt_start_time_;
 
   std::unique_ptr<base::TickClock> tick_clock_;
 
   // Monitors the player events.
   base::WeakPtr<MediaObserver> observer_;
 
-  // Whether the player is currently in autoplay muted state.
-  bool autoplay_muted_ = false;
-
   // The maximum video keyframe distance that allows triggering background
   // playback optimizations.
   // 10 seconds by default but can be overridden by a Finch experiment.
diff --git a/media/blink/webmediaplayer_impl_unittest.cc b/media/blink/webmediaplayer_impl_unittest.cc
index a58b32b..b0fbb40 100644
--- a/media/blink/webmediaplayer_impl_unittest.cc
+++ b/media/blink/webmediaplayer_impl_unittest.cc
@@ -111,13 +111,75 @@
   ~MockWebMediaPlayerDelegate() = default;
 
   // WebMediaPlayerDelegate implementation.
-  MOCK_METHOD1(AddObserver, int(Observer*));
-  MOCK_METHOD1(RemoveObserver, void(int));
-  MOCK_METHOD5(DidPlay, void(int, bool, bool, bool, MediaContentType));
-  MOCK_METHOD2(DidPause, void(int, bool));
+  int AddObserver(Observer* observer) override {
+    DCHECK_EQ(nullptr, observer_);
+    observer_ = observer;
+    return player_id_;
+  }
+
+  void RemoveObserver(int player_id) override {
+    DCHECK_EQ(player_id_, player_id);
+    observer_ = nullptr;
+  }
+
+  MOCK_METHOD4(DidPlay, void(int, bool, bool, MediaContentType));
+  MOCK_METHOD1(DidPause, void(int));
   MOCK_METHOD1(PlayerGone, void(int));
-  MOCK_METHOD0(IsHidden, bool());
-  MOCK_METHOD0(IsPlayingBackgroundVideo, bool());
+  MOCK_METHOD0(IsBackgroundVideoPlaybackUnlocked, bool());
+
+  void SetIdle(int player_id, bool is_idle) override {
+    DCHECK_EQ(player_id_, player_id);
+    is_idle_ = is_idle;
+    is_stale_ &= is_idle;
+  }
+
+  bool IsIdle(int player_id) override {
+    DCHECK_EQ(player_id_, player_id);
+    return is_idle_;
+  }
+
+  void ClearStaleFlag(int player_id) override {
+    DCHECK_EQ(player_id_, player_id);
+    is_stale_ = false;
+  }
+
+  bool IsStale(int player_id) override {
+    DCHECK_EQ(player_id_, player_id);
+    return is_stale_;
+  }
+
+  bool IsFrameHidden() override { return is_hidden_; }
+
+  bool IsFrameClosed() override { return is_closed_; }
+
+  void SetIdleForTesting(bool is_idle) { is_idle_ = is_idle; }
+
+  void SetStaleForTesting(bool is_stale) {
+    is_idle_ |= is_stale;
+    is_stale_ = is_stale;
+  }
+
+  // Returns true if the player does in fact expire.
+  bool ExpireForTesting() {
+    if (is_idle_ && !is_stale_) {
+      is_stale_ = true;
+      observer_->OnIdleTimeout();
+    }
+
+    return is_stale_;
+  }
+
+  void SetFrameHiddenForTesting(bool is_hidden) { is_hidden_ = is_hidden; }
+
+  void SetFrameClosedForTesting(bool is_closed) { is_closed_ = is_closed; }
+
+ private:
+  Observer* observer_ = nullptr;
+  int player_id_ = 1234;
+  bool is_idle_ = false;
+  bool is_stale_ = false;
+  bool is_hidden_ = false;
+  bool is_closed_ = false;
 };
 
 class WebMediaPlayerImplTest : public testing::Test {
@@ -189,74 +251,33 @@
   }
 
   WebMediaPlayerImpl::PlayState ComputePlayState() {
-    wmpi_->is_idle_ = false;
-    wmpi_->must_suspend_ = false;
     return wmpi_->UpdatePlayState_ComputePlayState(false, false, false, false);
   }
 
-  WebMediaPlayerImpl::PlayState ComputePlayStateSuspended() {
-    wmpi_->is_idle_ = false;
-    wmpi_->must_suspend_ = false;
-    return wmpi_->UpdatePlayState_ComputePlayState(false, false, true, false);
-  }
-
-  WebMediaPlayerImpl::PlayState ComputeBackgroundedPlayState() {
-    wmpi_->is_idle_ = false;
-    wmpi_->must_suspend_ = false;
+  WebMediaPlayerImpl::PlayState ComputePlayState_FrameHidden() {
     return wmpi_->UpdatePlayState_ComputePlayState(false, false, false, true);
   }
 
-  WebMediaPlayerImpl::PlayState ComputeIdlePlayState() {
-    wmpi_->is_idle_ = true;
-    wmpi_->must_suspend_ = false;
-    return wmpi_->UpdatePlayState_ComputePlayState(false, false, false, false);
-  }
-
-  WebMediaPlayerImpl::PlayState ComputeIdleSuspendedPlayState() {
-    wmpi_->is_idle_ = true;
-    wmpi_->must_suspend_ = false;
+  WebMediaPlayerImpl::PlayState ComputePlayState_Suspended() {
     return wmpi_->UpdatePlayState_ComputePlayState(false, false, true, false);
   }
 
-  WebMediaPlayerImpl::PlayState ComputeMustSuspendPlayState() {
-    wmpi_->is_idle_ = false;
-    wmpi_->must_suspend_ = true;
-    return wmpi_->UpdatePlayState_ComputePlayState(false, false, false, false);
+  WebMediaPlayerImpl::PlayState ComputePlayState_Remote() {
+    return wmpi_->UpdatePlayState_ComputePlayState(true, false, false, false);
   }
 
-  WebMediaPlayerImpl::PlayState ComputeStreamingPlayState(bool must_suspend) {
-    wmpi_->is_idle_ = true;
-    wmpi_->must_suspend_ = must_suspend;
+  WebMediaPlayerImpl::PlayState ComputePlayState_BackgroundedStreaming() {
     return wmpi_->UpdatePlayState_ComputePlayState(false, true, false, true);
   }
 
-  void SetDelegateState(WebMediaPlayerImpl::DelegateState state) {
-    wmpi_->SetDelegateState(state);
-  }
-
   bool IsSuspended() { return wmpi_->pipeline_controller_.IsSuspended(); }
 
   void AddBufferedRanges() {
     wmpi_->buffered_data_source_host_.AddBufferedByteRange(0, 1);
   }
 
-  void SetupForResumingBackgroundVideo() {
-#if !defined(OS_ANDROID)
-    // Need to enable media suspend to test resuming background videos.
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        switches::kEnableMediaSuspend);
-#endif  // !defined(OS_ANDROID)
-    scoped_feature_list_.InitAndEnableFeature(kResumeBackgroundVideo);
-  }
-
-  void SetBackgroundVideoOptimization(bool enable) {
-    if (enable) {
-      scoped_feature_list_.InitAndEnableFeature(
-          kBackgroundVideoTrackOptimization);
-    } else {
-      scoped_feature_list_.InitAndDisableFeature(
-          kBackgroundVideoTrackOptimization);
-    }
+  void SetDelegateState(WebMediaPlayerImpl::DelegateState state) {
+    wmpi_->SetDelegateState(state, false);
   }
 
   bool ShouldDisableVideoWhenHidden() const {
@@ -296,19 +317,17 @@
   // The WebMediaPlayerImpl instance under test.
   std::unique_ptr<WebMediaPlayerImpl> wmpi_;
 
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
   DISALLOW_COPY_AND_ASSIGN(WebMediaPlayerImplTest);
 };
 
 TEST_F(WebMediaPlayerImplTest, ConstructAndDestroy) {
   InitializeWebMediaPlayerImpl();
+  EXPECT_FALSE(IsSuspended());
 }
 
 TEST_F(WebMediaPlayerImplTest, IdleSuspendIsEnabledBeforeLoadingBegins) {
   InitializeWebMediaPlayerImpl();
-  wmpi_->OnSuspendRequested(false);
+  EXPECT_TRUE(delegate_.ExpireForTesting());
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(IsSuspended());
 }
@@ -323,7 +342,7 @@
   wmpi_->didLoadingProgress();
   // Advance less than the loading timeout.
   clock->Advance(base::TimeDelta::FromSeconds(1));
-  wmpi_->OnSuspendRequested(false);
+  EXPECT_FALSE(delegate_.ExpireForTesting());
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(IsSuspended());
 }
@@ -337,344 +356,287 @@
   wmpi_->didLoadingProgress();
   // Advance more than the loading timeout.
   clock->Advance(base::TimeDelta::FromSeconds(4));
-  wmpi_->OnSuspendRequested(false);
+  EXPECT_TRUE(delegate_.ExpireForTesting());
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(IsSuspended());
 }
 
 TEST_F(WebMediaPlayerImplTest, DidLoadingProgressTriggersResume) {
+  // Same setup as IdleSuspendIsEnabledBeforeLoadingBegins.
   InitializeWebMediaPlayerImpl();
-  EXPECT_FALSE(IsSuspended());
-  wmpi_->OnSuspendRequested(false);
+  EXPECT_TRUE(delegate_.ExpireForTesting());
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(IsSuspended());
+
+  // Like IdleSuspendIsDisabledIfLoadingProgressedRecently, the idle timeout
+  // should be rejected if it hasn't been long enough.
   AddBufferedRanges();
   wmpi_->didLoadingProgress();
+  EXPECT_FALSE(delegate_.ExpireForTesting());
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(IsSuspended());
 }
 
-TEST_F(WebMediaPlayerImplTest, ComputePlayState_AfterConstruction) {
+TEST_F(WebMediaPlayerImplTest, ComputePlayState_Constructed) {
   InitializeWebMediaPlayerImpl();
-  WebMediaPlayerImpl::PlayState state;
-
-  state = ComputePlayState();
+  WebMediaPlayerImpl::PlayState state = ComputePlayState();
   EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
+  EXPECT_TRUE(state.is_idle);
   EXPECT_FALSE(state.is_suspended);
-
-  state = ComputeIdlePlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
   EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
-
-  state = ComputeBackgroundedPlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_FALSE(state.is_suspended);
-
-  state = ComputeMustSuspendPlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
 }
 
-TEST_F(WebMediaPlayerImplTest, ComputePlayState_AfterMetadata) {
+TEST_F(WebMediaPlayerImplTest, ComputePlayState_HaveMetadata) {
   InitializeWebMediaPlayerImpl();
-  WebMediaPlayerImpl::PlayState state;
   SetMetadata(true, true);
-
-  state = ComputePlayState();
+  WebMediaPlayerImpl::PlayState state = ComputePlayState();
   EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
+  EXPECT_TRUE(state.is_idle);
   EXPECT_FALSE(state.is_suspended);
-
-  state = ComputeIdlePlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
   EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
-
-  state = ComputeBackgroundedPlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
-
-  state = ComputeMustSuspendPlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
 }
 
-TEST_F(WebMediaPlayerImplTest, ComputePlayState_AfterMetadata_AudioOnly) {
+TEST_F(WebMediaPlayerImplTest, ComputePlayState_HaveFutureData) {
   InitializeWebMediaPlayerImpl();
-  WebMediaPlayerImpl::PlayState state;
-  SetMetadata(true, false);
-
-  state = ComputePlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_FALSE(state.is_suspended);
-
-  state = ComputeIdlePlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
-
-  SetPaused(false);
-  state = ComputeBackgroundedPlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_FALSE(state.is_suspended);
-
-  state = ComputeMustSuspendPlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
-}
-
-TEST_F(WebMediaPlayerImplTest, ComputePlayState_AfterFutureData) {
-  InitializeWebMediaPlayerImpl();
-  WebMediaPlayerImpl::PlayState state;
   SetMetadata(true, true);
   SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
-
-  state = ComputePlayState();
+  WebMediaPlayerImpl::PlayState state = ComputePlayState();
   EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PAUSED, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
+  EXPECT_TRUE(state.is_idle);
   EXPECT_FALSE(state.is_suspended);
-
-  state = ComputeBackgroundedPlayState();
-
-  if (base::FeatureList::IsEnabled(kResumeBackgroundVideo))
-    EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PAUSED, state.delegate_state);
-  else
-    EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
   EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
-
-  // Idle suspension is possible after HaveFutureData.
-  state = ComputeIdlePlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PAUSED, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
-
-  state = ComputeMustSuspendPlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
 }
 
 TEST_F(WebMediaPlayerImplTest, ComputePlayState_Playing) {
   InitializeWebMediaPlayerImpl();
-  WebMediaPlayerImpl::PlayState state;
   SetMetadata(true, true);
   SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
   SetPaused(false);
-
-  state = ComputePlayState();
+  WebMediaPlayerImpl::PlayState state = ComputePlayState();
   EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PLAYING, state.delegate_state);
-  EXPECT_TRUE(state.is_memory_reporting_enabled);
+  EXPECT_FALSE(state.is_idle);
   EXPECT_FALSE(state.is_suspended);
-
-  state = ComputeBackgroundedPlayState();
-  if (base::FeatureList::IsEnabled(kResumeBackgroundVideo))
-    EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PAUSED, state.delegate_state);
-  else
-    EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
-
-  state = ComputeMustSuspendPlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
+  EXPECT_TRUE(state.is_memory_reporting_enabled);
 }
 
-TEST_F(WebMediaPlayerImplTest, ComputePlayState_PlayingThenUnderflow) {
+TEST_F(WebMediaPlayerImplTest, ComputePlayState_Underflow) {
   InitializeWebMediaPlayerImpl();
-  WebMediaPlayerImpl::PlayState state;
   SetMetadata(true, true);
   SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
   SetPaused(false);
   SetReadyState(blink::WebMediaPlayer::ReadyStateHaveCurrentData);
-
-  // Underflow should not trigger idle suspend. The user is still playing the
-  // the video, just waiting on the network.
-  state = ComputePlayState();
+  WebMediaPlayerImpl::PlayState state = ComputePlayState();
   EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PLAYING, state.delegate_state);
-  EXPECT_TRUE(state.is_memory_reporting_enabled);
+  EXPECT_FALSE(state.is_idle);
   EXPECT_FALSE(state.is_suspended);
-
-  // Background suspend should still be possible during underflow.
-  state = ComputeBackgroundedPlayState();
-  if (base::FeatureList::IsEnabled(kResumeBackgroundVideo))
-    EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PAUSED, state.delegate_state);
-  else
-    EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
-
-  // Forced suspend should still be possible during underflow.
-  state = ComputeMustSuspendPlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
+  EXPECT_TRUE(state.is_memory_reporting_enabled);
 }
 
-TEST_F(WebMediaPlayerImplTest, ComputePlayState_Playing_AudioOnly) {
+TEST_F(WebMediaPlayerImplTest, ComputePlayState_FrameHidden) {
   InitializeWebMediaPlayerImpl();
-  WebMediaPlayerImpl::PlayState state;
-  SetMetadata(true, false);
+  SetMetadata(true, true);
   SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
   SetPaused(false);
 
-  state = ComputePlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PLAYING, state.delegate_state);
-  EXPECT_TRUE(state.is_memory_reporting_enabled);
-  EXPECT_FALSE(state.is_suspended);
+  {
+    base::test::ScopedFeatureList scoped_feature_list;
+    scoped_feature_list.InitAndEnableFeature(kResumeBackgroundVideo);
 
-  // Audio-only stays playing in the background.
-  state = ComputeBackgroundedPlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PLAYING, state.delegate_state);
-  EXPECT_TRUE(state.is_memory_reporting_enabled);
-  EXPECT_FALSE(state.is_suspended);
+    WebMediaPlayerImpl::PlayState state = ComputePlayState_FrameHidden();
+    EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PAUSED, state.delegate_state);
+    EXPECT_TRUE(state.is_idle);
+    EXPECT_TRUE(state.is_suspended);
+    EXPECT_FALSE(state.is_memory_reporting_enabled);
+  }
 
-  // Backgrounding a paused audio only player should suspend, but keep the
-  // session alive for user interactions.
-  SetPaused(true);
-  state = ComputeBackgroundedPlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PAUSED, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
+  {
+    base::test::ScopedFeatureList scoped_feature_list;
+    scoped_feature_list.InitAndDisableFeature(kResumeBackgroundVideo);
 
-  state = ComputeMustSuspendPlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
+    WebMediaPlayerImpl::PlayState state = ComputePlayState_FrameHidden();
+    EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
+    EXPECT_TRUE(state.is_idle);
+    EXPECT_TRUE(state.is_suspended);
+    EXPECT_FALSE(state.is_memory_reporting_enabled);
+  }
 }
 
-TEST_F(WebMediaPlayerImplTest, ComputePlayState_Paused_Seek) {
+TEST_F(WebMediaPlayerImplTest, ComputePlayState_FrameClosed) {
   InitializeWebMediaPlayerImpl();
-  WebMediaPlayerImpl::PlayState state;
+  SetMetadata(true, true);
+  SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
+  SetPaused(false);
+  delegate_.SetFrameClosedForTesting(true);
+  WebMediaPlayerImpl::PlayState state = ComputePlayState();
+  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
+  EXPECT_TRUE(state.is_idle);
+  EXPECT_TRUE(state.is_suspended);
+  EXPECT_FALSE(state.is_memory_reporting_enabled);
+}
+
+TEST_F(WebMediaPlayerImplTest, ComputePlayState_PausedSeek) {
+  InitializeWebMediaPlayerImpl();
   SetMetadata(true, true);
   SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
   SetSeeking(true);
-
-  state = ComputePlayState();
+  WebMediaPlayerImpl::PlayState state = ComputePlayState();
   EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PAUSED, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
+  EXPECT_FALSE(state.is_idle);
   EXPECT_FALSE(state.is_suspended);
-}
-
-TEST_F(WebMediaPlayerImplTest, ComputePlayState_Paused_Fullscreen) {
-  InitializeWebMediaPlayerImpl();
-  WebMediaPlayerImpl::PlayState state;
-  SetMetadata(true, true);
-  SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
-  SetFullscreen(true);
-
-  state = ComputePlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PAUSED, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_FALSE(state.is_suspended);
+  EXPECT_TRUE(state.is_memory_reporting_enabled);
 }
 
 TEST_F(WebMediaPlayerImplTest, ComputePlayState_Ended) {
   InitializeWebMediaPlayerImpl();
-  WebMediaPlayerImpl::PlayState state;
   SetMetadata(true, true);
   SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
+  SetPaused(false);
   SetEnded(true);
 
-  // The pipeline is not suspended immediately on ended.
+  // Before Blink pauses us (or seeks for looping content), the media session
+  // should be preserved.
+  WebMediaPlayerImpl::PlayState state;
   state = ComputePlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::ENDED, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
+  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PLAYING, state.delegate_state);
+  EXPECT_FALSE(state.is_idle);
   EXPECT_FALSE(state.is_suspended);
+  EXPECT_TRUE(state.is_memory_reporting_enabled);
 
-  state = ComputeIdlePlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::ENDED, state.delegate_state);
+  SetPaused(true);
+  state = ComputePlayState();
+  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
+  EXPECT_TRUE(state.is_idle);
+  EXPECT_FALSE(state.is_suspended);
   EXPECT_FALSE(state.is_memory_reporting_enabled);
+}
+
+TEST_F(WebMediaPlayerImplTest, ComputePlayState_StaysSuspended) {
+  InitializeWebMediaPlayerImpl();
+  SetMetadata(true, true);
+  SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
+
+  // Should stay suspended even though not stale or backgrounded.
+  WebMediaPlayerImpl::PlayState state = ComputePlayState_Suspended();
+  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PAUSED, state.delegate_state);
+  EXPECT_TRUE(state.is_idle);
   EXPECT_TRUE(state.is_suspended);
+  EXPECT_FALSE(state.is_memory_reporting_enabled);
+}
+
+TEST_F(WebMediaPlayerImplTest, ComputePlayState_Remote) {
+  InitializeWebMediaPlayerImpl();
+  SetMetadata(true, true);
+  SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
+
+  // Remote media is always suspended.
+  // TODO(sandersd): Decide whether this should count as idle or not.
+  WebMediaPlayerImpl::PlayState state = ComputePlayState_Remote();
+  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
+  EXPECT_TRUE(state.is_suspended);
+  EXPECT_FALSE(state.is_memory_reporting_enabled);
+}
+
+TEST_F(WebMediaPlayerImplTest, ComputePlayState_Fullscreen) {
+  InitializeWebMediaPlayerImpl();
+  SetMetadata(true, true);
+  SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
+  SetFullscreen(true);
+  SetPaused(true);
+  delegate_.SetStaleForTesting(true);
+
+  // Fullscreen media is never suspended (Android only behavior).
+  WebMediaPlayerImpl::PlayState state = ComputePlayState();
+  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PAUSED, state.delegate_state);
+  EXPECT_TRUE(state.is_idle);
+  EXPECT_FALSE(state.is_suspended);
+  EXPECT_FALSE(state.is_memory_reporting_enabled);
 }
 
 TEST_F(WebMediaPlayerImplTest, ComputePlayState_Streaming) {
   InitializeWebMediaPlayerImpl();
-  WebMediaPlayerImpl::PlayState state;
   SetMetadata(true, true);
-
   SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
   SetPaused(true);
+  delegate_.SetStaleForTesting(true);
 
-  // Streaming media should not suspend, even if paused, idle, and backgrounded.
-  state = ComputeStreamingPlayState(false);
+  // Streaming media should not suspend, even if paused, stale, and
+  // backgrounded.
+  WebMediaPlayerImpl::PlayState state;
+  state = ComputePlayState_BackgroundedStreaming();
   EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PAUSED, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
+  EXPECT_TRUE(state.is_idle);
   EXPECT_FALSE(state.is_suspended);
+  EXPECT_FALSE(state.is_memory_reporting_enabled);
 
   // Streaming media should suspend when the tab is closed, regardless.
-  state = ComputeStreamingPlayState(true);
+  delegate_.SetFrameClosedForTesting(true);
+  state = ComputePlayState_BackgroundedStreaming();
   EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
+  EXPECT_TRUE(state.is_idle);
   EXPECT_TRUE(state.is_suspended);
+  EXPECT_FALSE(state.is_memory_reporting_enabled);
 }
 
-TEST_F(WebMediaPlayerImplTest, ComputePlayState_Suspended) {
+TEST_F(WebMediaPlayerImplTest, ComputePlayState_PlayingBackgroundedVideo) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(kResumeBackgroundVideo);
+
   InitializeWebMediaPlayerImpl();
-  WebMediaPlayerImpl::PlayState state;
   SetMetadata(true, true);
-
-  // Suspended players should be resumed unless we have reached the appropriate
-  // ready state and are not seeking.
-  SetPaused(true);
-  state = ComputePlayStateSuspended();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_FALSE(state.is_suspended);
-
-  // Paused players in the idle state are allowed to remain suspended.
-  state = ComputeIdleSuspendedPlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
-
-  SetPaused(false);
-  state = ComputePlayStateSuspended();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::GONE, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_FALSE(state.is_suspended);
-
   SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
-
-  // Paused players should stay suspended.
-  SetPaused(true);
-  state = ComputePlayStateSuspended();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PAUSED, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
-
-  // Playing players should resume into the playing state.
   SetPaused(false);
-  state = ComputePlayStateSuspended();
+  EXPECT_CALL(delegate_, IsBackgroundVideoPlaybackUnlocked())
+      .WillRepeatedly(Return(true));
+
+  WebMediaPlayerImpl::PlayState state = ComputePlayState_FrameHidden();
   EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PLAYING, state.delegate_state);
+  EXPECT_FALSE(state.is_idle);
+  EXPECT_FALSE(state.is_suspended);
   EXPECT_TRUE(state.is_memory_reporting_enabled);
-  EXPECT_FALSE(state.is_suspended);
+}
 
-  // If seeking, the previously suspended state does not matter; the player
-  // should always be resumed.
-  SetSeeking(true);
-
-  SetPaused(true);
-  state = ComputePlayStateSuspended();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PAUSED, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_FALSE(state.is_suspended);
-
+TEST_F(WebMediaPlayerImplTest, ComputePlayState_AudioOnly) {
+  InitializeWebMediaPlayerImpl();
+  SetMetadata(true, false);
+  SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
   SetPaused(false);
-  state = ComputePlayStateSuspended();
+
+  // Backgrounded audio-only playback stays playing.
+  WebMediaPlayerImpl::PlayState state = ComputePlayState_FrameHidden();
   EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PLAYING, state.delegate_state);
-  EXPECT_TRUE(state.is_memory_reporting_enabled);
+  EXPECT_FALSE(state.is_idle);
   EXPECT_FALSE(state.is_suspended);
+  EXPECT_TRUE(state.is_memory_reporting_enabled);
+}
+
+TEST_F(WebMediaPlayerImplTest, AutoplayMuted_StartsAndStops) {
+  InitializeWebMediaPlayerImpl();
+  SetMetadata(true, true);
+  SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
+  SetPaused(false);
+  client_.set_is_autoplaying_muted(true);
+
+  EXPECT_CALL(delegate_, DidPlay(_, true, false, _));
+  SetDelegateState(WebMediaPlayerImpl::DelegateState::PLAYING);
+
+  client_.set_is_autoplaying_muted(false);
+  EXPECT_CALL(delegate_, DidPlay(_, true, true, _));
+  SetDelegateState(WebMediaPlayerImpl::DelegateState::PLAYING);
+}
+
+TEST_F(WebMediaPlayerImplTest, AutoplayMuted_SetVolume) {
+  InitializeWebMediaPlayerImpl();
+  SetMetadata(true, true);
+  SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
+  SetPaused(false);
+  client_.set_is_autoplaying_muted(true);
+
+  EXPECT_CALL(delegate_, DidPlay(_, true, false, _));
+  SetDelegateState(WebMediaPlayerImpl::DelegateState::PLAYING);
+
+  client_.set_is_autoplaying_muted(false);
+  EXPECT_CALL(delegate_, DidPlay(_, true, true, _));
+  wmpi_->setVolume(1.0);
 }
 
 TEST_F(WebMediaPlayerImplTest, NaturalSizeChange) {
@@ -706,79 +668,12 @@
   ASSERT_EQ(blink::WebSize(1080, 1920), wmpi_->naturalSize());
 }
 
-// Audible backgrounded videos are not suspended if delegate_ allows it.
-TEST_F(WebMediaPlayerImplTest, ComputePlayState_BackgroundedVideoPlaying) {
-  InitializeWebMediaPlayerImpl();
-  WebMediaPlayerImpl::PlayState state;
-  SetMetadata(true, true);
-  SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
-
-  SetupForResumingBackgroundVideo();
-
-  EXPECT_CALL(delegate_, IsPlayingBackgroundVideo())
-      .WillRepeatedly(Return(true));
-  EXPECT_CALL(delegate_, IsHidden()).WillRepeatedly(Return(true));
-
-  SetPaused(false);
-  state = ComputeBackgroundedPlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PLAYING, state.delegate_state);
-  EXPECT_TRUE(state.is_memory_reporting_enabled);
-  EXPECT_FALSE(state.is_suspended);
-}
-
-// Backgrounding audible videos should suspend them and report as paused, not
-// gone.
-TEST_F(WebMediaPlayerImplTest, ComputePlayState_BackgroundedVideoPaused) {
-  InitializeWebMediaPlayerImpl();
-  WebMediaPlayerImpl::PlayState state;
-  SetMetadata(true, true);
-  SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
-
-  SetupForResumingBackgroundVideo();
-
-  EXPECT_CALL(delegate_, IsPlayingBackgroundVideo()).WillOnce(Return(false));
-  EXPECT_CALL(delegate_, IsHidden()).WillRepeatedly(Return(true));
-
-  state = ComputeBackgroundedPlayState();
-  EXPECT_EQ(WebMediaPlayerImpl::DelegateState::PAUSED, state.delegate_state);
-  EXPECT_FALSE(state.is_memory_reporting_enabled);
-  EXPECT_TRUE(state.is_suspended);
-}
-
-TEST_F(WebMediaPlayerImplTest, AutoplayMuted_StartsAndStops) {
-  InitializeWebMediaPlayerImpl();
-  SetMetadata(true, true);
-  SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
-  SetPaused(false);
-
-  EXPECT_CALL(delegate_, DidPlay(_, true, false, false, _));
-  client_.set_is_autoplaying_muted(true);
-  SetDelegateState(WebMediaPlayerImpl::DelegateState::PLAYING);
-
-  EXPECT_CALL(delegate_, DidPlay(_, true, true, false, _));
-  client_.set_is_autoplaying_muted(false);
-  SetDelegateState(WebMediaPlayerImpl::DelegateState::PLAYING);
-}
-
-TEST_F(WebMediaPlayerImplTest, AutoplayMuted_SetVolume) {
-  InitializeWebMediaPlayerImpl();
-  SetMetadata(true, true);
-  SetReadyState(blink::WebMediaPlayer::ReadyStateHaveFutureData);
-  SetPaused(false);
-
-  EXPECT_CALL(delegate_, DidPlay(_, true, false, false, _));
-  client_.set_is_autoplaying_muted(true);
-  SetDelegateState(WebMediaPlayerImpl::DelegateState::PLAYING);
-
-  EXPECT_CALL(delegate_, DidPlay(_, true, true, false, _));
-  client_.set_is_autoplaying_muted(false);
-  wmpi_->setVolume(1.0);
-}
-
 TEST_F(WebMediaPlayerImplTest, ShouldDisableVideoWhenHidden) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(kBackgroundVideoTrackOptimization);
+
   InitializeWebMediaPlayerImpl();
-  EXPECT_CALL(delegate_, IsHidden()).WillRepeatedly(Return(true));
-  SetBackgroundVideoOptimization(true);
+  delegate_.SetFrameHiddenForTesting(true);
 
   SetMetadata(true, true);
   SetVideoKeyframeDistanceAverage(base::TimeDelta::FromSeconds(5));
@@ -796,9 +691,11 @@
 }
 
 TEST_F(WebMediaPlayerImplTest, ShouldDisableVideoWhenHiddenFeatureDisabled) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(kBackgroundVideoTrackOptimization);
+
   InitializeWebMediaPlayerImpl();
-  EXPECT_CALL(delegate_, IsHidden()).WillRepeatedly(Return(true));
-  SetBackgroundVideoOptimization(false);
+  delegate_.SetFrameHiddenForTesting(true);
 
   SetMetadata(true, true);
   EXPECT_FALSE(ShouldDisableVideoWhenHidden());
diff --git a/media/ffmpeg/ffmpeg_regression_tests.cc b/media/ffmpeg/ffmpeg_regression_tests.cc
index 1c0b0a5..36a16ba 100644
--- a/media/ffmpeg/ffmpeg_regression_tests.cc
+++ b/media/ffmpeg/ffmpeg_regression_tests.cc
@@ -101,7 +101,10 @@
                  "security/112384.webm",
                  DEMUXER_ERROR_COULD_NOT_PARSE,
                  DEMUXER_ERROR_COULD_NOT_PARSE);
-FFMPEG_TEST_CASE(Cr112976, "security/112976.ogg", PIPELINE_OK, PIPELINE_OK);
+FFMPEG_TEST_CASE(Cr112976,
+                 "security/112976.ogg",
+                 PIPELINE_OK,
+                 DEMUXER_ERROR_COULD_NOT_PARSE);
 FFMPEG_TEST_CASE(Cr116927,
                  "security/116927.ogv",
                  DEMUXER_ERROR_NO_SUPPORTED_STREAMS,
@@ -110,17 +113,26 @@
                  "security/117912.webm",
                  DEMUXER_ERROR_COULD_NOT_OPEN,
                  DEMUXER_ERROR_COULD_NOT_OPEN);
-FFMPEG_TEST_CASE(Cr123481, "security/123481.ogv", PIPELINE_OK, PIPELINE_OK);
+FFMPEG_TEST_CASE(Cr123481,
+                 "security/123481.ogv",
+                 PIPELINE_OK,
+                 DEMUXER_ERROR_COULD_NOT_PARSE);
 FFMPEG_TEST_CASE(Cr132779,
                  "security/132779.webm",
                  DEMUXER_ERROR_COULD_NOT_PARSE,
                  DEMUXER_ERROR_COULD_NOT_PARSE);
-FFMPEG_TEST_CASE(Cr140165, "security/140165.ogg", PIPELINE_OK, PIPELINE_OK);
+FFMPEG_TEST_CASE(Cr140165,
+                 "security/140165.ogg",
+                 PIPELINE_OK,
+                 DEMUXER_ERROR_COULD_NOT_PARSE);
 FFMPEG_TEST_CASE(Cr140647,
                  "security/140647.ogv",
                  DEMUXER_ERROR_COULD_NOT_OPEN,
                  DEMUXER_ERROR_COULD_NOT_OPEN);
-FFMPEG_TEST_CASE(Cr142738, "crbug142738.ogg", PIPELINE_OK, PIPELINE_OK);
+FFMPEG_TEST_CASE(Cr142738,
+                 "crbug142738.ogg",
+                 PIPELINE_OK,
+                 DEMUXER_ERROR_COULD_NOT_PARSE);
 FFMPEG_TEST_CASE(Cr152691,
                  "security/152691.mp3",
                  PIPELINE_OK,
diff --git a/media/filters/audio_decoder_unittest.cc b/media/filters/audio_decoder_unittest.cc
index cfaf4b8a..12c86e35 100644
--- a/media/filters/audio_decoder_unittest.cc
+++ b/media/filters/audio_decoder_unittest.cc
@@ -380,8 +380,6 @@
   DISALLOW_COPY_AND_ASSIGN(AudioDecoderTest);
 };
 
-class FFmpegAudioDecoderBehavioralTest : public AudioDecoderTest {};
-
 TEST_P(AudioDecoderTest, Initialize) {
   SKIP_TEST_IF_NO_MEDIA_CODEC();
   ASSERT_NO_FATAL_FAILURE(Initialize());
@@ -483,7 +481,7 @@
 #endif
 };
 
-INSTANTIATE_TEST_CASE_P(MediaCodecAudioDecoderTest,
+INSTANTIATE_TEST_CASE_P(MediaCodec,
                         AudioDecoderTest,
                         testing::ValuesIn(kMediaCodecTests));
 #endif  // defined(OS_ANDROID)
@@ -571,16 +569,8 @@
      CHANNEL_LAYOUT_STEREO},
 };
 
-// Dummy data for behavioral tests.
-const DecoderTestData kFFmpegBehavioralTest[] = {
-    {FFMPEG, kUnknownAudioCodec, "", NULL, 0, 0, CHANNEL_LAYOUT_NONE},
-};
-
-INSTANTIATE_TEST_CASE_P(FFmpegAudioDecoderTest,
+INSTANTIATE_TEST_CASE_P(FFmpeg,
                         AudioDecoderTest,
                         testing::ValuesIn(kFFmpegTests));
-INSTANTIATE_TEST_CASE_P(FFmpegAudioDecoderBehavioralTest,
-                        FFmpegAudioDecoderBehavioralTest,
-                        testing::ValuesIn(kFFmpegBehavioralTest));
 
 }  // namespace media
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index 218989c..7d1fcea 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -475,59 +475,59 @@
   const base::TimeDelta stream_timestamp =
       ConvertStreamTimestamp(stream_->time_base, packet->pts);
 
-  if (stream_timestamp != kNoTimestamp) {
-    const bool is_audio = type() == AUDIO;
+  if (stream_timestamp == kNoTimestamp) {
+    demuxer_->NotifyDemuxerError(DEMUXER_ERROR_COULD_NOT_PARSE);
+    return;
+  }
 
-    // If this file has negative timestamps don't rebase any other stream types
-    // against the negative starting time.
-    base::TimeDelta start_time = demuxer_->start_time();
-    if (fixup_negative_timestamps_ && !is_audio &&
-        start_time < base::TimeDelta()) {
-      start_time = base::TimeDelta();
-    }
+  const bool is_audio = type() == AUDIO;
 
-    // Don't rebase timestamps for positive start times, the HTML Media Spec
-    // details this in section "4.8.10.6 Offsets into the media resource." We
-    // will still need to rebase timestamps before seeking with FFmpeg though.
-    if (start_time > base::TimeDelta())
-      start_time = base::TimeDelta();
+  // If this file has negative timestamps don't rebase any other stream types
+  // against the negative starting time.
+  base::TimeDelta start_time = demuxer_->start_time();
+  if (fixup_negative_timestamps_ && !is_audio &&
+      start_time < base::TimeDelta()) {
+    start_time = base::TimeDelta();
+  }
 
-    buffer->set_timestamp(stream_timestamp - start_time);
+  // Don't rebase timestamps for positive start times, the HTML Media Spec
+  // details this in section "4.8.10.6 Offsets into the media resource." We
+  // will still need to rebase timestamps before seeking with FFmpeg though.
+  if (start_time > base::TimeDelta())
+    start_time = base::TimeDelta();
 
-    // If enabled, and no codec delay is present, mark audio packets with
-    // negative timestamps for post-decode discard.
-    if (fixup_negative_timestamps_ && is_audio &&
-        stream_timestamp < base::TimeDelta() &&
-        buffer->duration() != kNoTimestamp) {
-      if (!audio_decoder_config().codec_delay()) {
-        DCHECK_EQ(buffer->discard_padding().first, base::TimeDelta());
+  buffer->set_timestamp(stream_timestamp - start_time);
 
-        if (stream_timestamp + buffer->duration() < base::TimeDelta()) {
-          DCHECK_EQ(buffer->discard_padding().second, base::TimeDelta());
+  // If enabled, and no codec delay is present, mark audio packets with
+  // negative timestamps for post-decode discard.
+  if (fixup_negative_timestamps_ && is_audio &&
+      stream_timestamp < base::TimeDelta() &&
+      buffer->duration() != kNoTimestamp) {
+    if (!audio_decoder_config().codec_delay()) {
+      DCHECK_EQ(buffer->discard_padding().first, base::TimeDelta());
 
-          // Discard the entire packet if it's entirely before zero.
-          buffer->set_discard_padding(
-              std::make_pair(kInfiniteDuration, base::TimeDelta()));
-        } else {
-          // Only discard part of the frame if it overlaps zero.
-          buffer->set_discard_padding(std::make_pair(
-              -stream_timestamp, buffer->discard_padding().second));
-        }
+      if (stream_timestamp + buffer->duration() < base::TimeDelta()) {
+        DCHECK_EQ(buffer->discard_padding().second, base::TimeDelta());
+
+        // Discard the entire packet if it's entirely before zero.
+        buffer->set_discard_padding(
+            std::make_pair(kInfiniteDuration, base::TimeDelta()));
       } else {
-        // Verify that codec delay would cover discard and that we don't need to
-        // mark the packet for post decode discard.  Since timestamps may be in
-        // milliseconds and codec delay in nanosecond precision, round up to the
-        // nearest millisecond.  See enable_negative_timestamp_fixups().
-        DCHECK_LE(-std::ceil(FramesToTimeDelta(
-                                 audio_decoder_config().codec_delay(),
-                                 audio_decoder_config().samples_per_second())
-                                 .InMillisecondsF()),
-                  stream_timestamp.InMillisecondsF());
+        // Only discard part of the frame if it overlaps zero.
+        buffer->set_discard_padding(std::make_pair(
+            -stream_timestamp, buffer->discard_padding().second));
       }
+    } else {
+      // Verify that codec delay would cover discard and that we don't need to
+      // mark the packet for post decode discard.  Since timestamps may be in
+      // milliseconds and codec delay in nanosecond precision, round up to the
+      // nearest millisecond.  See enable_negative_timestamp_fixups().
+      DCHECK_LE(-std::ceil(FramesToTimeDelta(
+                               audio_decoder_config().codec_delay(),
+                               audio_decoder_config().samples_per_second())
+                               .InMillisecondsF()),
+                stream_timestamp.InMillisecondsF());
     }
-  } else {
-    // If this happens on the first packet, decoders will throw an error.
-    buffer->set_timestamp(kNoTimestamp);
   }
 
   if (last_packet_timestamp_ != kNoTimestamp) {
@@ -538,27 +538,19 @@
     //
     // If the new link starts with a negative timestamp or a timestamp less than
     // the original (positive) |start_time|, we will get a negative timestamp
-    // here.  It's also possible FFmpeg returns kNoTimestamp here if it's not
-    // able to work out a timestamp using the previous link and the next.
+    // here.
     //
     // Fixing chained ogg is non-trivial, so for now just reuse the last good
     // timestamp.  The decoder will rewrite the timestamps to be sample accurate
     // later.  See http://crbug.com/396864.
     if (fixup_negative_timestamps_ &&
-        (buffer->timestamp() == kNoTimestamp ||
-         buffer->timestamp() < last_packet_timestamp_)) {
+        buffer->timestamp() < last_packet_timestamp_) {
       buffer->set_timestamp(last_packet_timestamp_ +
                             (last_packet_duration_ != kNoTimestamp
                                  ? last_packet_duration_
                                  : base::TimeDelta::FromMicroseconds(1)));
     }
 
-    if (buffer->timestamp() == kNoTimestamp) {
-      // If we didn't get a valid timestamp and didn't fix it up, then fail.
-      demuxer_->NotifyDemuxerError(DEMUXER_ERROR_COULD_NOT_PARSE);
-      return;
-    }
-
     // The demuxer should always output positive timestamps.
     DCHECK(buffer->timestamp() >= base::TimeDelta());
 
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index d6a2bffb..35bb7da 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
+#include "base/test/mock_callback.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "media/base/decrypt_config.h"
@@ -962,17 +963,6 @@
   base::RunLoop().Run();
 }
 
-class MockReadCB {
- public:
-  MockReadCB() {}
-  ~MockReadCB() {}
-
-  MOCK_METHOD2(Run, void(DemuxerStream::Status status,
-                         const scoped_refptr<DecoderBuffer>& buffer));
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockReadCB);
-};
-
 TEST_F(FFmpegDemuxerTest, Stop) {
   // Tests that calling Read() on a stopped demuxer stream immediately deletes
   // the callback.
@@ -986,11 +976,11 @@
   demuxer_->Stop();
 
   // Reads after being stopped are all EOS buffers.
-  StrictMock<MockReadCB> callback;
+  StrictMock<base::MockCallback<DemuxerStream::ReadCB>> callback;
   EXPECT_CALL(callback, Run(DemuxerStream::kOk, IsEndOfStreamBuffer()));
 
   // Attempt the read...
-  audio->Read(base::Bind(&MockReadCB::Run, base::Unretained(&callback)));
+  audio->Read(callback.Get());
   base::RunLoop().RunUntilIdle();
 
   // Don't let the test call Stop() again.
diff --git a/media/muxers/webm_muxer.cc b/media/muxers/webm_muxer.cc
index 616e7b9..02a4ca9 100644
--- a/media/muxers/webm_muxer.cc
+++ b/media/muxers/webm_muxer.cc
@@ -106,7 +106,8 @@
       has_video_(has_video),
       has_audio_(has_audio),
       write_data_callback_(write_data_callback),
-      position_(0) {
+      position_(0),
+      force_one_libwebm_error_(false) {
   DCHECK(has_video_ || has_audio_);
   DCHECK(!write_data_callback_.is_null());
   DCHECK(codec == kCodecVP8 || codec == kCodecVP9 || codec == kCodecH264)
@@ -131,7 +132,7 @@
   segment_.Finalize();
 }
 
-void WebmMuxer::OnEncodedVideo(const VideoParameters& params,
+bool WebmMuxer::OnEncodedVideo(const VideoParameters& params,
                                std::unique_ptr<std::string> encoded_data,
                                base::TimeTicks timestamp,
                                bool is_key_frame) {
@@ -154,23 +155,17 @@
 
     encoded_frames_queue_.push_back(base::MakeUnique<EncodedVideoFrame>(
         std::move(encoded_data), timestamp, is_key_frame));
-    return;
+    return true;
   }
 
-  // Dump all saved encoded video frames if any.
-  while (!encoded_frames_queue_.empty()) {
-    AddFrame(
-        std::move(encoded_frames_queue_.front()->data), video_track_index_,
-        encoded_frames_queue_.front()->timestamp - first_frame_timestamp_video_,
-        encoded_frames_queue_.front()->is_keyframe);
-    encoded_frames_queue_.pop_front();
-  }
+  // Any saved encoded video frames must have been dumped in OnEncodedAudio();
+  DCHECK(encoded_frames_queue_.empty());
 
-  AddFrame(std::move(encoded_data), video_track_index_,
-           timestamp - first_frame_timestamp_video_, is_key_frame);
+  return AddFrame(std::move(encoded_data), video_track_index_,
+                  timestamp - first_frame_timestamp_video_, is_key_frame);
 }
 
-void WebmMuxer::OnEncodedAudio(const media::AudioParameters& params,
+bool WebmMuxer::OnEncodedAudio(const media::AudioParameters& params,
                                std::unique_ptr<std::string> encoded_data,
                                base::TimeTicks timestamp) {
   DVLOG(2) << __func__ << " - " << encoded_data->size() << "B";
@@ -186,21 +181,24 @@
   // TODO(ajose): Support multiple tracks: http://crbug.com/528523
   if (has_video_ && !video_track_index_) {
     DVLOG(1) << __func__ << ": delaying until video track ready.";
-    return;
+    return true;
   }
 
   // Dump all saved encoded video frames if any.
   while (!encoded_frames_queue_.empty()) {
-    AddFrame(
-        std::move(encoded_frames_queue_.front()->data), video_track_index_,
+    const bool res = AddFrame(
+        base::MakeUnique<std::string>(*encoded_frames_queue_.front()->data),
+        video_track_index_,
         encoded_frames_queue_.front()->timestamp - first_frame_timestamp_video_,
         encoded_frames_queue_.front()->is_keyframe);
+    if (!res)
+      return false;
     encoded_frames_queue_.pop_front();
   }
 
-  AddFrame(std::move(encoded_data), audio_track_index_,
-           timestamp - first_frame_timestamp_audio_,
-           true /* is_key_frame -- always true for audio */);
+  return AddFrame(std::move(encoded_data), audio_track_index_,
+                  timestamp - first_frame_timestamp_audio_,
+                  true /* is_key_frame -- always true for audio */);
 }
 
 void WebmMuxer::Pause() {
@@ -309,7 +307,7 @@
       << "Can't go back in a live WebM stream.";
 }
 
-void WebmMuxer::AddFrame(std::unique_ptr<std::string> encoded_data,
+bool WebmMuxer::AddFrame(std::unique_ptr<std::string> encoded_data,
                          uint8_t track_index,
                          base::TimeDelta timestamp,
                          bool is_key_frame) {
@@ -320,11 +318,19 @@
   most_recent_timestamp_ =
       std::max(most_recent_timestamp_, timestamp - total_time_in_pause_);
 
-  segment_.AddFrame(reinterpret_cast<const uint8_t*>(encoded_data->data()),
-                    encoded_data->size(), track_index,
-                    most_recent_timestamp_.InMicroseconds() *
-                        base::Time::kNanosecondsPerMicrosecond,
-                    is_key_frame);
+  if (force_one_libwebm_error_) {
+    DVLOG(1) << "Forcing a libwebm error";
+    force_one_libwebm_error_ = false;
+    return false;
+  }
+
+  DCHECK(encoded_data->data());
+  return segment_.AddFrame(
+      reinterpret_cast<const uint8_t*>(encoded_data->data()),
+      encoded_data->size(), track_index,
+      most_recent_timestamp_.InMicroseconds() *
+          base::Time::kNanosecondsPerMicrosecond,
+      is_key_frame);
 }
 
 WebmMuxer::EncodedVideoFrame::EncodedVideoFrame(
diff --git a/media/muxers/webm_muxer.h b/media/muxers/webm_muxer.h
index e019983..e8ac3a4 100644
--- a/media/muxers/webm_muxer.h
+++ b/media/muxers/webm_muxer.h
@@ -66,18 +66,20 @@
   ~WebmMuxer() override;
 
   // Functions to add video and audio frames with |encoded_data.data()|
-  // to WebM Segment.
-  void OnEncodedVideo(const VideoParameters& params,
+  // to WebM Segment. Either one returns true on success.
+  bool OnEncodedVideo(const VideoParameters& params,
                       std::unique_ptr<std::string> encoded_data,
                       base::TimeTicks timestamp,
                       bool is_key_frame);
-  void OnEncodedAudio(const media::AudioParameters& params,
+  bool OnEncodedAudio(const media::AudioParameters& params,
                       std::unique_ptr<std::string> encoded_data,
                       base::TimeTicks timestamp);
 
   void Pause();
   void Resume();
 
+  void ForceOneLibWebmErrorForTesting() { force_one_libwebm_error_ = true; }
+
  private:
   friend class WebmMuxerTest;
 
@@ -97,8 +99,8 @@
   void ElementStartNotify(mkvmuxer::uint64 element_id,
                           mkvmuxer::int64 position) override;
 
-  // Helper to simplify saving frames.
-  void AddFrame(std::unique_ptr<std::string> encoded_data,
+  // Helper to simplify saving frames. Returns true on success.
+  bool AddFrame(std::unique_ptr<std::string> encoded_data,
                 uint8_t track_index,
                 base::TimeDelta timestamp,
                 bool is_key_frame);
@@ -136,6 +138,8 @@
 
   // The MkvMuxer active element.
   mkvmuxer::Segment segment_;
+  // Flag to force the next call to a |segment_| method to return false.
+  bool force_one_libwebm_error_;
 
   // Hold on to all encoded video frames to dump them with and when audio is
   // received, if expected, since WebM headers can only be written once.
diff --git a/media/muxers/webm_muxer_unittest.cc b/media/muxers/webm_muxer_unittest.cc
index a65a885..8dcceb8 100644
--- a/media/muxers/webm_muxer_unittest.cc
+++ b/media/muxers/webm_muxer_unittest.cc
@@ -33,13 +33,13 @@
 
 namespace media {
 
-struct kTestParams {
+struct TestParams {
   VideoCodec codec;
   size_t num_video_tracks;
   size_t num_audio_tracks;
 };
 
-class WebmMuxerTest : public TestWithParam<kTestParams> {
+class WebmMuxerTest : public TestWithParam<TestParams> {
  public:
   WebmMuxerTest()
       : webm_muxer_(
@@ -109,9 +109,10 @@
       .Times(AtLeast(1))
       .WillRepeatedly(
           WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen)));
-  webm_muxer_.OnEncodedVideo(WebmMuxer::VideoParameters(video_frame),
-                             base::WrapUnique(new std::string(encoded_data)),
-                             base::TimeTicks::Now(), false /* keyframe */);
+  EXPECT_TRUE(webm_muxer_.OnEncodedVideo(
+      WebmMuxer::VideoParameters(video_frame),
+      base::WrapUnique(new std::string(encoded_data)), base::TimeTicks::Now(),
+      false /* keyframe */));
 
   // First time around WriteCallback() is pinged a number of times to write the
   // Matroska header, but at the end it dumps |encoded_data|.
@@ -125,9 +126,9 @@
       .Times(AtLeast(1))
       .WillRepeatedly(
           WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen)));
-  webm_muxer_.OnEncodedVideo(video_frame,
-                             base::WrapUnique(new std::string(encoded_data)),
-                             base::TimeTicks::Now(), false /* keyframe */);
+  EXPECT_TRUE(webm_muxer_.OnEncodedVideo(
+      video_frame, base::WrapUnique(new std::string(encoded_data)),
+      base::TimeTicks::Now(), false /* keyframe */));
 
   // The second time around the callbacks should include a SimpleBlock header,
   // namely the track index, a timestamp and a flags byte, for a total of 6B.
@@ -137,6 +138,13 @@
   EXPECT_EQ(static_cast<int64_t>(begin_of_second_block + kSimpleBlockSize +
                                  encoded_data.size()),
             accumulated_position_);
+
+  // Force an error in libwebm and expect OnEncodedVideo to fail.
+  webm_muxer_.ForceOneLibWebmErrorForTesting();
+  EXPECT_FALSE(
+      webm_muxer_.OnEncodedVideo(WebmMuxer::VideoParameters(video_frame),
+                                 base::MakeUnique<std::string>(encoded_data),
+                                 base::TimeTicks::Now(), true /* keyframe */));
 }
 
 TEST_P(WebmMuxerTest, OnEncodedAudioTwoFrames) {
@@ -157,9 +165,9 @@
       .Times(AtLeast(1))
       .WillRepeatedly(
           WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen)));
-  webm_muxer_.OnEncodedAudio(audio_params,
-                             base::MakeUnique<std::string>(encoded_data),
-                             base::TimeTicks::Now());
+  EXPECT_TRUE(webm_muxer_.OnEncodedAudio(
+      audio_params, base::MakeUnique<std::string>(encoded_data),
+      base::TimeTicks::Now()));
 
   // First time around WriteCallback() is pinged a number of times to write the
   // Matroska header, but at the end it dumps |encoded_data|.
@@ -173,9 +181,9 @@
       .Times(AtLeast(1))
       .WillRepeatedly(
           WithArgs<0>(Invoke(this, &WebmMuxerTest::SaveEncodedDataLen)));
-  webm_muxer_.OnEncodedAudio(audio_params,
-                             base::MakeUnique<std::string>(encoded_data),
-                             base::TimeTicks::Now());
+  EXPECT_TRUE(webm_muxer_.OnEncodedAudio(
+      audio_params, base::MakeUnique<std::string>(encoded_data),
+      base::TimeTicks::Now()));
 
   // The second time around the callbacks should include a SimpleBlock header,
   // namely the track index, a timestamp and a flags byte, for a total of 6B.
@@ -185,6 +193,12 @@
   EXPECT_EQ(static_cast<int64_t>(begin_of_second_block + kSimpleBlockSize +
                                  encoded_data.size()),
             accumulated_position_);
+
+  // Force an error in libwebm and expect OnEncodedAudio to fail.
+  webm_muxer_.ForceOneLibWebmErrorForTesting();
+  EXPECT_FALSE(webm_muxer_.OnEncodedAudio(
+      audio_params, base::MakeUnique<std::string>(encoded_data),
+      base::TimeTicks::Now()));
 }
 
 // This test verifies that when video data comes before audio data, we save the
@@ -194,24 +208,24 @@
   if (GetParam().num_video_tracks == 0 || GetParam().num_audio_tracks == 0)
     return;
 
-  // First send a video keyframe
+  // First send a video keyframe.
   const gfx::Size frame_size(160, 80);
   const scoped_refptr<VideoFrame> video_frame =
       VideoFrame::CreateBlackFrame(frame_size);
   const std::string encoded_video("thisisanencodedvideopacket");
-  webm_muxer_.OnEncodedVideo(WebmMuxer::VideoParameters(video_frame),
-                             base::WrapUnique(new std::string(encoded_video)),
-                             base::TimeTicks::Now(), true /* keyframe */);
+  EXPECT_TRUE(webm_muxer_.OnEncodedVideo(
+      WebmMuxer::VideoParameters(video_frame),
+      base::WrapUnique(new std::string(encoded_video)), base::TimeTicks::Now(),
+      true /* keyframe */));
   // A few encoded non key frames.
   const int kNumNonKeyFrames = 2;
   for (int i = 0; i < kNumNonKeyFrames; ++i) {
-    webm_muxer_.OnEncodedVideo(WebmMuxer::VideoParameters(video_frame),
-                               base::WrapUnique(new std::string(encoded_video)),
-                               base::TimeTicks::Now(), false /* keyframe */);
+    EXPECT_TRUE(webm_muxer_.OnEncodedVideo(
+        WebmMuxer::VideoParameters(video_frame),
+        base::WrapUnique(new std::string(encoded_video)),
+        base::TimeTicks::Now(), false /* keyframe */));
   }
 
-  // Send some audio. The header will be written and muxing will proceed
-  // normally.
   const int sample_rate = 48000;
   const int bits_per_sample = 16;
   const int frames_per_buffer = 480;
@@ -221,7 +235,13 @@
       frames_per_buffer);
   const std::string encoded_audio("thisisanencodedaudiopacket");
 
-  // We should first get the encoded video frames, then the encoded audio frame.
+  // Force one libwebm error and verify OnEncodedAudio() fails.
+  webm_muxer_.ForceOneLibWebmErrorForTesting();
+  EXPECT_FALSE(webm_muxer_.OnEncodedAudio(
+      audio_params, base::WrapUnique(new std::string(encoded_audio)),
+      base::TimeTicks::Now()));
+
+  // We should get the queued encoded video frames, then an encoded audio frame.
   Sequence s;
   EXPECT_CALL(*this, WriteCallback(Eq(encoded_video)))
       .Times(1 + kNumNonKeyFrames)
@@ -231,12 +251,12 @@
   EXPECT_CALL(*this, WriteCallback(
                          AllOf(Not(Eq(encoded_video)), Not(Eq(encoded_audio)))))
       .Times(AnyNumber());
-  webm_muxer_.OnEncodedAudio(audio_params,
-                             base::WrapUnique(new std::string(encoded_audio)),
-                             base::TimeTicks::Now());
+  EXPECT_TRUE(webm_muxer_.OnEncodedAudio(
+      audio_params, base::WrapUnique(new std::string(encoded_audio)),
+      base::TimeTicks::Now()));
 }
 
-const kTestParams kTestCases[] = {
+const TestParams kTestCases[] = {
     {kCodecVP8, 1 /* num_video_tracks */, 0 /*num_audio_tracks*/},
     {kCodecVP8, 0, 1},
     {kCodecVP8, 1, 1},
diff --git a/net/cert/ct_log_response_parser.cc b/net/cert/ct_log_response_parser.cc
index 5baf1b3..b6067d1 100644
--- a/net/cert/ct_log_response_parser.cc
+++ b/net/cert/ct_log_response_parser.cc
@@ -4,10 +4,11 @@
 
 #include "net/cert/ct_log_response_parser.h"
 
+#include <memory>
+
 #include "base/base64.h"
 #include "base/json/json_value_converter.h"
 #include "base/logging.h"
-#include "base/memory/scoped_vector.h"
 #include "base/strings/string_piece.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -105,7 +106,7 @@
 // Structure for making JSON decoding easier. The string fields
 // are base64-encoded so will require further decoding.
 struct JsonConsistencyProof {
-  ScopedVector<std::string> proof_nodes;
+  std::vector<std::unique_ptr<std::string>> proof_nodes;
 
   static void RegisterJSONConverter(
       base::JSONValueConverter<JsonConsistencyProof>* converter);
@@ -170,7 +171,7 @@
   }
 
   consistency_proof->reserve(parsed_proof.proof_nodes.size());
-  for (std::string* proof_node : parsed_proof.proof_nodes) {
+  for (const auto& proof_node : parsed_proof.proof_nodes) {
     consistency_proof->push_back(*proof_node);
   }
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 13e17b5..d26c1d3e 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -885,6 +885,18 @@
             ]
         }
     ],
+    "InstantApps": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "InstantAppsEnabled"
+                }
+            ]
+        }
+    ],
     "IntelligentSessionRestore": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index c0985ae..da8d276d 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -2314,9 +2314,6 @@
 
 crbug.com/676229 [ Linux ] plugins/mouse-click-plugin-clears-selection.html [ Failure Pass ]
 
-crbug.com/677145 inspector/tracing/timeline-js/timeline-script-tag-1.html [ Pass Failure ]
-crbug.com/677145 virtual/threaded/inspector/tracing/timeline-js/timeline-script-tag-1.html [ Pass Failure ]
-
 crbug.com/678346 [ Win7 Debug ] fast/dom/shadow/selections-in-shadow.html [ Pass Timeout ]
 crbug.com/678346 [ Win7 Debug ] storage/indexeddb/index-cursor.html [ Pass Timeout ]
 crbug.com/678346 [ Win7 Debug ] storage/indexeddb/mozilla/test_objectStore_openKeyCursor.html [ Pass Timeout ]
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/timeline-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector/timeline-test.js
index c1ad4c83..0059174 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/timeline-test.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/timeline-test.js
@@ -268,57 +268,6 @@
         formatter(record);
 };
 
-// Dump just the record name, indenting output on separate lines for subrecords
-InspectorTest.dumpTimelineRecord = function(record, detailsCallback, level, filterTypes)
-{
-    if (typeof level !== "number")
-        level = 0;
-    var message = "";
-    for (var i = 0; i < level ; ++i)
-        message = "----" + message;
-    if (level > 0)
-        message = message + "> ";
-    if (record.type() === TimelineModel.TimelineModel.RecordType.TimeStamp
-        || record.type() === TimelineModel.TimelineModel.RecordType.ConsoleTime) {
-        message += Timeline.TimelineUIUtils.eventTitle(record.traceEvent());
-    } else  {
-        message += record.type();
-    }
-    if (detailsCallback)
-        message += " " + detailsCallback(record);
-    InspectorTest.addResult(message);
-
-    var children = record.children();
-    var numChildren = children.length;
-    for (var i = 0; i < numChildren; ++i) {
-        if (filterTypes && filterTypes.indexOf(children[i].type()) == -1)
-            continue;
-        InspectorTest.dumpTimelineRecord(children[i], detailsCallback, level + 1, filterTypes);
-    }
-}
-
-InspectorTest.dumpTimelineModelRecord = function(record, level)
-{
-    if (typeof level !== "number")
-        level = 0;
-    var prefix = "";
-    for (var i = 0; i < level ; ++i)
-        prefix = "----" + prefix;
-    if (level > 0)
-        prefix = prefix + "> ";
-    InspectorTest.addResult(prefix + record.type() + ": " + (Timeline.TimelineUIUtils.buildDetailsTextForTraceEvent(record.traceEvent(), null) || ""));
-
-    var numChildren = record.children() ? record.children().length : 0;
-    for (var i = 0; i < numChildren; ++i)
-        InspectorTest.dumpTimelineModelRecord(record.children()[i], level + 1);
-}
-
-InspectorTest.dumpTimelineRecords = function(timelineRecords)
-{
-    for (var i = 0; i < timelineRecords.length; ++i)
-        InspectorTest.dumpTimelineRecord(timelineRecords[i], 0);
-};
-
 InspectorTest.printTimelineRecordProperties = function(record)
 {
     InspectorTest.printTraceEventProperties(record.traceEvent());
@@ -330,6 +279,18 @@
         InspectorTest.printTraceEventProperties(traceEvent);
 }
 
+InspectorTest.forAllEvents = function(events, callback)
+{
+    let eventStack = [];
+    for (let event of events) {
+        while (eventStack.length && eventStack.peekLast().endTime <= event.startTime)
+            eventStack.pop();
+        callback(event, eventStack);
+        if (event.endTime)
+            eventStack.push(event);
+    }
+}
+
 InspectorTest.printTraceEventProperties = function(traceEvent)
 {
     InspectorTest.addResult(traceEvent.name + " Properties:");
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-correct-suggestions-expected.txt b/third_party/WebKit/LayoutTests/inspector/console/console-correct-suggestions-expected.txt
index 9bdbd35e..86eade7 100644
--- a/third_party/WebKit/LayoutTests/inspector/console/console-correct-suggestions-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/console/console-correct-suggestions-expected.txt
@@ -71,4 +71,36 @@
 Checking 'shou'
 Not Found: should not find this
 
+Checking 'document.   bo'
+Found: body
+
+Checking 'document.	bo'
+Found: body
+
+Checking 'document.
+bo'
+Found: body
+
+Checking 'document.
+bo'
+Found: body
+
+Checking 'document   [    'bo'
+Not Found: 'body']
+
+Checking 'function hey(should'
+Not Found: shouldNotFindThisFunction
+
+Checking 'var should'
+Not Found: shouldNotFindThisFunction
+
+Checking 'document[[win'
+Found: window
+
+Checking 'document[   [win'
+Found: window
+
+Checking 'document[   [  win'
+Found: window
+
 
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-correct-suggestions.html b/third_party/WebKit/LayoutTests/inspector/console/console-correct-suggestions.html
index d2a9cec6..f01839c4 100644
--- a/third_party/WebKit/LayoutTests/inspector/console/console-correct-suggestions.html
+++ b/third_party/WebKit/LayoutTests/inspector/console/console-correct-suggestions.html
@@ -68,7 +68,17 @@
         () => testCompletions("`${do", ["document"], false),
         () => testCompletions("// do", ["document"], false),
         () => testCompletions('["should', ["shouldNotFindThisFunction"]),
-        () => testCompletions("shou", ["should not find this"])
+        () => testCompletions("shou", ["should not find this"]),
+        () => testCompletions("document.   bo", ["body"]),
+        () => testCompletions("document.\tbo", ["body"]),
+        () => testCompletions("document.\nbo", ["body"]),
+        () => testCompletions("document.\r\nbo", ["body"]),
+        () => testCompletions("document   [    'bo", ["'body']"]),
+        () => testCompletions("function hey(should", ["shouldNotFindThisFunction"]),
+        () => testCompletions("var should", ["shouldNotFindThisFunction"]),
+        () => testCompletions("document[[win", ["window"]),
+        () => testCompletions("document[   [win", ["window"]),
+        () => testCompletions("document[   [  win", ["window"])
     ]).then(InspectorTest.completeTest);
 
 }
diff --git a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-js/timeline-script-tag-1-expected.txt b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-js/timeline-script-tag-1-expected.txt
index fa7747d..75665e6 100644
--- a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-js/timeline-script-tag-1-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-js/timeline-script-tag-1-expected.txt
@@ -2,7 +2,6 @@
 Tests the Timeline API instrumentation of an HTML script tag.
 
 
-EvaluateScript
 EvaluateScript Properties:
 {
     data : {
@@ -11,8 +10,6 @@
     startTime : <number>
     type : "EvaluateScript"
 }
-EvaluateScript
-----> Timestamp: SCRIPT TAG
 EvaluateScript Properties:
 {
     data : {
@@ -26,4 +23,5 @@
     startTime : <number>
     type : "EvaluateScript"
 }
+----> Timestamp: SCRIPT TAG
 
diff --git a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-js/timeline-script-tag-1.html b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-js/timeline-script-tag-1.html
index 5a0259f4..d77e264 100644
--- a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-js/timeline-script-tag-1.html
+++ b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-js/timeline-script-tag-1.html
@@ -31,14 +31,12 @@
         function format(record)
         {
             if (record.type() === TimelineModel.TimelineModel.RecordType.EvaluateScript) {
-                InspectorTest.dumpTimelineRecord(record, undefined, undefined, [
-                    "TimeStamp",
-                ]);
                 InspectorTest.printTimelineRecordProperties(record);
+            } else if (record.type() === TimelineModel.TimelineModel.RecordType.TimeStamp) {
+                InspectorTest.addResult(`----> ${Timeline.TimelineUIUtils.eventTitle(record.traceEvent())}`);
             }
-        }
-
-        InspectorTest.printTimelineRecords(null, InspectorTest.safeWrap(format));
+        };
+        InspectorTest.printTimelineRecords(null, format);
         InspectorTest.completeTest();
     }
 }
diff --git a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-model-expected.txt b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-model-expected.txt
index 3607f7c..0ca0edb 100644
--- a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-model-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-model-expected.txt
@@ -1,5 +1,7 @@
 Test trace-specific implementation of timeline model
 
+TracingStartedInPage: 
+SetLayerTreeId: 
 Program: 
 ----> FunctionCall: 
 --------> ResourceSendRequest: example.com
diff --git a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-model.html b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-model.html
index 3973c64..d72855c 100644
--- a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-model.html
+++ b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-model.html
@@ -52,12 +52,11 @@
     ];
 
     var tracingTimelineModel = InspectorTest.createTimelineModelWithEvents(commonMetadata.concat(traceEvents));
-    var records = tracingTimelineModel.records();
-    var topLevelRecordsCount = 2;
-    InspectorTest.assertEquals(topLevelRecordsCount, records.length);
-    for (var i = 0; i < records.length; ++i) {
-        InspectorTest.dumpTimelineModelRecord(records[i]);
-    }
+    InspectorTest.forAllEvents(tracingTimelineModel.mainThreadEvents(), (event, stack) => {
+        const prefix = Array(stack.length + 1).join("----") + (stack.length ? "> " : "");
+        const details = Timeline.TimelineUIUtils.buildDetailsTextForTraceEvent(event, null) || "";
+        InspectorTest.addResult(`${prefix}${event.name}: ${details}`);
+    });
     InspectorTest.completeTest();
 }
 
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/border-radius-repaint-2-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/border-radius-repaint-2-expected.txt
index 7e1f99d..04692bb 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/border-radius-repaint-2-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/border-radius-repaint-2-expected.txt
@@ -18,6 +18,11 @@
           "reason": "border box change"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 780],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow BODY",
           "rect": [8, 224, 784, 548],
           "reason": "incremental"
@@ -36,6 +41,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "incremental"
     },
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/fixed-and-absolute-position-scrolled-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/fixed-and-absolute-position-scrolled-expected.txt
index e1beaf2..8c7044e 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/fixed-and-absolute-position-scrolled-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/fixed-and-absolute-position-scrolled-expected.txt
@@ -12,6 +12,11 @@
           "reason": "incremental"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 2016],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow (positioned) DIV id='absoluteDiv' class='absolute green'",
           "rect": [108, 5708, 100, 100],
           "reason": "subtree"
@@ -34,6 +39,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow (positioned) DIV id='absoluteDiv' class='absolute green'",
       "reason": "style change"
     }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/scrolled-iframe-scrollbar-change-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/scrolled-iframe-scrollbar-change-expected.txt
index b9a0b28..72fe8f3 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/scrolled-iframe-scrollbar-change-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/scrolled-iframe-scrollbar-change-expected.txt
@@ -13,6 +13,10 @@
       "reason": "style change"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY class='noScroll'",
       "reason": "style change"
     },
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt
index 16c08a5..5b2f5e69 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 785, 829],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [0, 735, 785, 94],
           "reason": "incremental"
@@ -60,6 +65,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutIFrame IFRAME id='iframe'",
       "reason": "layoutObject insertion"
     },
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/shift-relative-positioned-container-with-image-removal-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/shift-relative-positioned-container-with-image-removal-expected.txt
index 1b942e91..65d3d3e 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/shift-relative-positioned-container-with-image-removal-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/shift-relative-positioned-container-with-image-removal-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 785, 833],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [0, 735, 785, 98],
           "reason": "incremental"
@@ -62,6 +67,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow (relative positioned) DIV class='relative paddingTop'",
       "reason": "bounds change"
     },
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-background-image-fixed-centered-composited-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-background-image-fixed-centered-composited-expected.txt
index d8a230a..90bf90d7 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-background-image-fixed-centered-composited-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-background-image-fixed-centered-composited-expected.txt
@@ -9,6 +9,11 @@
           "object": "LayoutView #document",
           "rect": [0, 250, 600, 250],
           "reason": "incremental"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 600, 8],
+          "reason": "forced by layout"
         }
       ]
     }
@@ -17,6 +22,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -28,6 +37,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 600, 8],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [400, 0, 200, 250],
           "reason": "incremental"
@@ -39,6 +53,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -53,6 +71,11 @@
           "object": "LayoutView #document",
           "rect": [0, 250, 400, 350],
           "reason": "incremental"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 400, 8],
+          "reason": "forced by layout"
         }
       ]
     }
@@ -61,6 +84,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -72,6 +99,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 8],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [400, 0, 400, 600],
           "reason": "incremental"
@@ -83,6 +115,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-background-image-fixed-centered-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-background-image-fixed-centered-expected.txt
index cdd87ce..128cff1e 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-background-image-fixed-centered-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-background-image-fixed-centered-expected.txt
@@ -10,6 +10,11 @@
           "object": "LayoutView #document",
           "rect": [0, 0, 600, 500],
           "reason": "bounds change"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 600, 8],
+          "reason": "forced by layout"
         }
       ]
     }
@@ -18,6 +23,10 @@
     {
       "object": "LayoutView #document",
       "reason": "bounds change"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -33,6 +42,11 @@
           "object": "LayoutView #document",
           "rect": [0, 0, 600, 250],
           "reason": "bounds change"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 600, 8],
+          "reason": "forced by layout"
         }
       ]
     }
@@ -41,6 +55,10 @@
     {
       "object": "LayoutView #document",
       "reason": "bounds change"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -56,6 +74,11 @@
           "object": "LayoutView #document",
           "rect": [0, 0, 400, 600],
           "reason": "bounds change"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 400, 8],
+          "reason": "forced by layout"
         }
       ]
     }
@@ -64,6 +87,10 @@
     {
       "object": "LayoutView #document",
       "reason": "bounds change"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -79,6 +106,11 @@
           "object": "LayoutView #document",
           "rect": [0, 0, 800, 600],
           "reason": "bounds change"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 8],
+          "reason": "forced by layout"
         }
       ]
     }
@@ -87,6 +119,10 @@
     {
       "object": "LayoutView #document",
       "reason": "bounds change"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-background-image-generated-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-background-image-generated-expected.txt
index cdd87ce..128cff1e 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-background-image-generated-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-background-image-generated-expected.txt
@@ -10,6 +10,11 @@
           "object": "LayoutView #document",
           "rect": [0, 0, 600, 500],
           "reason": "bounds change"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 600, 8],
+          "reason": "forced by layout"
         }
       ]
     }
@@ -18,6 +23,10 @@
     {
       "object": "LayoutView #document",
       "reason": "bounds change"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -33,6 +42,11 @@
           "object": "LayoutView #document",
           "rect": [0, 0, 600, 250],
           "reason": "bounds change"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 600, 8],
+          "reason": "forced by layout"
         }
       ]
     }
@@ -41,6 +55,10 @@
     {
       "object": "LayoutView #document",
       "reason": "bounds change"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -56,6 +74,11 @@
           "object": "LayoutView #document",
           "rect": [0, 0, 400, 600],
           "reason": "bounds change"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 400, 8],
+          "reason": "forced by layout"
         }
       ]
     }
@@ -64,6 +87,10 @@
     {
       "object": "LayoutView #document",
       "reason": "bounds change"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -79,6 +106,11 @@
           "object": "LayoutView #document",
           "rect": [0, 0, 800, 600],
           "reason": "bounds change"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 8],
+          "reason": "forced by layout"
         }
       ]
     }
@@ -87,6 +119,10 @@
     {
       "object": "LayoutView #document",
       "reason": "bounds change"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-background-image-non-fixed-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-background-image-non-fixed-expected.txt
index 5e3ebd4..302f48d4 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-background-image-non-fixed-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-background-image-non-fixed-expected.txt
@@ -10,6 +10,11 @@
           "object": "LayoutView #document",
           "rect": [0, 250, 600, 250],
           "reason": "incremental"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 600, 8],
+          "reason": "forced by layout"
         }
       ]
     }
@@ -18,6 +23,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -30,6 +39,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 600, 8],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [400, 0, 200, 250],
           "reason": "incremental"
@@ -41,6 +55,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -56,6 +74,11 @@
           "object": "LayoutView #document",
           "rect": [0, 250, 400, 350],
           "reason": "incremental"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 400, 8],
+          "reason": "forced by layout"
         }
       ]
     }
@@ -64,6 +87,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -76,6 +103,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 8],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [400, 0, 400, 600],
           "reason": "incremental"
@@ -87,6 +119,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-centered-inline-under-fixed-pos-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-centered-inline-under-fixed-pos-expected.txt
index 73762ea..499f797 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-centered-inline-under-fixed-pos-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-centered-inline-under-fixed-pos-expected.txt
@@ -17,6 +17,11 @@
           "reason": "incremental"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 600, 8],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow DIV class='parent'",
           "rect": [0, 0, 6, 500],
           "reason": "forced by layout"
@@ -40,6 +45,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow (positioned) DIV class='container'",
       "reason": "forced by layout"
     },
@@ -71,6 +80,11 @@
           "reason": "forced by layout"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 600, 8],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [400, 0, 200, 250],
           "reason": "incremental"
@@ -94,6 +108,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow (positioned) DIV class='container'",
       "reason": "forced by layout"
     },
@@ -130,6 +148,11 @@
           "reason": "incremental"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 400, 8],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow DIV class='parent'",
           "rect": [0, 0, 6, 600],
           "reason": "forced by layout"
@@ -153,6 +176,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow (positioned) DIV class='container'",
       "reason": "full"
     },
@@ -184,6 +211,11 @@
           "reason": "full"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 8],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [400, 0, 400, 600],
           "reason": "incremental"
@@ -207,6 +239,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow (positioned) DIV class='container'",
       "reason": "full"
     },
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-frameset-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-frameset-expected.txt
index 4b7110b..a29a0fb 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-frameset-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-frameset-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 600, 500],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutFrameSet FRAMESET",
           "rect": [0, 0, 600, 500],
           "reason": "border box change"
@@ -17,17 +22,32 @@
           "reason": "incremental"
         },
         {
-          "object": "LayoutFrame FRAME",
+          "object": "LayoutBlockFlow HTML",
           "rect": [153, 0, 294, 500],
           "reason": "forced by layout"
         },
         {
           "object": "LayoutFrame FRAME",
+          "rect": [153, 0, 294, 500],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
           "rect": [453, 0, 147, 500],
           "reason": "forced by layout"
         },
         {
           "object": "LayoutFrame FRAME",
+          "rect": [453, 0, 147, 500],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 147, 500],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutFrame FRAME",
           "rect": [0, 0, 147, 500],
           "reason": "forced by layout"
         }
@@ -40,6 +60,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutFrameSet FRAMESET",
       "reason": "border box change"
     },
@@ -60,12 +84,24 @@
       "reason": "scroll"
     },
     {
-      "object": "VerticalScrollbar",
-      "reason": "scroll"
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     },
     {
       "object": "VerticalScrollbar",
       "reason": "scroll"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "VerticalScrollbar",
+      "reason": "scroll"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -78,11 +114,21 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 600, 250],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutFrameSet FRAMESET",
           "rect": [0, 0, 600, 250],
           "reason": "border box change"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [153, 0, 294, 250],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutFrame FRAME",
           "rect": [153, 0, 294, 250],
           "reason": "forced by layout"
@@ -93,21 +139,41 @@
           "reason": "incremental"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [103, 0, 194, 250],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutFrame FRAME",
           "rect": [103, 0, 194, 250],
           "reason": "forced by layout"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [453, 0, 147, 250],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutFrame FRAME",
           "rect": [453, 0, 147, 250],
           "reason": "forced by layout"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 147, 250],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutFrame FRAME",
           "rect": [0, 0, 147, 250],
           "reason": "forced by layout"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [303, 0, 97, 250],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutFrame FRAME",
           "rect": [303, 0, 97, 250],
           "reason": "forced by layout"
@@ -121,6 +187,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutFrameSet FRAMESET",
       "reason": "border box change"
     },
@@ -141,6 +211,10 @@
       "reason": "scroll"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "HorizontalScrollbar",
       "reason": "scroll"
     },
@@ -150,7 +224,7 @@
     },
     {
       "object": "LayoutBlockFlow HTML",
-      "reason": "location change"
+      "reason": "forced by layout"
     },
     {
       "object": "LayoutBlockFlow BODY",
@@ -166,7 +240,7 @@
     },
     {
       "object": "LayoutBlockFlow HTML",
-      "reason": "location change"
+      "reason": "forced by layout"
     },
     {
       "object": "LayoutBlockFlow BODY",
@@ -183,6 +257,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 400, 600],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutFrameSet FRAMESET",
           "rect": [0, 0, 400, 600],
           "reason": "border box change"
@@ -193,17 +272,32 @@
           "reason": "incremental"
         },
         {
-          "object": "LayoutFrame FRAME",
+          "object": "LayoutBlockFlow HTML",
           "rect": [103, 0, 194, 600],
           "reason": "forced by layout"
         },
         {
           "object": "LayoutFrame FRAME",
+          "rect": [103, 0, 194, 600],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
           "rect": [303, 0, 97, 600],
           "reason": "forced by layout"
         },
         {
           "object": "LayoutFrame FRAME",
+          "rect": [303, 0, 97, 600],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 97, 600],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutFrame FRAME",
           "rect": [0, 0, 97, 600],
           "reason": "forced by layout"
         }
@@ -216,6 +310,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutFrameSet FRAMESET",
       "reason": "border box change"
     },
@@ -230,6 +328,18 @@
     {
       "object": "LayoutFrame FRAME",
       "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -242,6 +352,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 600],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutFrameSet FRAMESET",
           "rect": [0, 0, 800, 600],
           "reason": "border box change"
@@ -252,27 +367,52 @@
           "reason": "incremental"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [203, 0, 394, 600],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutFrame FRAME",
           "rect": [203, 0, 394, 600],
           "reason": "forced by layout"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [603, 0, 197, 600],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutFrame FRAME",
           "rect": [603, 0, 197, 600],
           "reason": "forced by layout"
         },
         {
-          "object": "LayoutFrame FRAME",
+          "object": "LayoutBlockFlow HTML",
           "rect": [0, 0, 197, 600],
           "reason": "forced by layout"
         },
         {
           "object": "LayoutFrame FRAME",
+          "rect": [0, 0, 197, 600],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
           "rect": [103, 0, 194, 600],
           "reason": "forced by layout"
         },
         {
           "object": "LayoutFrame FRAME",
+          "rect": [103, 0, 194, 600],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [303, 0, 97, 600],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutFrame FRAME",
           "rect": [303, 0, 97, 600],
           "reason": "forced by layout"
         }
@@ -285,6 +425,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutFrameSet FRAMESET",
       "reason": "border box change"
     },
@@ -301,12 +445,16 @@
       "reason": "forced by layout"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutView #document",
       "reason": "location change"
     },
     {
       "object": "LayoutBlockFlow HTML",
-      "reason": "location change"
+      "reason": "forced by layout"
     },
     {
       "object": "LayoutBlockFlow BODY",
@@ -318,7 +466,7 @@
     },
     {
       "object": "LayoutBlockFlow HTML",
-      "reason": "location change"
+      "reason": "forced by layout"
     },
     {
       "object": "LayoutBlockFlow BODY",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-media-query-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-media-query-expected.txt
index 3d6a9e3..833a115 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-media-query-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-media-query-expected.txt
@@ -11,6 +11,11 @@
           "object": "LayoutView #document",
           "rect": [0, 250, 600, 250],
           "reason": "incremental"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 600, 8],
+          "reason": "forced by layout"
         }
       ]
     }
@@ -19,6 +24,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -32,6 +41,11 @@
       "backgroundColor": "#0000FF",
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 600, 8],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [400, 0, 200, 250],
           "reason": "incremental"
@@ -43,6 +57,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -59,6 +77,11 @@
           "object": "LayoutView #document",
           "rect": [0, 250, 400, 350],
           "reason": "incremental"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 400, 8],
+          "reason": "forced by layout"
         }
       ]
     }
@@ -67,6 +90,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -80,6 +107,11 @@
       "backgroundColor": "#0000FF",
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 8],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [400, 0, 400, 600],
           "reason": "incremental"
@@ -91,6 +123,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-percent-html-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-percent-html-expected.txt
index b12c233..2a0f7d8 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-percent-html-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-percent-html-expected.txt
@@ -12,6 +12,11 @@
           "reason": "incremental"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 600, 250],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow DIV",
           "rect": [0, 0, 300, 125],
           "reason": "border box change"
@@ -25,6 +30,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow DIV",
       "reason": "border box change"
     }
@@ -39,6 +48,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 600, 125],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow DIV",
           "rect": [0, 0, 300, 63],
           "reason": "border box change"
@@ -57,6 +71,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow DIV",
       "reason": "border box change"
     }
@@ -76,6 +94,11 @@
           "reason": "incremental"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 400, 300],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow DIV",
           "rect": [0, 0, 200, 150],
           "reason": "border box change"
@@ -89,6 +112,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow DIV",
       "reason": "border box change"
     }
@@ -103,6 +130,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 300],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [400, 0, 400, 600],
           "reason": "incremental"
@@ -121,6 +153,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow DIV",
       "reason": "incremental"
     }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-percent-width-height-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-percent-width-height-expected.txt
index 8cd53c0..9b89be8 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-percent-width-height-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-percent-width-height-expected.txt
@@ -25,6 +25,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow (positioned) DIV",
       "reason": "incremental"
     }
@@ -57,6 +61,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow (positioned) DIV",
       "reason": "incremental"
     }
@@ -89,6 +97,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow (positioned) DIV",
       "reason": "incremental"
     }
@@ -121,6 +133,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow (positioned) DIV",
       "reason": "incremental"
     }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-positioned-bottom-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-positioned-bottom-expected.txt
index 08674f06..667e9d0 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-positioned-bottom-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-positioned-bottom-expected.txt
@@ -30,6 +30,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow (positioned) DIV",
       "reason": "bounds change"
     }
@@ -55,6 +59,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -90,6 +98,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow (positioned) DIV",
       "reason": "bounds change"
     }
@@ -115,6 +127,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-positioned-percent-top-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-positioned-percent-top-expected.txt
index a6abe419..5635bd5 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-positioned-percent-top-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-positioned-percent-top-expected.txt
@@ -30,6 +30,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow (positioned) DIV",
       "reason": "bounds change"
     }
@@ -55,6 +59,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -90,6 +98,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow (positioned) DIV",
       "reason": "bounds change"
     }
@@ -115,6 +127,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-viewport-percent-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-viewport-percent-expected.txt
index 9ba1963..b0b4159b 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-viewport-percent-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/window-resize-viewport-percent-expected.txt
@@ -30,6 +30,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow DIV",
       "reason": "incremental"
     }
@@ -55,6 +59,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
@@ -90,6 +98,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow DIV",
       "reason": "incremental"
     }
@@ -115,6 +127,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/abspos-shift-image-incorrect-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/abspos-shift-image-incorrect-repaint-expected.txt
index 8501e34..3bc3c4a8 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/abspos-shift-image-incorrect-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/abspos-shift-image-incorrect-repaint-expected.txt
@@ -17,6 +17,11 @@
           "reason": "bounds change"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 56],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow (relative positioned) DIV class='imageWrapper'",
           "rect": [704, 0, 700, 237],
           "reason": "bounds change"
@@ -80,6 +85,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow (positioned) DIV id='shiftMe'",
       "reason": "bounds change"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
index 727e8d9..8303ca25 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
@@ -30,6 +30,16 @@
           "reason": "style change"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 500, 300, 36],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 500, 285, 150],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow BODY",
           "rect": [8, 508, 284, 20],
           "reason": "forced by layout"
@@ -66,6 +76,10 @@
       "reason": "style change"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "forced by layout"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/line-flow-with-floats-2-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/line-flow-with-floats-2-expected.txt
index 46a24049..d85a716 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/line-flow-with-floats-2-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/line-flow-with-floats-2-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 485, 600],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow P",
           "rect": [8, 74, 418, 526],
           "reason": "border box change"
@@ -135,6 +140,10 @@
       "reason": "scroll"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow P",
       "reason": "border box change"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/line-flow-with-floats-8-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/line-flow-with-floats-8-expected.txt
index 151c399..1954ee2 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/line-flow-with-floats-8-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/line-flow-with-floats-8-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 485, 600],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow P",
           "rect": [8, 74, 418, 526],
           "reason": "forced by layout"
@@ -114,6 +119,10 @@
       "reason": "scroll"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow P",
       "reason": "forced by layout"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/resize-iframe-text-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/resize-iframe-text-expected.txt
index aeb175d..40a3c663 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/resize-iframe-text-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/resize-iframe-text-expected.txt
@@ -12,11 +12,21 @@
           "reason": "forced by layout"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 759],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [0, 600, 800, 200],
           "reason": "incremental"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 36],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow BODY",
           "rect": [8, 8, 784, 20],
           "reason": "forced by layout"
@@ -45,6 +55,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "forced by layout"
     },
@@ -77,6 +91,10 @@
       "reason": "scroll"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow H1",
       "reason": "became visible"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt
index 9919ab9..e8e68ae2 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 785, 836],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [0, 742, 785, 94],
           "reason": "incremental"
@@ -60,6 +65,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutIFrame IFRAME id='iframe'",
       "reason": "layoutObject insertion"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/shift-relative-positioned-container-with-image-removal-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/shift-relative-positioned-container-with-image-removal-expected.txt
index 1c8dfb15..08729c6 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/shift-relative-positioned-container-with-image-removal-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/shift-relative-positioned-container-with-image-removal-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 785, 841],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [0, 742, 785, 99],
           "reason": "incremental"
@@ -62,6 +67,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow (relative positioned) DIV class='relative paddingTop'",
       "reason": "bounds change"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-shrink-row-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-shrink-row-repaint-expected.txt
index 2bbd089..d86f50e 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-shrink-row-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/table-shrink-row-repaint-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 785, 1050],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [0, 850, 785, 200],
           "reason": "incremental"
@@ -265,6 +270,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutTableCell TD id='resizeMe'",
       "reason": "incremental"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/text-match-document-change-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/text-match-document-change-expected.txt
index fba9d1c2..7d685a1 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/text-match-document-change-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/text-match-document-change-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [10, 102, 285, 400],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow DIV id='to-be-changed'",
           "rect": [18, 130, 269, 40],
           "reason": "full"
@@ -39,6 +44,10 @@
       "reason": "scroll"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow DIV id='to-be-changed'",
       "reason": "full"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/window-resize-vertical-writing-mode-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
index 609d5f1..574f683 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 939, 235],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [0, 0, 939, 235],
           "reason": "bounds change"
@@ -37,6 +42,11 @@
           "reason": "scroll"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [497, 0, 442, 500],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow BODY",
           "rect": [505, 8, 426, 484],
           "reason": "forced by layout"
@@ -59,6 +69,10 @@
       "reason": "bounds change"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "forced by layout"
     },
@@ -269,6 +283,11 @@
           "reason": "full"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [-539, 0, 939, 235],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [-539, 0, 939, 235],
           "reason": "bounds change"
@@ -294,6 +313,11 @@
           "reason": "scroll"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [29, 0, 371, 600],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow BODY",
           "rect": [37, 8, 355, 584],
           "reason": "forced by layout"
@@ -316,6 +340,10 @@
       "reason": "bounds change"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "forced by layout"
     },
@@ -363,6 +391,16 @@
           "reason": "incremental"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [429, 0, 371, 600],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [29, 0, 371, 600],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow BODY",
           "rect": [437, 8, 355, 584],
           "reason": "forced by layout"
@@ -391,6 +429,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "forced by layout"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/stable/paint/invalidation/resize-iframe-text-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/virtual/stable/paint/invalidation/resize-iframe-text-expected.txt
new file mode 100644
index 0000000..40a3c663
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/stable/paint/invalidation/resize-iframe-text-expected.txt
@@ -0,0 +1,107 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 800],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutIFrame (positioned) IFRAME",
+          "rect": [0, 0, 800, 800],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 759],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [0, 600, 800, 200],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 36],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow BODY",
+          "rect": [8, 8, 784, 20],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow H1",
+          "rect": [8, 700, 600, 37],
+          "reason": "became visible"
+        },
+        {
+          "object": "LayoutText #text",
+          "rect": [8, 8, 324, 19],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [785, 0, 15, 600],
+          "reason": "scroll"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutView #document",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "InlineTextBox 'Test passes if you see \"Success\" after window resizes.'",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutIFrame (positioned) IFRAME",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "VerticalScrollbar",
+      "reason": "scroll"
+    },
+    {
+      "object": "HorizontalScrollbar",
+      "reason": "scroll"
+    },
+    {
+      "object": "LayoutView #document",
+      "reason": "scroll"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutBlockFlow H1",
+      "reason": "became visible"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "became visible"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/stable/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/virtual/stable/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt
index 024270b..95b54eb 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/stable/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/stable/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 785, 836],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [0, 742, 785, 94],
           "reason": "incremental"
@@ -27,6 +32,11 @@
           "reason": "layoutObject insertion"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [10, 94, 728, 90],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutImage IMG",
           "rect": [58, 236, 489, 537],
           "reason": "bounds change"
@@ -45,6 +55,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutIFrame IFRAME id='iframe'",
       "reason": "layoutObject insertion"
     },
@@ -78,7 +92,7 @@
     },
     {
       "object": "LayoutBlockFlow HTML",
-      "reason": "location change"
+      "reason": "forced by layout"
     },
     {
       "object": "LayoutBlockFlow BODY",
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/resize-iframe-text-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/resize-iframe-text-expected.txt
new file mode 100644
index 0000000..f2b69df4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/resize-iframe-text-expected.txt
@@ -0,0 +1,90 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 681],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutIFrame (positioned) IFRAME",
+          "rect": [0, 0, 800, 681],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [0, 600, 800, 81],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [0, 600, 785, 81],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutBlockFlow BODY",
+          "rect": [8, 8, 784, 18],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutText #text",
+          "rect": [8, 8, 346, 18],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [785, 0, 15, 681],
+          "reason": "scroll"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [785, 0, 15, 600],
+          "reason": "scroll"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutView #document",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "InlineTextBox 'Test passes if you see \"Success\" after window resizes.'",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutIFrame (positioned) IFRAME",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "HorizontalScrollbar",
+      "reason": "scroll"
+    },
+    {
+      "object": "LayoutView #document",
+      "reason": "scroll"
+    },
+    {
+      "object": "VerticalScrollbar",
+      "reason": "scroll"
+    },
+    {
+      "object": "LayoutView #document",
+      "reason": "incremental"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/stable/paint/invalidation/resize-iframe-text-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/stable/paint/invalidation/resize-iframe-text-expected.txt
new file mode 100644
index 0000000..f2b69df4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/stable/paint/invalidation/resize-iframe-text-expected.txt
@@ -0,0 +1,90 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 681],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutIFrame (positioned) IFRAME",
+          "rect": [0, 0, 800, 681],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [0, 600, 800, 81],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [0, 600, 785, 81],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutBlockFlow BODY",
+          "rect": [8, 8, 784, 18],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutText #text",
+          "rect": [8, 8, 346, 18],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [785, 0, 15, 681],
+          "reason": "scroll"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [785, 0, 15, 600],
+          "reason": "scroll"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutView #document",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "InlineTextBox 'Test passes if you see \"Success\" after window resizes.'",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutIFrame (positioned) IFRAME",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "HorizontalScrollbar",
+      "reason": "scroll"
+    },
+    {
+      "object": "LayoutView #document",
+      "reason": "scroll"
+    },
+    {
+      "object": "VerticalScrollbar",
+      "reason": "scroll"
+    },
+    {
+      "object": "LayoutView #document",
+      "reason": "incremental"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/resize-iframe-text-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/resize-iframe-text-expected.txt
index b35b467f..9039a5b 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/resize-iframe-text-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/resize-iframe-text-expected.txt
@@ -17,6 +17,16 @@
           "reason": "incremental"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 34],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 785, 677],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [0, 600, 785, 77],
           "reason": "incremental"
@@ -50,6 +60,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "forced by layout"
     },
@@ -84,6 +98,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/paint/invalidation/resize-iframe-text-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-retina/paint/invalidation/resize-iframe-text-expected.txt
index 366ac00f6..a96960b 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-retina/paint/invalidation/resize-iframe-text-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/paint/invalidation/resize-iframe-text-expected.txt
@@ -12,11 +12,21 @@
           "reason": "forced by layout"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 759],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [0, 600, 800, 200],
           "reason": "incremental"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 34],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow BODY",
           "rect": [8, 8, 784, 18],
           "reason": "forced by layout"
@@ -45,6 +55,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "forced by layout"
     },
@@ -77,6 +91,10 @@
       "reason": "scroll"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow H1",
       "reason": "became visible"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/abspos-shift-image-incorrect-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/abspos-shift-image-incorrect-repaint-expected.txt
index a4956893..1b5d71a 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/abspos-shift-image-incorrect-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/abspos-shift-image-incorrect-repaint-expected.txt
@@ -17,6 +17,11 @@
           "reason": "bounds change"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 52],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow (relative positioned) DIV class='imageWrapper'",
           "rect": [704, 0, 700, 236],
           "reason": "bounds change"
@@ -80,6 +85,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow (positioned) DIV id='shiftMe'",
       "reason": "bounds change"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
index 53fdc643..55cf8f2 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
@@ -30,6 +30,16 @@
           "reason": "style change"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 500, 300, 34],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 500, 285, 150],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow BODY",
           "rect": [8, 508, 284, 18],
           "reason": "forced by layout"
@@ -66,6 +76,10 @@
       "reason": "style change"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "forced by layout"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/line-flow-with-floats-9-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/line-flow-with-floats-9-expected.txt
index f5e66e64..578a9781 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/line-flow-with-floats-9-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/line-flow-with-floats-9-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 500, 600],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow P",
           "rect": [8, 569, 418, 21],
           "reason": "incremental"
@@ -118,6 +123,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow P",
       "reason": "incremental"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/resize-iframe-text-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/resize-iframe-text-expected.txt
index f2b69df4..0feedc7a 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/resize-iframe-text-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/resize-iframe-text-expected.txt
@@ -17,6 +17,16 @@
           "reason": "incremental"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 34],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 785, 681],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [0, 600, 785, 81],
           "reason": "incremental"
@@ -50,6 +60,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "forced by layout"
     },
@@ -84,6 +98,10 @@
     {
       "object": "LayoutView #document",
       "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
     }
   ]
 }
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table-shrink-row-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table-shrink-row-repaint-expected.txt
index d380b3e3..b34869d 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table-shrink-row-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/table-shrink-row-repaint-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 785, 1048],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [0, 848, 785, 200],
           "reason": "incremental"
@@ -265,6 +270,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutTableCell TD id='resizeMe'",
       "reason": "incremental"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/text-match-document-change-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/text-match-document-change-expected.txt
index cb161c6..8104edb 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/text-match-document-change-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/text-match-document-change-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [10, 102, 285, 400],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow DIV id='to-be-changed'",
           "rect": [18, 128, 269, 36],
           "reason": "full"
@@ -39,6 +44,10 @@
       "reason": "scroll"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow DIV id='to-be-changed'",
       "reason": "full"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/window-resize-vertical-writing-mode-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
index 244c8ab..6b212b3 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 913, 235],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [0, 0, 913, 235],
           "reason": "bounds change"
@@ -37,6 +42,11 @@
           "reason": "scroll"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [483, 0, 430, 500],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow BODY",
           "rect": [491, 8, 414, 484],
           "reason": "forced by layout"
@@ -59,6 +69,10 @@
       "reason": "bounds change"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "forced by layout"
     },
@@ -269,6 +283,11 @@
           "reason": "full"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [-513, 0, 913, 235],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [-513, 0, 913, 235],
           "reason": "bounds change"
@@ -294,6 +313,11 @@
           "reason": "scroll"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [39, 0, 361, 600],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow BODY",
           "rect": [47, 8, 345, 584],
           "reason": "forced by layout"
@@ -316,6 +340,10 @@
       "reason": "bounds change"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "forced by layout"
     },
@@ -363,6 +391,16 @@
           "reason": "incremental"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [439, 0, 361, 600],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [39, 0, 361, 600],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow BODY",
           "rect": [447, 8, 345, 584],
           "reason": "forced by layout"
@@ -391,6 +429,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "forced by layout"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/paint/invalidation/resize-iframe-text-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/paint/invalidation/resize-iframe-text-expected.txt
new file mode 100644
index 0000000..0feedc7a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/paint/invalidation/resize-iframe-text-expected.txt
@@ -0,0 +1,108 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 681],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutIFrame (positioned) IFRAME",
+          "rect": [0, 0, 800, 681],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [0, 600, 800, 81],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 34],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 785, 681],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [0, 600, 785, 81],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutBlockFlow BODY",
+          "rect": [8, 8, 784, 18],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutText #text",
+          "rect": [8, 8, 346, 18],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [785, 0, 15, 681],
+          "reason": "scroll"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [785, 0, 15, 600],
+          "reason": "scroll"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutView #document",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "InlineTextBox 'Test passes if you see \"Success\" after window resizes.'",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutIFrame (positioned) IFRAME",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "HorizontalScrollbar",
+      "reason": "scroll"
+    },
+    {
+      "object": "LayoutView #document",
+      "reason": "scroll"
+    },
+    {
+      "object": "VerticalScrollbar",
+      "reason": "scroll"
+    },
+    {
+      "object": "LayoutView #document",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/abspos-shift-image-incorrect-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/abspos-shift-image-incorrect-repaint-expected.txt
index 0666d0f..044c8161 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/abspos-shift-image-incorrect-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/abspos-shift-image-incorrect-repaint-expected.txt
@@ -17,6 +17,11 @@
           "reason": "bounds change"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 52],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow (relative positioned) DIV class='imageWrapper'",
           "rect": [704, 0, 700, 236],
           "reason": "bounds change"
@@ -80,6 +85,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow (positioned) DIV id='shiftMe'",
       "reason": "bounds change"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
index c38994e..66790ed 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/compositing/iframe-inside-squashed-layer-expected.txt
@@ -30,6 +30,16 @@
           "reason": "style change"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 500, 300, 34],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 500, 285, 150],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow BODY",
           "rect": [8, 508, 284, 18],
           "reason": "forced by layout"
@@ -66,6 +76,10 @@
       "reason": "style change"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "forced by layout"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/line-flow-with-floats-9-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/line-flow-with-floats-9-expected.txt
index 8a56550..23255e98 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/line-flow-with-floats-9-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/line-flow-with-floats-9-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 500, 600],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow P",
           "rect": [8, 569, 418, 21],
           "reason": "incremental"
@@ -118,6 +123,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow P",
       "reason": "incremental"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/resize-iframe-text-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/resize-iframe-text-expected.txt
index 542eb4c..f3fbb352 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/resize-iframe-text-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/resize-iframe-text-expected.txt
@@ -17,6 +17,16 @@
           "reason": "incremental"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 34],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 785, 728],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [0, 600, 785, 128],
           "reason": "incremental"
@@ -55,6 +65,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "forced by layout"
     },
@@ -91,6 +105,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow H1",
       "reason": "became visible"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table-shrink-row-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table-shrink-row-repaint-expected.txt
index a3198cc8..ee597de 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table-shrink-row-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/table-shrink-row-repaint-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 785, 1048],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [0, 848, 785, 200],
           "reason": "incremental"
@@ -265,6 +270,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutTableCell TD id='resizeMe'",
       "reason": "incremental"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/text-match-document-change-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/text-match-document-change-expected.txt
index ff00f33..21bba70 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/text-match-document-change-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/text-match-document-change-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [10, 102, 285, 400],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow DIV id='to-be-changed'",
           "rect": [18, 128, 269, 36],
           "reason": "full"
@@ -39,6 +44,10 @@
       "reason": "scroll"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow DIV id='to-be-changed'",
       "reason": "full"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/window-resize-vertical-writing-mode-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
index 3be2d60..24b1d65 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/window-resize-vertical-writing-mode-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 913, 235],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [0, 0, 913, 235],
           "reason": "bounds change"
@@ -37,6 +42,11 @@
           "reason": "scroll"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [483, 0, 430, 500],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow BODY",
           "rect": [491, 8, 414, 484],
           "reason": "forced by layout"
@@ -59,6 +69,10 @@
       "reason": "bounds change"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "forced by layout"
     },
@@ -269,6 +283,11 @@
           "reason": "full"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [-513, 0, 913, 235],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [-513, 0, 913, 235],
           "reason": "bounds change"
@@ -294,6 +313,11 @@
           "reason": "scroll"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [39, 0, 361, 600],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow BODY",
           "rect": [47, 8, 345, 584],
           "reason": "forced by layout"
@@ -316,6 +340,10 @@
       "reason": "bounds change"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "forced by layout"
     },
@@ -363,6 +391,16 @@
           "reason": "incremental"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [439, 0, 361, 600],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [39, 0, 361, 600],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow BODY",
           "rect": [447, 8, 345, 584],
           "reason": "forced by layout"
@@ -391,6 +429,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow BODY",
       "reason": "forced by layout"
     },
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/stable/paint/invalidation/resize-iframe-text-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/stable/paint/invalidation/resize-iframe-text-expected.txt
new file mode 100644
index 0000000..f3fbb352
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/stable/paint/invalidation/resize-iframe-text-expected.txt
@@ -0,0 +1,121 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 728],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "paintInvalidations": [
+        {
+          "object": "LayoutIFrame (positioned) IFRAME",
+          "rect": [0, 0, 800, 728],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [0, 600, 800, 128],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 34],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 785, 728],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [0, 600, 785, 128],
+          "reason": "incremental"
+        },
+        {
+          "object": "LayoutBlockFlow BODY",
+          "rect": [8, 8, 784, 18],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutBlockFlow H1",
+          "rect": [8, 700, 600, 28],
+          "reason": "became visible"
+        },
+        {
+          "object": "LayoutText #text",
+          "rect": [8, 8, 346, 17],
+          "reason": "forced by layout"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [785, 0, 15, 728],
+          "reason": "scroll"
+        },
+        {
+          "object": "LayoutView #document",
+          "rect": [785, 0, 15, 600],
+          "reason": "scroll"
+        }
+      ]
+    }
+  ],
+  "objectPaintInvalidations": [
+    {
+      "object": "LayoutView #document",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutBlockFlow BODY",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutText #text",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "InlineTextBox 'Test passes if you see \"Success\" after window resizes.'",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutIFrame (positioned) IFRAME",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "HorizontalScrollbar",
+      "reason": "scroll"
+    },
+    {
+      "object": "LayoutView #document",
+      "reason": "scroll"
+    },
+    {
+      "object": "VerticalScrollbar",
+      "reason": "scroll"
+    },
+    {
+      "object": "LayoutView #document",
+      "reason": "incremental"
+    },
+    {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
+      "object": "LayoutBlockFlow H1",
+      "reason": "became visible"
+    },
+    {
+      "object": "RootInlineBox",
+      "reason": "became visible"
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt
index a88792e..504d969f 100644
--- a/third_party/WebKit/LayoutTests/virtual/stable/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/stable/paint/invalidation/shift-relative-positioned-container-with-image-addition-expected.txt
@@ -7,6 +7,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 785, 829],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [0, 735, 785, 94],
           "reason": "incremental"
@@ -27,6 +32,11 @@
           "reason": "layoutObject insertion"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [10, 88, 728, 90],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutImage IMG",
           "rect": [58, 230, 489, 537],
           "reason": "bounds change"
@@ -45,6 +55,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutIFrame IFRAME id='iframe'",
       "reason": "layoutObject insertion"
     },
@@ -78,7 +92,7 @@
     },
     {
       "object": "LayoutBlockFlow HTML",
-      "reason": "location change"
+      "reason": "forced by layout"
     },
     {
       "object": "LayoutBlockFlow BODY",
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/paint/invalidation/window-resize-percent-html-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/paint/invalidation/window-resize-percent-html-expected.txt
index 5513527..141cf54 100644
--- a/third_party/WebKit/LayoutTests/virtual/stable/paint/invalidation/window-resize-percent-html-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/stable/paint/invalidation/window-resize-percent-html-expected.txt
@@ -12,6 +12,11 @@
           "reason": "incremental"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 600, 250],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow DIV",
           "rect": [0, 62, 300, 63],
           "reason": "incremental"
@@ -25,6 +30,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow DIV",
       "reason": "incremental"
     }
@@ -39,6 +48,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 600, 125],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [400, 0, 200, 250],
           "reason": "incremental"
@@ -57,6 +71,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow DIV",
       "reason": "incremental"
     }
@@ -76,6 +94,11 @@
           "reason": "incremental"
         },
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 400, 300],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutBlockFlow DIV",
           "rect": [0, 62, 200, 88],
           "reason": "incremental"
@@ -89,6 +112,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow DIV",
       "reason": "incremental"
     }
@@ -103,6 +130,11 @@
       "drawsContent": true,
       "paintInvalidations": [
         {
+          "object": "LayoutBlockFlow HTML",
+          "rect": [0, 0, 800, 300],
+          "reason": "forced by layout"
+        },
+        {
           "object": "LayoutView #document",
           "rect": [400, 0, 400, 600],
           "reason": "incremental"
@@ -121,6 +153,10 @@
       "reason": "incremental"
     },
     {
+      "object": "LayoutBlockFlow HTML",
+      "reason": "forced by layout"
+    },
+    {
       "object": "LayoutBlockFlow DIV",
       "reason": "incremental"
     }
diff --git a/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl
index bbea073..d32da99 100644
--- a/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl
@@ -283,7 +283,7 @@
 void crossOriginNamedEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info) {
   Vector<String> names;
   for (const auto& attribute : kCrossOriginAttributeTable)
-    names.append(attribute.name);
+    names.push_back(attribute.name);
 
   v8SetReturnValue(
       info,
diff --git a/third_party/WebKit/Source/bindings/templates/methods.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/methods.cpp.tmpl
index d7009da8..a0a4779 100644
--- a/third_party/WebKit/Source/bindings/templates/methods.cpp.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/methods.cpp.tmpl
@@ -188,7 +188,7 @@
     {{throw_argument_error(method, argument, "parameter %(index)d is not of type '%(type)s'.")}}
     return;
   }
-  {{argument.name}}.append(V8{{argument.idl_type}}::toImpl(v8::Local<v8::Object>::Cast(info[i])));
+  {{argument.name}}.push_back(V8{{argument.idl_type}}::toImpl(v8::Local<v8::Object>::Cast(info[i])));
 }
 {% elif argument.is_dictionary %}
 {% if not argument.use_permissive_dictionary_conversion %}
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp
index a17fe42c2..da7325d 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp
@@ -437,7 +437,7 @@
 void crossOriginNamedEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info) {
   Vector<String> names;
   for (const auto& attribute : kCrossOriginAttributeTable)
-    names.append(attribute.name);
+    names.push_back(attribute.name);
 
   v8SetReturnValue(
       info,
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
index d482b46..af019af 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
@@ -7869,7 +7869,7 @@
 
       return;
     }
-    variadicTestInterfaceEmptyArgs.append(V8TestInterfaceEmpty::toImpl(v8::Local<v8::Object>::Cast(info[i])));
+    variadicTestInterfaceEmptyArgs.push_back(V8TestInterfaceEmpty::toImpl(v8::Local<v8::Object>::Cast(info[i])));
   }
 
   impl->voidMethodVariadicTestInterfaceEmptyArg(variadicTestInterfaceEmptyArgs);
@@ -7904,7 +7904,7 @@
 
       return;
     }
-    variadicTestInterfaceEmptyArgs.append(V8TestInterfaceEmpty::toImpl(v8::Local<v8::Object>::Cast(info[i])));
+    variadicTestInterfaceEmptyArgs.push_back(V8TestInterfaceEmpty::toImpl(v8::Local<v8::Object>::Cast(info[i])));
   }
 
   impl->voidMethodTestInterfaceEmptyArgVariadicTestInterfaceEmptyArg(testInterfaceEmptyArg, variadicTestInterfaceEmptyArgs);
@@ -7926,7 +7926,7 @@
 
       return;
     }
-    variadicTestInterfaceGarbageCollectedArg.append(V8TestInterfaceGarbageCollected::toImpl(v8::Local<v8::Object>::Cast(info[i])));
+    variadicTestInterfaceGarbageCollectedArg.push_back(V8TestInterfaceGarbageCollected::toImpl(v8::Local<v8::Object>::Cast(info[i])));
   }
 
   impl->voidMethodVariadicTestInterfaceGarbageCollectedArg(variadicTestInterfaceGarbageCollectedArg);
@@ -10575,7 +10575,7 @@
 
       return;
     }
-    testInterfaceEmptyArg.append(V8TestInterfaceEmpty::toImpl(v8::Local<v8::Object>::Cast(info[i])));
+    testInterfaceEmptyArg.push_back(V8TestInterfaceEmpty::toImpl(v8::Local<v8::Object>::Cast(info[i])));
   }
 
   impl->legacyInterfaceTypeCheckingVoidMethodTestInterfaceEmptyVariadicArg(testInterfaceEmptyArg);
diff --git a/third_party/WebKit/Source/core/dom/Node.cpp b/third_party/WebKit/Source/core/dom/Node.cpp
index caf5618..600af2f1 100644
--- a/third_party/WebKit/Source/core/dom/Node.cpp
+++ b/third_party/WebKit/Source/core/dom/Node.cpp
@@ -109,6 +109,24 @@
 
 namespace blink {
 
+namespace {
+
+// TODO(crbug.com/545926): Unsafe hack to avoid triggering the
+// ThreadRestrictionVerifier on StringImpl. This should be fixed completely, and
+// we should always avoid accessing these strings from the impl thread.
+// Currently code that calls into this method from the impl thread tries to make
+// sure that the main thread is not running at this time.
+void appendUnsafe(StringBuilder& builder, const String& offThreadString) {
+  StringImpl* impl = offThreadString.impl();
+  if (impl) {
+    builder.append(impl->is8Bit()
+                       ? StringView(impl->characters8(), impl->length())
+                       : StringView(impl->characters16(), impl->length()));
+  }
+}
+
+}  // namespace
+
 using namespace HTMLNames;
 
 struct SameSizeAsNode : EventTarget {
@@ -1488,12 +1506,12 @@
 
 String Node::debugName() const {
   StringBuilder name;
-  name.append(debugNodeName());
+  appendUnsafe(name, debugNodeName());
   if (isElementNode()) {
     const Element& thisElement = toElement(*this);
     if (thisElement.hasID()) {
       name.append(" id=\'");
-      name.append(thisElement.getIdAttribute());
+      appendUnsafe(name, thisElement.getIdAttribute());
       name.append('\'');
     }
 
@@ -1502,7 +1520,7 @@
       for (size_t i = 0; i < thisElement.classNames().size(); ++i) {
         if (i > 0)
           name.append(' ');
-        name.append(thisElement.classNames()[i]);
+        appendUnsafe(name, thisElement.classNames()[i]);
       }
       name.append('\'');
     }
diff --git a/third_party/WebKit/Source/core/layout/LayoutView.cpp b/third_party/WebKit/Source/core/layout/LayoutView.cpp
index 58d135e..a5f630b1 100644
--- a/third_party/WebKit/Source/core/layout/LayoutView.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutView.cpp
@@ -247,6 +247,8 @@
 
   SubtreeLayoutScope layoutScope(*this);
 
+  LayoutRect oldLayoutOverflowRect = layoutOverflowRect();
+
   // Use calcWidth/Height to get the new width/height, since this will take the
   // full page zoom factor into account.
   bool relayoutChildren =
@@ -281,6 +283,17 @@
 
   layoutContent();
 
+  if (layoutOverflowRect() != oldLayoutOverflowRect) {
+    // The document element paints the viewport background, so we need to
+    // invalidate it when layout overflow changes.
+    // FIXME: Improve viewport background styling/invalidation/painting.
+    // crbug.com/475115
+    if (Element* documentElement = document().documentElement()) {
+      if (LayoutObject* rootObject = documentElement->layoutObject())
+        rootObject->setShouldDoFullPaintInvalidation();
+    }
+  }
+
 #if ENABLE(ASSERT)
   checkLayoutState();
 #endif
diff --git a/third_party/WebKit/Source/core/paint/BoxPaintInvalidatorTest.cpp b/third_party/WebKit/Source/core/paint/BoxPaintInvalidatorTest.cpp
index 975a31a4..d4f4992 100644
--- a/third_party/WebKit/Source/core/paint/BoxPaintInvalidatorTest.cpp
+++ b/third_party/WebKit/Source/core/paint/BoxPaintInvalidatorTest.cpp
@@ -238,14 +238,22 @@
     // is clipped.
     // TODO(skobes): Treat LayoutView in the same way as normal objects having
     // background-attachment: local. crbug.com/568847.
-    EXPECT_FALSE(layoutView()
-                     .layer()
-                     ->graphicsLayerBacking()
-                     ->getRasterInvalidationTracking());
+    // TODO(wangxianzhu): Temporary for crbug.com/680745.
+    // EXPECT_FALSE(layoutView()
+    //                  .layer()
+    //                  ->graphicsLayerBacking()
+    //                  ->getRasterInvalidationTracking());
+    EXPECT_EQ(1u, layoutView()
+                      .layer()
+                      ->graphicsLayerBacking()
+                      ->getRasterInvalidationTracking()
+                      ->trackedRasterInvalidations.size());
   } else {
     const auto& rasterInvalidations =
         getRasterInvalidationTracking()->trackedRasterInvalidations;
-    ASSERT_EQ(1u, rasterInvalidations.size());
+    // TODO(wangxianzhu): Temporary for crbug.com/680745.
+    // ASSERT_EQ(1u, rasterInvalidations.size());
+    ASSERT_EQ(2u, rasterInvalidations.size());
     EXPECT_EQ(IntRect(0, 2000, 800, 1000), rasterInvalidations[0].rect);
     EXPECT_EQ(static_cast<const DisplayItemClient*>(&layoutView()),
               rasterInvalidations[0].client);
@@ -292,7 +300,9 @@
   } else {
     const auto& rasterInvalidations =
         getRasterInvalidationTracking()->trackedRasterInvalidations;
-    ASSERT_EQ(1u, rasterInvalidations.size());
+    // TODO(wangxianzhu): Temporary for crbug.com/680745.
+    // ASSERT_EQ(1u, rasterInvalidations.size());
+    ASSERT_EQ(2u, rasterInvalidations.size());
     EXPECT_EQ(IntRect(0, 0, 800, 3000), rasterInvalidations[0].rect);
     EXPECT_EQ(static_cast<const DisplayItemClient*>(&layoutView()),
               rasterInvalidations[0].client);
@@ -333,7 +343,10 @@
   content->setAttribute(HTMLNames::styleAttr, "height: 500px");
   document().view()->updateAllLifecyclePhases();
   // No invalidation because the changed part of layout overflow is clipped.
-  EXPECT_FALSE(getRasterInvalidationTracking());
+  // TODO(wangxianzhu): Temporary for crbug.com/680745.
+  // EXPECT_FALSE(getRasterInvalidationTracking());
+  EXPECT_EQ(1u,
+            getRasterInvalidationTracking()->trackedRasterInvalidations.size());
   document().view()->setTracksPaintInvalidations(false);
 
   // Resize the iframe.
@@ -392,7 +405,9 @@
   document().view()->updateAllLifecyclePhases();
   const auto* rasterInvalidations =
       &getRasterInvalidationTracking()->trackedRasterInvalidations;
-  ASSERT_EQ(1u, rasterInvalidations->size());
+  // TODO(wangxianzhu): Temporary for crbug.com/680745.
+  // ASSERT_EQ(1u, rasterInvalidations->size());
+  ASSERT_EQ(2u, rasterInvalidations->size());
   EXPECT_EQ(IntRect(0, 0, 100, 100), (*rasterInvalidations)[0].rect);
   EXPECT_EQ(static_cast<const DisplayItemClient*>(frameLayoutView),
             (*rasterInvalidations)[0].client);
diff --git a/third_party/WebKit/Source/devtools/front_end/components/JavaScriptAutocomplete.js b/third_party/WebKit/Source/devtools/front_end/components/JavaScriptAutocomplete.js
index 0314ec0..b9aad4f 100644
--- a/third_party/WebKit/Source/devtools/front_end/components/JavaScriptAutocomplete.js
+++ b/third_party/WebKit/Source/devtools/front_end/components/JavaScriptAutocomplete.js
@@ -15,13 +15,18 @@
  */
 Components.JavaScriptAutocomplete.completionsForTextInCurrentContext = function(text, query, force) {
   var index;
-  var stopChars = new Set(' =:({;,!+-*/&|^<>`'.split(''));
+  var stopChars = new Set('=:({;,!+-*/&|^<>`'.split(''));
+  var whiteSpaceChars = new Set(' \r\n\t'.split(''));
+  var continueChars = new Set('[. \r\n\t'.split(''));
+
   for (index = text.length - 1; index >= 0; index--) {
     // Pass less stop characters to rangeOfWord so the range will be a more complete expression.
     if (stopChars.has(text.charAt(index)))
       break;
+    if (whiteSpaceChars.has(text.charAt(index)) && !continueChars.has(text.charAt(index - 1)))
+      break;
   }
-  var clippedExpression = text.substring(index + 1);
+  var clippedExpression = text.substring(index + 1).trim();
   var bracketCount = 0;
 
   index = clippedExpression.length - 1;
@@ -37,7 +42,7 @@
     }
     index--;
   }
-  clippedExpression = clippedExpression.substring(index + 1);
+  clippedExpression = clippedExpression.substring(index + 1).trim();
 
   return Components.JavaScriptAutocomplete.completionsForExpression(clippedExpression, query, force);
 };
@@ -56,7 +61,7 @@
   var lastIndex = expressionString.length - 1;
 
   var dotNotation = (expressionString[lastIndex] === '.');
-  var bracketNotation = (expressionString[lastIndex] === '[');
+  var bracketNotation = (expressionString.length > 1 && expressionString[lastIndex] === '[');
 
   if (dotNotation || bracketNotation)
     expressionString = expressionString.substr(0, lastIndex);
@@ -67,9 +72,6 @@
   if ((expressionString && !isNaN(expressionString)) || (!expressionString && query && !isNaN(query)))
     return Promise.resolve([]);
 
-  // User is creating an array, do not suggest anything.
-  if (bracketNotation && !expressionString)
-    return Promise.resolve([]);
 
   if (!query && !expressionString && !force)
     return Promise.resolve([]);
diff --git a/third_party/WebKit/Source/devtools/front_end/console/ConsolePrompt.js b/third_party/WebKit/Source/devtools/front_end/console/ConsolePrompt.js
index 6fab893..c1f8a5ee 100644
--- a/third_party/WebKit/Source/devtools/front_end/console/ConsolePrompt.js
+++ b/third_party/WebKit/Source/devtools/front_end/console/ConsolePrompt.js
@@ -244,7 +244,7 @@
     var lineText = this._editor.line(lineNumber);
     var index;
     for (index = lineText.length - 1; index >= 0; index--) {
-      if (' =:[({;,!+-*/&|^<>.'.indexOf(lineText.charAt(index)) !== -1)
+      if (' =:[({;,!+-*/&|^<>.\t\r\n'.indexOf(lineText.charAt(index)) !== -1)
         break;
     }
     return new Common.TextRange(lineNumber, index + 1, lineNumber, columnNumber);
@@ -262,9 +262,12 @@
     var before = this._editor.text(new Common.TextRange(0, 0, queryRange.startLine, queryRange.startColumn));
     var historyWords = this._historyCompletions(query, force);
 
-    var excludedTokens = new Set(['js-comment', 'js-string-2']);
-    if (!before.endsWith('['))
+    var excludedTokens = new Set(['js-comment', 'js-string-2', 'js-def']);
+    var trimmedBefore = before.trim();
+    if (!trimmedBefore.endsWith('['))
       excludedTokens.add('js-string');
+    if (!trimmedBefore.endsWith('.'))
+      excludedTokens.add('js-property');
     if (excludedTokens.has(currentTokenType))
       return Promise.resolve(historyWords);
 
diff --git a/third_party/WebKit/Source/devtools/front_end/quick_open/FilteredListWidget.js b/third_party/WebKit/Source/devtools/front_end/quick_open/FilteredListWidget.js
index 5912967..7b9266ec6 100644
--- a/third_party/WebKit/Source/devtools/front_end/quick_open/FilteredListWidget.js
+++ b/third_party/WebKit/Source/devtools/front_end/quick_open/FilteredListWidget.js
@@ -271,6 +271,7 @@
 
     this._progressBarElement.style.transform = 'scaleX(0)';
     this._progressBarElement.classList.remove('filtered-widget-progress-fade');
+    this._progressBarElement.classList.remove('hidden');
 
     var query = this._delegate.rewriteQuery(this._value());
     this._query = query;
@@ -284,6 +285,7 @@
     var bestItemsToCollect = 100;
     var minBestScore = 0;
     var overflowItems = [];
+    var scoreStartTime = window.performance.now();
 
     var maxWorkItems = Number.constrain(10, 500, (this._delegate.itemCount() / 10) | 0);
 
@@ -338,11 +340,16 @@
       // Process everything in chunks.
       if (i < this._delegate.itemCount()) {
         this._scoringTimer = setTimeout(scoreItems.bind(this, i), 0);
-        this._progressBarElement.style.transform = 'scaleX(' + i / this._delegate.itemCount() + ')';
+        if (window.performance.now() - scoreStartTime > 50)
+          this._progressBarElement.style.transform = 'scaleX(' + i / this._delegate.itemCount() + ')';
         return;
       }
-      this._progressBarElement.style.transform = 'scaleX(1)';
-      this._progressBarElement.classList.add('filtered-widget-progress-fade');
+      if (window.performance.now() - scoreStartTime > 100) {
+        this._progressBarElement.style.transform = 'scaleX(1)';
+        this._progressBarElement.classList.add('filtered-widget-progress-fade');
+      } else {
+        this._progressBarElement.classList.add('hidden');
+      }
       this._refreshListWithCurrentResult();
     }
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/FilteredUISourceCodeListDelegate.js b/third_party/WebKit/Source/devtools/front_end/sources/FilteredUISourceCodeListDelegate.js
index 86f9371..8286ee1 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/FilteredUISourceCodeListDelegate.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/FilteredUISourceCodeListDelegate.js
@@ -183,9 +183,9 @@
    * @return {string}
    */
   rewriteQuery(query) {
-    if (!query)
-      return query;
-    query = query.trim();
+    query = query ? query.trim() : '';
+    if (!query || query === ':')
+      return '';
     var lineNumberMatch = query.match(/^([^:]+)((?::[^:]*){0,2})$/);
     this._queryLineNumberAndColumnNumber = lineNumberMatch ? lineNumberMatch[2] : '';
     return lineNumberMatch ? lineNumberMatch[1] : query;
diff --git a/third_party/WebKit/Source/devtools/scripts/chrome_debug_launcher/launch_chrome.js b/third_party/WebKit/Source/devtools/scripts/chrome_debug_launcher/launch_chrome.js
index 545c949..426e8906e 100644
--- a/third_party/WebKit/Source/devtools/scripts/chrome_debug_launcher/launch_chrome.js
+++ b/third_party/WebKit/Source/devtools/scripts/chrome_debug_launcher/launch_chrome.js
@@ -100,7 +100,7 @@
     var child;
     try {
         child = childProcess.spawn(filePath, chromeArgs, {
-            stdio: "ignore",
+            stdio: "inherit",
         });
     } catch (error) {
         onLaunchChromeError();
diff --git a/third_party/WebKit/Source/devtools/scripts/compile_frontend.py b/third_party/WebKit/Source/devtools/scripts/compile_frontend.py
index be2c35e..5be7930d 100755
--- a/third_party/WebKit/Source/devtools/scripts/compile_frontend.py
+++ b/third_party/WebKit/Source/devtools/scripts/compile_frontend.py
@@ -75,6 +75,10 @@
 protocol_externs_file = path.join(devtools_frontend_path, 'protocol_externs.js')
 runtime_file = to_platform_path(path.join(devtools_frontend_path, 'Runtime.js'))
 
+closure_compiler_jar = to_platform_path(path.join(scripts_path, 'closure', 'compiler.jar'))
+closure_runner_jar = to_platform_path(path.join(scripts_path, 'closure', 'closure_runner', 'closure_runner.jar'))
+jsdoc_validator_jar = to_platform_path(path.join(scripts_path, 'jsdoc_validator', 'jsdoc_validator.jar'))
+
 type_checked_jsdoc_tags_list = ['param', 'return', 'type', 'enum']
 type_checked_jsdoc_tags_or = '|'.join(type_checked_jsdoc_tags_list)
 
@@ -116,66 +120,70 @@
     return re.search(error_warning_regex, output) != None
 
 
-def verify_jsdoc_extra():
-    files = [to_platform_path(compiled_file) for compiled_file in descriptors.all_compiled_files()]
-    file_list = tempfile.NamedTemporaryFile(mode='wt', delete=False)
-    try:
-        file_list.write('\n'.join(files))
-    finally:
-        file_list.close()
-    return popen(java_exec + ['-jar', jsdoc_validator_jar, '--files-list-name', to_platform_path_exact(file_list.name)]), file_list
+class JSDocChecker:
+    def __init__(self):
+        self._error_found = False
+        self._all_files = descriptors.all_compiled_files()
 
+    def check(self):
+        print 'Verifying JSDoc comments...'
+        self._verify_jsdoc()
+        self._run_jsdoc_validator()
+        return self._error_found
 
-def verify_jsdoc():
-    def file_list():
-        return descriptors.all_compiled_files()
+    def _run_jsdoc_validator(self):
+        files = [to_platform_path(f) for f in self._all_files]
+        file_list = tempfile.NamedTemporaryFile(mode='wt', delete=False)
+        try:
+            file_list.write('\n'.join(files))
+        finally:
+            file_list.close()
+        proc = popen(java_exec + ['-jar', jsdoc_validator_jar, '--files-list-name', to_platform_path_exact(file_list.name)])
+        (out, _) = proc.communicate()
+        if out:
+            print ('JSDoc validator output:%s%s' % (os.linesep, out))
+            self._error_found = True
+        os.remove(file_list.name)
 
-    errors_found = False
-    for full_file_name in file_list():
-        line_index = 0
-        with open(full_file_name, 'r') as sourceFile:
-            for line in sourceFile:
-                line = line.rstrip()
-                line_index += 1
-                if not line:
-                    continue
-                if verify_jsdoc_line(full_file_name, line_index, line):
-                    errors_found = True
-    return errors_found
+    def _verify_jsdoc(self):
+        for full_file_name in self._all_files:
+            line_index = 0
+            with open(full_file_name, 'r') as sourceFile:
+                for line in sourceFile:
+                    line_index += 1
+                    if line.rstrip():
+                        self._verify_jsdoc_line(full_file_name, line_index, line)
 
+    def _verify_jsdoc_line(self, file_name, line_index, line):
+        def print_error(message, error_position):
+            print '%s:%s: ERROR - %s%s%s%s%s%s' % (file_name, line_index, message, os.linesep, line, os.linesep, ' ' * error_position + '^', os.linesep)
 
-def verify_jsdoc_line(file_name, line_index, line):
-    def print_error(message, error_position):
-        print '%s:%s: ERROR - %s%s%s%s%s%s' % (file_name, line_index, message, os.linesep, line, os.linesep, ' ' * error_position + '^', os.linesep)
+        known_css = {}
+        match = re.search(invalid_type_regex, line)
+        if match:
+            print_error('Type "%s" nullability not marked explicitly with "?" (nullable) or "!" (non-nullable)' % match.group(1), match.start(1))
+            self._error_found = True
 
-    known_css = {}
-    errors_found = False
-    match = re.search(invalid_type_regex, line)
-    if match:
-        print_error('Type "%s" nullability not marked explicitly with "?" (nullable) or "!" (non-nullable)' % match.group(1), match.start(1))
-        errors_found = True
+        match = re.search(invalid_non_object_type_regex, line)
+        if match:
+            print_error('Non-object type explicitly marked with "!" (non-nullable), which is the default and should be omitted', match.start(1))
+            self._error_found = True
 
-    match = re.search(invalid_non_object_type_regex, line)
-    if match:
-        print_error('Non-object type explicitly marked with "!" (non-nullable), which is the default and should be omitted', match.start(1))
-        errors_found = True
+        match = re.search(invalid_type_designator_regex, line)
+        if match:
+            print_error('Type nullability indicator misplaced, should precede type', match.start(1))
+            self._error_found = True
 
-    match = re.search(invalid_type_designator_regex, line)
-    if match:
-        print_error('Type nullability indicator misplaced, should precede type', match.start(1))
-        errors_found = True
-
-    match = re.search(loaded_css_regex, line)
-    if match:
-        file = path.join(devtools_frontend_path, match.group(1))
-        exists = known_css.get(file)
-        if exists is None:
-            exists = path.isfile(file)
-            known_css[file] = exists
-        if not exists:
-            print_error('Dynamically loaded CSS stylesheet is missing in the source tree', match.start(1))
-            errors_found = True
-    return errors_found
+        match = re.search(loaded_css_regex, line)
+        if match:
+            file = path.join(devtools_frontend_path, match.group(1))
+            exists = known_css.get(file)
+            if exists is None:
+                exists = path.isfile(file)
+                known_css[file] = exists
+            if not exists:
+                print_error('Dynamically loaded CSS stylesheet is missing in the source tree', match.start(1))
+                self._error_found = True
 
 
 def find_java():
@@ -214,10 +222,6 @@
 
 java_exec = find_java()
 
-closure_compiler_jar = to_platform_path(path.join(scripts_path, 'closure', 'compiler.jar'))
-closure_runner_jar = to_platform_path(path.join(scripts_path, 'closure', 'closure_runner', 'closure_runner.jar'))
-jsdoc_validator_jar = to_platform_path(path.join(scripts_path, 'jsdoc_validator', 'jsdoc_validator.jar'))
-
 common_closure_args = [
     '--summary_detail_level', '3',
     '--jscomp_error', 'visibility',
@@ -231,20 +235,6 @@
     '--checks-only',
 ]
 
-worker_modules_by_name = {}
-dependents_by_module_name = {}
-
-for module_name in descriptors.application:
-    module = descriptors.modules[module_name]
-    if descriptors.application[module_name].get('type', None) == 'worker':
-        worker_modules_by_name[module_name] = module
-    for dep in module.get('dependencies', []):
-        list = dependents_by_module_name.get(dep)
-        if not list:
-            list = []
-            dependents_by_module_name[dep] = list
-        list.append(module_name)
-
 
 def check_conditional_dependencies():
     errors_found = False
@@ -258,39 +248,6 @@
 
 errors_found |= check_conditional_dependencies()
 
-
-def verify_worker_modules():
-    errors_found = False
-    for name in modules_by_name:
-        for dependency in modules_by_name[name].get('dependencies', []):
-            if dependency in worker_modules_by_name:
-                log_error('Module "%s" may not depend on the worker module "%s"' % (name, dependency))
-                errors_found = True
-    return errors_found
-
-errors_found |= verify_worker_modules()
-
-
-def check_duplicate_files():
-
-    def check_module(module, seen_files, seen_modules):
-        name = module['name']
-        seen_modules[name] = True
-        for dep_name in module.get('dependencies', []):
-            if not dep_name in seen_modules:
-                check_module(modules_by_name[dep_name], seen_files, seen_modules)
-        for source in module.get('scripts', []):
-            referencing_module = seen_files.get(source)
-            if referencing_module:
-                log_error('Duplicate use of %s in "%s" (previously seen in "%s")' % (source, name, referencing_module))
-            seen_files[source] = name
-
-    for module_name in worker_modules_by_name:
-        check_module(worker_modules_by_name[module_name], {}, {})
-
-print 'Checking duplicate files across modules...'
-check_duplicate_files()
-
 print 'Compiling frontend...'
 
 temp_devtools_path = tempfile.mkdtemp()
@@ -339,18 +296,7 @@
 ]
 devtools_js_compile_proc = popen(devtools_js_compile_command)
 
-print 'Verifying JSDoc comments...'
-errors_found |= verify_jsdoc()
-(jsdoc_validator_proc, jsdoc_validator_file_list) = verify_jsdoc_extra()
-
-print
-
-(jsdoc_validator_out, _) = jsdoc_validator_proc.communicate()
-if jsdoc_validator_out:
-    print ('JSDoc validator output:%s%s' % (os.linesep, jsdoc_validator_out))
-    errors_found = True
-
-os.remove(jsdoc_validator_file_list.name)
+errors_found |= JSDocChecker().check()
 
 (devtools_js_compile_out, _) = devtools_js_compile_proc.communicate()
 print 'devtools_compatibility.js compilation output:%s' % os.linesep, devtools_js_compile_out
diff --git a/third_party/WebKit/Source/devtools/scripts/npm_test.js b/third_party/WebKit/Source/devtools/scripts/npm_test.js
index b493f0c7a..37a93ac 100644
--- a/third_party/WebKit/Source/devtools/scripts/npm_test.js
+++ b/third_party/WebKit/Source/devtools/scripts/npm_test.js
@@ -35,8 +35,7 @@
     if (!IS_FETCH_CONTENT_SHELL && hasUserCompiledContentShell) {
         var outDir = path.resolve(RELEASE_PATH, "..");
         if (!IS_DEBUG_ENABLED) {
-            console.log("Compiling devtools frontend");
-            shell(`ninja -C ${RELEASE_PATH} devtools_frontend_resources`);
+            compileFrontend();
         }
         runTests(outDir, IS_DEBUG_ENABLED);
         return;
@@ -55,6 +54,18 @@
 }
 main();
 
+function compileFrontend()
+{
+    console.log("Compiling devtools frontend");
+    try {
+        shell(`ninja -C ${RELEASE_PATH} devtools_frontend_resources`);
+    } catch (err) {
+        console.log(err.stdout.toString());
+        console.log('ERROR: Cannot compile frontend\n' + err);
+        process.exit(1);
+    }
+}
+
 function onUploadedCommitPosition(commitPosition)
 {
     var contentShellDirPath = path.resolve(CACHE_PATH, commitPosition, "out", "Release");
diff --git a/third_party/WebKit/Source/modules/webdatabase/SQLTransactionClient.cpp b/third_party/WebKit/Source/modules/webdatabase/SQLTransactionClient.cpp
index 47dbb0ee..3cbac44 100644
--- a/third_party/WebKit/Source/modules/webdatabase/SQLTransactionClient.cpp
+++ b/third_party/WebKit/Source/modules/webdatabase/SQLTransactionClient.cpp
@@ -65,16 +65,14 @@
   String databaseName = database->stringIdentifier();
   ExecutionContext* executionContext =
       database->getDatabaseContext()->getExecutionContext();
+  SecurityOrigin* origin = database->getSecurityOrigin();
   if (!executionContext->isContextThread()) {
     executionContext->postTask(
         TaskType::DatabaseAccess, BLINK_FROM_HERE,
-        createCrossThreadTask(
-            &databaseModifiedCrossThread,
-            executionContext->getSecurityOrigin()->toRawString(),
-            databaseName));
+        createCrossThreadTask(&databaseModifiedCrossThread,
+                              origin->toRawString(), databaseName));
   } else {
-    databaseModified(WebSecurityOrigin(executionContext->getSecurityOrigin()),
-                     databaseName);
+    databaseModified(WebSecurityOrigin(origin), databaseName);
   }
 }
 
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
index 7930eae..7ec6b2e 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
@@ -599,7 +599,7 @@
   ContextProviderCreationInfo creationInfo;
   creationInfo.contextAttributes = contextAttributes;
   creationInfo.glInfo = glInfo;
-  creationInfo.url = url;
+  creationInfo.url = url.copy();
   RefPtr<WebTaskRunner> taskRunner =
       Platform::current()->mainThread()->getWebTaskRunner();
   taskRunner->postTask(BLINK_FROM_HERE,
diff --git a/third_party/WebKit/Source/wtf/RefCounted.h b/third_party/WebKit/Source/wtf/RefCounted.h
index 398db7b..a4847ef 100644
--- a/third_party/WebKit/Source/wtf/RefCounted.h
+++ b/third_party/WebKit/Source/wtf/RefCounted.h
@@ -43,7 +43,7 @@
  public:
   void ref() const {
 #if CHECK_REF_COUNTED_LIFECYCLE
-    m_verifier.onRef(m_refCount);
+    SECURITY_DCHECK(m_verifier.onRef(m_refCount));
     DCHECK(!m_adoptionIsRequired);
 #endif
     SECURITY_DCHECK(!m_deletionHasBegun);
@@ -53,14 +53,14 @@
   bool hasOneRef() const {
     SECURITY_DCHECK(!m_deletionHasBegun);
 #if CHECK_REF_COUNTED_LIFECYCLE
-    m_verifier.checkSafeToUse();
+    SECURITY_DCHECK(m_verifier.isSafeToUse());
 #endif
     return m_refCount == 1;
   }
 
   int refCount() const {
 #if CHECK_REF_COUNTED_LIFECYCLE
-    m_verifier.checkSafeToUse();
+    SECURITY_DCHECK(m_verifier.isSafeToUse());
 #endif
     return m_refCount;
   }
@@ -90,7 +90,7 @@
   bool derefBase() const {
     SECURITY_DCHECK(!m_deletionHasBegun);
 #if CHECK_REF_COUNTED_LIFECYCLE
-    m_verifier.onDeref(m_refCount);
+    SECURITY_DCHECK(m_verifier.onDeref(m_refCount));
     DCHECK(!m_adoptionIsRequired);
 #endif
 
diff --git a/third_party/WebKit/Source/wtf/ThreadRestrictionVerifier.h b/third_party/WebKit/Source/wtf/ThreadRestrictionVerifier.h
index 15ea5fa..5af787c 100644
--- a/third_party/WebKit/Source/wtf/ThreadRestrictionVerifier.h
+++ b/third_party/WebKit/Source/wtf/ThreadRestrictionVerifier.h
@@ -48,14 +48,10 @@
  public:
   ThreadRestrictionVerifier() : m_shared(false), m_owningThread(0) {}
 
-  void checkSafeToUse() const {
-    // If this assert fires, it either indicates a thread safety issue or
-    // that the verification needs to change.
-    SECURITY_DCHECK(isSafeToUse());
-  }
-
-  // Call onRef() before refCount is incremented in ref().
-  void onRef(int refCount) {
+  // Call onRef() before refCount is incremented in ref(). Returns whether the
+  // ref() is safe.
+  template <typename COUNTERTYPE>
+  bool onRef(COUNTERTYPE refCount) {
     // Start thread verification as soon as the ref count gets to 2. This
     // heuristic reflects the fact that items are often created on one
     // thread and then given to another thread to be used.
@@ -66,17 +62,24 @@
     // explicit.
     if (refCount == 1)
       setShared(true);
-    checkSafeToUse();
+    return isSafeToUse();
   }
 
-  // Call onDeref() before refCount is decremented in deref().
-  void onDeref(int refCount) {
-    checkSafeToUse();
-
+  // Call onDeref() before refCount is decremented in deref(). Returns whether
+  // the deref() is safe.
+  template <typename COUNTERTYPE>
+  bool onDeref(COUNTERTYPE refCount) {
+    bool safe = isSafeToUse();
     // Stop thread verification when the ref goes to 1 because it
     // is safe to be passed to another thread at this point.
     if (refCount == 2)
       setShared(false);
+    return safe;
+  }
+
+  // Is it OK to use the object at this moment on the current thread?
+  bool isSafeToUse() const {
+    return !m_shared || m_owningThread == currentThread();
   }
 
  private:
@@ -94,14 +97,6 @@
     m_owningThread = currentThread();
   }
 
-  // Is it OK to use the object at this moment on the current thread?
-  bool isSafeToUse() const {
-    if (!m_shared)
-      return true;
-
-    return m_owningThread == currentThread();
-  }
-
   bool m_shared;
 
   ThreadIdentifier m_owningThread;
diff --git a/third_party/WebKit/Source/wtf/text/StringImpl.cpp b/third_party/WebKit/Source/wtf/text/StringImpl.cpp
index 641fbc9..f8b6b7b 100644
--- a/third_party/WebKit/Source/wtf/text/StringImpl.cpp
+++ b/third_party/WebKit/Source/wtf/text/StringImpl.cpp
@@ -32,6 +32,7 @@
 #include "wtf/allocator/Partitions.h"
 #include "wtf/text/AtomicString.h"
 #include "wtf/text/AtomicStringTable.h"
+#include "wtf/text/CString.h"
 #include "wtf/text/CharacterNames.h"
 #include "wtf/text/StringBuffer.h"
 #include "wtf/text/StringHash.h"
@@ -54,8 +55,16 @@
 
 using namespace Unicode;
 
-static_assert(sizeof(StringImpl) == 3 * sizeof(int),
+// As of Jan 2017, StringImpl needs 2 * sizeof(int) + 29 bits of data, and
+// sizeof(ThreadRestrictionVerifier) is 16 bytes. Thus, in DCHECK mode the
+// class may be padded to 32 bytes.
+#if DCHECK_IS_ON()
+static_assert(sizeof(StringImpl) <= 8 * sizeof(int),
               "StringImpl should stay small");
+#else
+static_assert(sizeof(StringImpl) <= 3 * sizeof(int),
+              "StringImpl should stay small");
+#endif
 
 #ifdef STRING_STATS
 
@@ -300,7 +309,7 @@
     AtomicStringTable::instance().remove(this);
 }
 
-void StringImpl::destroyIfNotStatic() {
+void StringImpl::destroyIfNotStatic() const {
   if (!isStatic())
     delete this;
 }
@@ -324,6 +333,13 @@
   return false;
 }
 
+#if DCHECK_IS_ON()
+std::string StringImpl::asciiForDebugging() const {
+  CString ascii = String(substring(0, 128)).ascii();
+  return std::string(ascii.data(), ascii.length());
+}
+#endif
+
 PassRefPtr<StringImpl> StringImpl::createUninitialized(unsigned length,
                                                        LChar*& data) {
   if (!length) {
@@ -501,13 +517,16 @@
   return true;
 }
 
-PassRefPtr<StringImpl> StringImpl::substring(unsigned start, unsigned length) {
+PassRefPtr<StringImpl> StringImpl::substring(unsigned start,
+                                             unsigned length) const {
   if (start >= m_length)
     return empty();
   unsigned maxLength = m_length - start;
   if (length >= maxLength) {
+    // PassRefPtr has trouble dealing with const arguments. It should be updated
+    // so this const_cast is not necessary.
     if (!start)
-      return this;
+      return const_cast<StringImpl*>(this);
     length = maxLength;
   }
   if (is8Bit())
diff --git a/third_party/WebKit/Source/wtf/text/StringImpl.h b/third_party/WebKit/Source/wtf/text/StringImpl.h
index 6288b56..77dfcc3 100644
--- a/third_party/WebKit/Source/wtf/text/StringImpl.h
+++ b/third_party/WebKit/Source/wtf/text/StringImpl.h
@@ -35,6 +35,10 @@
 #include <limits.h>
 #include <string.h>
 
+#if DCHECK_IS_ON()
+#include "wtf/ThreadRestrictionVerifier.h"
+#endif
+
 #if OS(MACOSX)
 typedef const struct __CFString* CFStringRef;
 #endif
@@ -297,11 +301,25 @@
     return hashSlowCase();
   }
 
-  ALWAYS_INLINE bool hasOneRef() const { return m_refCount == 1; }
+  ALWAYS_INLINE bool hasOneRef() const {
+#if DCHECK_IS_ON()
+    DCHECK(isStatic() || m_verifier.isSafeToUse()) << asciiForDebugging();
+#endif
+    return m_refCount == 1;
+  }
 
-  ALWAYS_INLINE void ref() { ++m_refCount; }
+  ALWAYS_INLINE void ref() const {
+#if DCHECK_IS_ON()
+    DCHECK(isStatic() || m_verifier.onRef(m_refCount)) << asciiForDebugging();
+#endif
+    ++m_refCount;
+  }
 
-  ALWAYS_INLINE void deref() {
+  ALWAYS_INLINE void deref() const {
+#if DCHECK_IS_ON()
+    DCHECK(isStatic() || m_verifier.onDeref(m_refCount))
+        << asciiForDebugging() << " " << currentThread();
+#endif
     if (!--m_refCount)
       destroyIfNotStatic();
   }
@@ -329,7 +347,7 @@
   // its own copy of the string.
   PassRefPtr<StringImpl> isolatedCopy() const;
 
-  PassRefPtr<StringImpl> substring(unsigned pos, unsigned len = UINT_MAX);
+  PassRefPtr<StringImpl> substring(unsigned pos, unsigned len = UINT_MAX) const;
 
   UChar operator[](unsigned i) const {
     SECURITY_DCHECK(i < m_length);
@@ -487,9 +505,13 @@
                                                           StripBehavior);
   NEVER_INLINE unsigned hashSlowCase() const;
 
-  void destroyIfNotStatic();
+  void destroyIfNotStatic() const;
   void updateContainsOnlyASCII() const;
 
+#if DCHECK_IS_ON()
+  std::string asciiForDebugging() const;
+#endif
+
 #ifdef STRING_STATS
   static StringStats m_stringStats;
 #endif
@@ -505,7 +527,10 @@
 #endif
 
  private:
-  unsigned m_refCount;
+#if DCHECK_IS_ON()
+  mutable ThreadRestrictionVerifier m_verifier;
+#endif
+  mutable unsigned m_refCount;
   const unsigned m_length;
   mutable unsigned m_hash : 24;
   mutable unsigned m_containsOnlyASCII : 1;
diff --git a/third_party/WebKit/Source/wtf/text/StringImplTest.cpp b/third_party/WebKit/Source/wtf/text/StringImplTest.cpp
index 7dab3d9..9843a07 100644
--- a/third_party/WebKit/Source/wtf/text/StringImplTest.cpp
+++ b/third_party/WebKit/Source/wtf/text/StringImplTest.cpp
@@ -89,6 +89,9 @@
   static const UChar testWithNonASCIICapitalized[3] = {0x0041, 0x00e1,
                                                        0};  // A\xE1
 
+  // Make sure we support RefPtr<const StringImpl>.
+  RefPtr<const StringImpl> constRef = testStringImpl->isolatedCopy();
+  DCHECK(constRef->hasOneRef());
   EXPECT_TRUE(equal(
       StringImpl::create(testWithNonASCII, 2).get(),
       StringImpl::create(testWithNonASCIICapitalized, 2)->lowerASCII().get()));
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android.py
index 298125a7..34998b1 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/android.py
@@ -506,14 +506,8 @@
 
     @staticmethod
     def _android_server_process_constructor(port, server_name, cmd_line, env=None, more_logging=False):
-        # We need universal_newlines=True, because 'adb shell' for some unknown reason
-        # does newline conversion of unix-style LF into win-style CRLF (and we need
-        # to convert that back). This can cause headaches elsewhere because
-        # server_process' stdout and stderr are now unicode file-like objects,
-        # not binary file-like objects like all of the other ports are.
-        # FIXME: crbug.com/496983.
         return server_process.ServerProcess(port, server_name, cmd_line, env,
-                                            universal_newlines=True, treat_no_data_as_crash=True, more_logging=more_logging)
+                                            treat_no_data_as_crash=True, more_logging=more_logging)
 
 
 class AndroidPerf(SingleFileOutputProfiler):
@@ -881,14 +875,8 @@
                     self._device.serial,
                     stack)
 
-        # The parent method expects stdout and stderr to be byte streams, but
-        # since adb shell does newline conversion, we used universal_newlines
-        # when launching the processes, and hence our stdout and stderr are
-        # text objects that need to be encoded back into bytes.
         return super(ChromiumAndroidDriver, self)._get_crash_log(
-            stdout.encode('utf8', 'replace'),
-            stderr.encode('utf8', 'replace'),
-            newer_than)
+            stdout, stderr, newer_than)
 
     def cmd_line(self, pixel_tests, per_test_args):
         # The returned command line is used to start _server_process. In our case, it's an interactive 'adb shell'.
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
index 7f0bed8..52d4064 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
@@ -1465,12 +1465,12 @@
 
         # We require stdout and stderr to be bytestrings, not character strings.
         if stdout:
-            assert isinstance(stdout, str)
+            assert isinstance(stdout, basestring)
             stdout_lines = stdout.decode('utf8', 'replace').splitlines()
         else:
             stdout_lines = [u'<empty>']
         if stderr:
-            assert isinstance(stderr, str)
+            assert isinstance(stderr, basestring)
             stderr_lines = stderr.decode('utf8', 'replace').splitlines()
         else:
             stderr_lines = [u'<empty>']
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process.py
index a582ebe..6795b35d 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process.py
@@ -79,15 +79,12 @@
     as necessary to keep issuing commands.
     """
 
-    def __init__(self, port_obj, name, cmd, env=None, universal_newlines=False, treat_no_data_as_crash=False,
+    def __init__(self, port_obj, name, cmd, env=None, treat_no_data_as_crash=False,
                  more_logging=False):
         self._port = port_obj
         self._name = name  # Should be the command name (e.g. content_shell, image_diff)
         self._cmd = cmd
         self._env = env
-        # Set if the process outputs non-standard newlines like '\r\n' or '\r'.
-        # Don't set if there will be binary data or the data must be ASCII encoded.
-        self._universal_newlines = universal_newlines
         self._treat_no_data_as_crash = treat_no_data_as_crash
         self._logging = more_logging
         self._host = self._port.host
@@ -140,8 +137,7 @@
                                                 stdout=self._host.executive.PIPE,
                                                 stderr=self._host.executive.PIPE,
                                                 close_fds=close_fds,
-                                                env=self._env,
-                                                universal_newlines=self._universal_newlines)
+                                                env=self._env)
         self._pid = self._proc.pid
         fd = self._proc.stdout.fileno()
         if not self._use_win32_apis:
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_mock.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_mock.py
index 5a1633a..70ac65c 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_mock.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/server_process_mock.py
@@ -29,8 +29,12 @@
 
 class MockServerProcess(object):
 
-    def __init__(self, port_obj=None, name=None, cmd=None, env=None, universal_newlines=False,
-                 treat_no_data_as_crash=False, more_logging=False, lines=None, crashed=False):
+    def __init__(self, port_obj=None, name=None, cmd=None, env=None,
+                 treat_no_data_as_crash=False, more_logging=False, lines=None,
+                 crashed=False):
+        # port_obj and name are unused, but are maintained for compatibility
+        # with server_process.ServerProcess.
+        # pylint: disable=unused-argument
         self.timed_out = False
         self.lines = lines or ['#READY']
         self.crashed = crashed
diff --git a/third_party/node/LICENSE b/third_party/node/LICENSE
new file mode 100644
index 0000000..3a7260e
--- /dev/null
+++ b/third_party/node/LICENSE
@@ -0,0 +1,1192 @@
+Node.js is licensed for use as follows:
+
+"""
+Copyright Node.js contributors. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+"""
+
+This license applies to parts of Node.js originating from the
+https://github.com/joyent/node repository:
+
+"""
+Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+"""
+
+The Node.js license applies to all parts of Node.js that are not externally
+maintained libraries.
+
+The externally maintained libraries used by Node.js are:
+
+- c-ares, located at deps/cares, is licensed as follows:
+  """
+    Copyright 1998 by the Massachusetts Institute of Technology.
+    Copyright (C) 2007-2013 by Daniel Stenberg
+
+    Permission to use, copy, modify, and distribute this
+    software and its documentation for any purpose and without
+    fee is hereby granted, provided that the above copyright
+    notice appear in all copies and that both that copyright
+    notice and this permission notice appear in supporting
+    documentation, and that the name of M.I.T. not be used in
+    advertising or publicity pertaining to distribution of the
+    software without specific, written prior permission.
+    M.I.T. makes no representations about the suitability of
+    this software for any purpose.  It is provided "as is"
+    without express or implied warranty.
+  """
+
+- HTTP Parser, located at deps/http_parser, is licensed as follows:
+  """
+    http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright
+    Igor Sysoev.
+
+    Additional changes are licensed under the same terms as NGINX and
+    copyright Joyent, Inc. and other Node contributors. All rights reserved.
+
+    Permission is hereby granted, free of charge, to any person obtaining a copy
+    of this software and associated documentation files (the "Software"), to
+    deal in the Software without restriction, including without limitation the
+    rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+    sell copies of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included in
+    all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+    IN THE SOFTWARE.
+  """
+
+- ICU, located at deps/icu-small, is licensed as follows:
+  """
+    COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later)
+
+    Copyright © 1991-2016 Unicode, Inc. All rights reserved.
+    Distributed under the Terms of Use in http://www.unicode.org/copyright.html
+
+    Permission is hereby granted, free of charge, to any person obtaining
+    a copy of the Unicode data files and any associated documentation
+    (the "Data Files") or Unicode software and any associated documentation
+    (the "Software") to deal in the Data Files or Software
+    without restriction, including without limitation the rights to use,
+    copy, modify, merge, publish, distribute, and/or sell copies of
+    the Data Files or Software, and to permit persons to whom the Data Files
+    or Software are furnished to do so, provided that either
+    (a) this copyright and permission notice appear with all copies
+    of the Data Files or Software, or
+    (b) this copyright and permission notice appear in associated
+    Documentation.
+
+    THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+    ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+    WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+    IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
+    NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
+    DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+    DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+    TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+    PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+    Except as contained in this notice, the name of a copyright holder
+    shall not be used in advertising or otherwise to promote the sale,
+    use or other dealings in these Data Files or Software without prior
+    written authorization of the copyright holder.
+
+    ---------------------
+
+    Third-Party Software Licenses
+
+    This section contains third-party software notices and/or additional
+    terms for licensed third-party software components included within ICU
+    libraries.
+
+    1. ICU License - ICU 1.8.1 to ICU 57.1
+
+    COPYRIGHT AND PERMISSION NOTICE
+
+    Copyright (c) 1995-2016 International Business Machines Corporation and others
+    All rights reserved.
+
+    Permission is hereby granted, free of charge, to any person obtaining
+    a copy of this software and associated documentation files (the
+    "Software"), to deal in the Software without restriction, including
+    without limitation the rights to use, copy, modify, merge, publish,
+    distribute, and/or sell copies of the Software, and to permit persons
+    to whom the Software is furnished to do so, provided that the above
+    copyright notice(s) and this permission notice appear in all copies of
+    the Software and that both the above copyright notice(s) and this
+    permission notice appear in supporting documentation.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+    OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+    HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY
+    SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
+    RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+    CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+    CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+    Except as contained in this notice, the name of a copyright holder
+    shall not be used in advertising or otherwise to promote the sale, use
+    or other dealings in this Software without prior written authorization
+    of the copyright holder.
+
+    All trademarks and registered trademarks mentioned herein are the
+    property of their respective owners.
+
+    2. Chinese/Japanese Word Break Dictionary Data (cjdict.txt)
+
+     #     The Google Chrome software developed by Google is licensed under
+     # the BSD license. Other software included in this distribution is
+     # provided under other licenses, as set forth below.
+     #
+     #  The BSD License
+     #  http://opensource.org/licenses/bsd-license.php
+     #  Copyright (C) 2006-2008, Google Inc.
+     #
+     #  All rights reserved.
+     #
+     #  Redistribution and use in source and binary forms, with or without
+     # modification, are permitted provided that the following conditions are met:
+     #
+     #  Redistributions of source code must retain the above copyright notice,
+     # this list of conditions and the following disclaimer.
+     #  Redistributions in binary form must reproduce the above
+     # copyright notice, this list of conditions and the following
+     # disclaimer in the documentation and/or other materials provided with
+     # the distribution.
+     #  Neither the name of  Google Inc. nor the names of its
+     # contributors may be used to endorse or promote products derived from
+     # this software without specific prior written permission.
+     #
+     #
+     #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+     # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+     # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+     # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+     # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+     # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+     # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+     # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+     # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+     # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+     # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+     # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+     #
+     #
+     #  The word list in cjdict.txt are generated by combining three word lists
+     # listed below with further processing for compound word breaking. The
+     # frequency is generated with an iterative training against Google web
+     # corpora.
+     #
+     #  * Libtabe (Chinese)
+     #    - https://sourceforge.net/project/?group_id=1519
+     #    - Its license terms and conditions are shown below.
+     #
+     #  * IPADIC (Japanese)
+     #    - http://chasen.aist-nara.ac.jp/chasen/distribution.html
+     #    - Its license terms and conditions are shown below.
+     #
+     #  ---------COPYING.libtabe ---- BEGIN--------------------
+     #
+     #  /*
+     #   * Copyrighy (c) 1999 TaBE Project.
+     #   * Copyright (c) 1999 Pai-Hsiang Hsiao.
+     #   * All rights reserved.
+     #   *
+     #   * Redistribution and use in source and binary forms, with or without
+     #   * modification, are permitted provided that the following conditions
+     #   * are met:
+     #   *
+     #   * . Redistributions of source code must retain the above copyright
+     #   *   notice, this list of conditions and the following disclaimer.
+     #   * . Redistributions in binary form must reproduce the above copyright
+     #   *   notice, this list of conditions and the following disclaimer in
+     #   *   the documentation and/or other materials provided with the
+     #   *   distribution.
+     #   * . Neither the name of the TaBE Project nor the names of its
+     #   *   contributors may be used to endorse or promote products derived
+     #   *   from this software without specific prior written permission.
+     #   *
+     #   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+     #   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+     #   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+     #   * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+     #   * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+     #   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+     #   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+     #   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+     #   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+     #   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+     #   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+     #   * OF THE POSSIBILITY OF SUCH DAMAGE.
+     #   */
+     #
+     #  /*
+     #   * Copyright (c) 1999 Computer Systems and Communication Lab,
+     #   *                    Institute of Information Science, Academia
+     #       *                    Sinica. All rights reserved.
+     #   *
+     #   * Redistribution and use in source and binary forms, with or without
+     #   * modification, are permitted provided that the following conditions
+     #   * are met:
+     #   *
+     #   * . Redistributions of source code must retain the above copyright
+     #   *   notice, this list of conditions and the following disclaimer.
+     #   * . Redistributions in binary form must reproduce the above copyright
+     #   *   notice, this list of conditions and the following disclaimer in
+     #   *   the documentation and/or other materials provided with the
+     #   *   distribution.
+     #   * . Neither the name of the Computer Systems and Communication Lab
+     #   *   nor the names of its contributors may be used to endorse or
+     #   *   promote products derived from this software without specific
+     #   *   prior written permission.
+     #   *
+     #   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+     #   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+     #   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+     #   * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+     #   * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+     #   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+     #   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+     #   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+     #   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+     #   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+     #   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+     #   * OF THE POSSIBILITY OF SUCH DAMAGE.
+     #   */
+     #
+     #  Copyright 1996 Chih-Hao Tsai @ Beckman Institute,
+     #      University of Illinois
+     #  c-tsai4@uiuc.edu  http://casper.beckman.uiuc.edu/~c-tsai4
+     #
+     #  ---------------COPYING.libtabe-----END--------------------------------
+     #
+     #
+     #  ---------------COPYING.ipadic-----BEGIN-------------------------------
+     #
+     #  Copyright 2000, 2001, 2002, 2003 Nara Institute of Science
+     #  and Technology.  All Rights Reserved.
+     #
+     #  Use, reproduction, and distribution of this software is permitted.
+     #  Any copy of this software, whether in its original form or modified,
+     #  must include both the above copyright notice and the following
+     #  paragraphs.
+     #
+     #  Nara Institute of Science and Technology (NAIST),
+     #  the copyright holders, disclaims all warranties with regard to this
+     #  software, including all implied warranties of merchantability and
+     #  fitness, in no event shall NAIST be liable for
+     #  any special, indirect or consequential damages or any damages
+     #  whatsoever resulting from loss of use, data or profits, whether in an
+     #  action of contract, negligence or other tortuous action, arising out
+     #  of or in connection with the use or performance of this software.
+     #
+     #  A large portion of the dictionary entries
+     #  originate from ICOT Free Software.  The following conditions for ICOT
+     #  Free Software applies to the current dictionary as well.
+     #
+     #  Each User may also freely distribute the Program, whether in its
+     #  original form or modified, to any third party or parties, PROVIDED
+     #  that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear
+     #  on, or be attached to, the Program, which is distributed substantially
+     #  in the same form as set out herein and that such intended
+     #  distribution, if actually made, will neither violate or otherwise
+     #  contravene any of the laws and regulations of the countries having
+     #  jurisdiction over the User or the intended distribution itself.
+     #
+     #  NO WARRANTY
+     #
+     #  The program was produced on an experimental basis in the course of the
+     #  research and development conducted during the project and is provided
+     #  to users as so produced on an experimental basis.  Accordingly, the
+     #  program is provided without any warranty whatsoever, whether express,
+     #  implied, statutory or otherwise.  The term "warranty" used herein
+     #  includes, but is not limited to, any warranty of the quality,
+     #  performance, merchantability and fitness for a particular purpose of
+     #  the program and the nonexistence of any infringement or violation of
+     #  any right of any third party.
+     #
+     #  Each user of the program will agree and understand, and be deemed to
+     #  have agreed and understood, that there is no warranty whatsoever for
+     #  the program and, accordingly, the entire risk arising from or
+     #  otherwise connected with the program is assumed by the user.
+     #
+     #  Therefore, neither ICOT, the copyright holder, or any other
+     #  organization that participated in or was otherwise related to the
+     #  development of the program and their respective officials, directors,
+     #  officers and other employees shall be held liable for any and all
+     #  damages, including, without limitation, general, special, incidental
+     #  and consequential damages, arising out of or otherwise in connection
+     #  with the use or inability to use the program or any product, material
+     #  or result produced or otherwise obtained by using the program,
+     #  regardless of whether they have been advised of, or otherwise had
+     #  knowledge of, the possibility of such damages at any time during the
+     #  project or thereafter.  Each user will be deemed to have agreed to the
+     #  foregoing by his or her commencement of use of the program.  The term
+     #  "use" as used herein includes, but is not limited to, the use,
+     #  modification, copying and distribution of the program and the
+     #  production of secondary products from the program.
+     #
+     #  In the case where the program, whether in its original form or
+     #  modified, was distributed or delivered to or received by a user from
+     #  any person, organization or entity other than ICOT, unless it makes or
+     #  grants independently of ICOT any specific warranty to the user in
+     #  writing, such person, organization or entity, will also be exempted
+     #  from and not be held liable to the user for any such damages as noted
+     #  above as far as the program is concerned.
+     #
+     #  ---------------COPYING.ipadic-----END----------------------------------
+
+    3. Lao Word Break Dictionary Data (laodict.txt)
+
+     #  Copyright (c) 2013 International Business Machines Corporation
+     #  and others. All Rights Reserved.
+     #
+     # Project: http://code.google.com/p/lao-dictionary/
+     # Dictionary: http://lao-dictionary.googlecode.com/git/Lao-Dictionary.txt
+     # License: http://lao-dictionary.googlecode.com/git/Lao-Dictionary-LICENSE.txt
+     #              (copied below)
+     #
+     #  This file is derived from the above dictionary, with slight
+     #  modifications.
+     #  ----------------------------------------------------------------------
+     #  Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell.
+     #  All rights reserved.
+     #
+     #  Redistribution and use in source and binary forms, with or without
+     #  modification,
+     #  are permitted provided that the following conditions are met:
+     #
+     #
+     # Redistributions of source code must retain the above copyright notice, this
+     #  list of conditions and the following disclaimer. Redistributions in
+     #  binary form must reproduce the above copyright notice, this list of
+     #  conditions and the following disclaimer in the documentation and/or
+     #  other materials provided with the distribution.
+     #
+     #
+     # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+     # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+     # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+     # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+     # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+     # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+     # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+     # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+     # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+     # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+     # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+     # OF THE POSSIBILITY OF SUCH DAMAGE.
+     #  --------------------------------------------------------------------------
+
+    4. Burmese Word Break Dictionary Data (burmesedict.txt)
+
+     #  Copyright (c) 2014 International Business Machines Corporation
+     #  and others. All Rights Reserved.
+     #
+     #  This list is part of a project hosted at:
+     #    github.com/kanyawtech/myanmar-karen-word-lists
+     #
+     #  --------------------------------------------------------------------------
+     #  Copyright (c) 2013, LeRoy Benjamin Sharon
+     #  All rights reserved.
+     #
+     #  Redistribution and use in source and binary forms, with or without
+     #  modification, are permitted provided that the following conditions
+     #  are met: Redistributions of source code must retain the above
+     #  copyright notice, this list of conditions and the following
+     #  disclaimer.  Redistributions in binary form must reproduce the
+     #  above copyright notice, this list of conditions and the following
+     #  disclaimer in the documentation and/or other materials provided
+     #  with the distribution.
+     #
+     #    Neither the name Myanmar Karen Word Lists, nor the names of its
+     #    contributors may be used to endorse or promote products derived
+     #    from this software without specific prior written permission.
+     #
+     #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+     #  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+     #  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+     #  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+     #  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+     #  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+     #  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+     #  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+     #  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+     #  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+     #  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+     #  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+     #  SUCH DAMAGE.
+     #  --------------------------------------------------------------------------
+
+    5. Time Zone Database
+
+      ICU uses the public domain data and code derived from Time Zone
+    Database for its time zone support. The ownership of the TZ database
+    is explained in BCP 175: Procedure for Maintaining the Time Zone
+    Database section 7.
+
+     # 7.  Database Ownership
+     #
+     #    The TZ database itself is not an IETF Contribution or an IETF
+     #    document.  Rather it is a pre-existing and regularly updated work
+     #    that is in the public domain, and is intended to remain in the
+     #    public domain.  Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do
+     #    not apply to the TZ Database or contributions that individuals make
+     #    to it.  Should any claims be made and substantiated against the TZ
+     #    Database, the organization that is providing the IANA
+     #    Considerations defined in this RFC, under the memorandum of
+     #    understanding with the IETF, currently ICANN, may act in accordance
+     #    with all competent court orders.  No ownership claims will be made
+     #    by ICANN or the IETF Trust on the database or the code.  Any person
+     #    making a contribution to the database or code waives all rights to
+     #    future claims in that contribution or in the TZ Database.
+  """
+
+- libuv, located at deps/uv, is licensed as follows:
+  """
+    libuv is part of the Node project: http://nodejs.org/
+    libuv may be distributed alone under Node's license:
+
+    ====
+
+    Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+    Permission is hereby granted, free of charge, to any person obtaining a copy
+    of this software and associated documentation files (the "Software"), to
+    deal in the Software without restriction, including without limitation the
+    rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+    sell copies of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included in
+    all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+    IN THE SOFTWARE.
+
+    ====
+
+    This license applies to all parts of libuv that are not externally
+    maintained libraries.
+
+    The externally maintained libraries used by libuv are:
+
+      - tree.h (from FreeBSD), copyright Niels Provos. Two clause BSD license.
+
+      - inet_pton and inet_ntop implementations, contained in src/inet.c, are
+        copyright the Internet Systems Consortium, Inc., and licensed under the ISC
+        license.
+
+      - stdint-msvc2008.h (from msinttypes), copyright Alexander Chemeris. Three
+        clause BSD license.
+
+      - pthread-fixes.h, pthread-fixes.c, copyright Google Inc. and Sony Mobile
+        Communications AB. Three clause BSD license.
+
+      - android-ifaddrs.h, android-ifaddrs.c, copyright Berkeley Software Design
+        Inc, Kenneth MacKay and Emergya (Cloud4all, FP7/2007-2013, grant agreement
+        n° 289016). Three clause BSD license.
+  """
+
+- OpenSSL, located at deps/openssl, is licensed as follows:
+  """
+    Copyright (c) 1998-2016 The OpenSSL Project.  All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+    3. All advertising materials mentioning features or use of this
+    software must display the following acknowledgment:
+    "This product includes software developed by the OpenSSL Project
+    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+
+    4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+    endorse or promote products derived from this software without
+    prior written permission. For written permission, please contact
+    openssl-core@openssl.org.
+
+    5. Products derived from this software may not be called "OpenSSL"
+    nor may "OpenSSL" appear in their names without prior written
+    permission of the OpenSSL Project.
+
+    6. Redistributions of any form whatsoever must retain the following
+    acknowledgment:
+    "This product includes software developed by the OpenSSL Project
+    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+
+    THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+    EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+    PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+    ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+    OF THE POSSIBILITY OF SUCH DAMAGE.
+    ====================================================================
+
+    This product includes cryptographic software written by Eric Young
+    (eay@cryptsoft.com).  This product includes software written by Tim
+    Hudson (tjh@cryptsoft.com).
+  """
+
+- Punycode.js, located at lib/punycode.js, is licensed as follows:
+  """
+    Copyright Mathias Bynens <https://mathiasbynens.be/>
+
+    Permission is hereby granted, free of charge, to any person obtaining
+    a copy of this software and associated documentation files (the
+    "Software"), to deal in the Software without restriction, including
+    without limitation the rights to use, copy, modify, merge, publish,
+    distribute, sublicense, and/or sell copies of the Software, and to
+    permit persons to whom the Software is furnished to do so, subject to
+    the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+  """
+
+- V8, located at deps/v8, is licensed as follows:
+  """
+    This license applies to all parts of V8 that are not externally
+    maintained libraries.  The externally maintained libraries used by V8
+    are:
+
+      - PCRE test suite, located in
+        test/mjsunit/third_party/regexp-pcre/regexp-pcre.js.  This is based on the
+        test suite from PCRE-7.3, which is copyrighted by the University
+        of Cambridge and Google, Inc.  The copyright notice and license
+        are embedded in regexp-pcre.js.
+
+      - Layout tests, located in test/mjsunit/third_party/object-keys.  These are
+        based on layout tests from webkit.org which are copyrighted by
+        Apple Computer, Inc. and released under a 3-clause BSD license.
+
+      - Strongtalk assembler, the basis of the files assembler-arm-inl.h,
+        assembler-arm.cc, assembler-arm.h, assembler-ia32-inl.h,
+        assembler-ia32.cc, assembler-ia32.h, assembler-x64-inl.h,
+        assembler-x64.cc, assembler-x64.h, assembler-mips-inl.h,
+        assembler-mips.cc, assembler-mips.h, assembler.cc and assembler.h.
+        This code is copyrighted by Sun Microsystems Inc. and released
+        under a 3-clause BSD license.
+
+      - Valgrind client API header, located at third_party/valgrind/valgrind.h
+        This is release under the BSD license.
+
+    These libraries have their own licenses; we recommend you read them,
+    as their terms may differ from the terms below.
+
+    Further license information can be found in LICENSE files located in
+    sub-directories.
+
+    Copyright 2014, the V8 project authors. All rights reserved.
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+        * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+        * Redistributions in binary form must reproduce the above
+          copyright notice, this list of conditions and the following
+          disclaimer in the documentation and/or other materials provided
+          with the distribution.
+        * Neither the name of Google Inc. nor the names of its
+          contributors may be used to endorse or promote products derived
+          from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  """
+
+- zlib, located at deps/zlib, is licensed as follows:
+  """
+    zlib.h -- interface of the 'zlib' general purpose compression library
+    version 1.2.8, April 28th, 2013
+
+    Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
+
+    This software is provided 'as-is', without any express or implied
+    warranty.  In no event will the authors be held liable for any damages
+    arising from the use of this software.
+
+    Permission is granted to anyone to use this software for any purpose,
+    including commercial applications, and to alter it and redistribute it
+    freely, subject to the following restrictions:
+
+    1. The origin of this software must not be misrepresented; you must not
+    claim that you wrote the original software. If you use this software
+    in a product, an acknowledgment in the product documentation would be
+    appreciated but is not required.
+    2. Altered source versions must be plainly marked as such, and must not be
+    misrepresented as being the original software.
+    3. This notice may not be removed or altered from any source distribution.
+
+    Jean-loup Gailly        Mark Adler
+    jloup@gzip.org          madler@alumni.caltech.edu
+  """
+
+- npm, located at deps/npm, is licensed as follows:
+  """
+    The npm application
+    Copyright (c) npm, Inc. and Contributors
+    Licensed on the terms of The Artistic License 2.0
+
+    Node package dependencies of the npm application
+    Copyright (c) their respective copyright owners
+    Licensed on their respective license terms
+
+    The npm public registry at https://registry.npmjs.org
+    and the npm website at https://www.npmjs.com
+    Operated by npm, Inc.
+    Use governed by terms published on https://www.npmjs.com
+
+    "Node.js"
+    Trademark Joyent, Inc., https://joyent.com
+    Neither npm nor npm, Inc. are affiliated with Joyent, Inc.
+
+    The Node.js application
+    Project of Node Foundation, https://nodejs.org
+
+    The npm Logo
+    Copyright (c) Mathias Pettersson and Brian Hammond
+
+    "Gubblebum Blocky" typeface
+    Copyright (c) Tjarda Koster, https://jelloween.deviantart.com
+    Used with permission
+
+    --------
+
+    The Artistic License 2.0
+
+    Copyright (c) 2000-2006, The Perl Foundation.
+
+    Everyone is permitted to copy and distribute verbatim copies
+    of this license document, but changing it is not allowed.
+
+    Preamble
+
+    This license establishes the terms under which a given free software
+    Package may be copied, modified, distributed, and/or redistributed.
+    The intent is that the Copyright Holder maintains some artistic
+    control over the development of that Package while still keeping the
+    Package available as open source and free software.
+
+    You are always permitted to make arrangements wholly outside of this
+    license directly with the Copyright Holder of a given Package.  If the
+    terms of this license do not permit the full use that you propose to
+    make of the Package, you should contact the Copyright Holder and seek
+    a different licensing arrangement.
+
+    Definitions
+
+        "Copyright Holder" means the individual(s) or organization(s)
+        named in the copyright notice for the entire Package.
+
+        "Contributor" means any party that has contributed code or other
+        material to the Package, in accordance with the Copyright Holder's
+        procedures.
+
+        "You" and "your" means any person who would like to copy,
+        distribute, or modify the Package.
+
+        "Package" means the collection of files distributed by the
+        Copyright Holder, and derivatives of that collection and/or of
+        those files. A given Package may consist of either the Standard
+        Version, or a Modified Version.
+
+        "Distribute" means providing a copy of the Package or making it
+        accessible to anyone else, or in the case of a company or
+        organization, to others outside of your company or organization.
+
+        "Distributor Fee" means any fee that you charge for Distributing
+        this Package or providing support for this Package to another
+        party.  It does not mean licensing fees.
+
+        "Standard Version" refers to the Package if it has not been
+        modified, or has been modified only in ways explicitly requested
+        by the Copyright Holder.
+
+        "Modified Version" means the Package, if it has been changed, and
+        such changes were not explicitly requested by the Copyright
+        Holder.
+
+        "Original License" means this Artistic License as Distributed with
+        the Standard Version of the Package, in its current version or as
+        it may be modified by The Perl Foundation in the future.
+
+        "Source" form means the source code, documentation source, and
+        configuration files for the Package.
+
+        "Compiled" form means the compiled bytecode, object code, binary,
+        or any other form resulting from mechanical transformation or
+        translation of the Source form.
+
+    Permission for Use and Modification Without Distribution
+
+    (1)  You are permitted to use the Standard Version and create and use
+    Modified Versions for any purpose without restriction, provided that
+    you do not Distribute the Modified Version.
+
+    Permissions for Redistribution of the Standard Version
+
+    (2)  You may Distribute verbatim copies of the Source form of the
+    Standard Version of this Package in any medium without restriction,
+    either gratis or for a Distributor Fee, provided that you duplicate
+    all of the original copyright notices and associated disclaimers.  At
+    your discretion, such verbatim copies may or may not include a
+    Compiled form of the Package.
+
+    (3)  You may apply any bug fixes, portability changes, and other
+    modifications made available from the Copyright Holder.  The resulting
+    Package will still be considered the Standard Version, and as such
+    will be subject to the Original License.
+
+    Distribution of Modified Versions of the Package as Source
+
+    (4)  You may Distribute your Modified Version as Source (either gratis
+    or for a Distributor Fee, and with or without a Compiled form of the
+    Modified Version) provided that you clearly document how it differs
+    from the Standard Version, including, but not limited to, documenting
+    any non-standard features, executables, or modules, and provided that
+    you do at least ONE of the following:
+
+        (a)  make the Modified Version available to the Copyright Holder
+        of the Standard Version, under the Original License, so that the
+        Copyright Holder may include your modifications in the Standard
+        Version.
+
+        (b)  ensure that installation of your Modified Version does not
+        prevent the user installing or running the Standard Version. In
+        addition, the Modified Version must bear a name that is different
+        from the name of the Standard Version.
+
+        (c)  allow anyone who receives a copy of the Modified Version to
+        make the Source form of the Modified Version available to others
+        under
+
+            (i)  the Original License or
+
+            (ii)  a license that permits the licensee to freely copy,
+            modify and redistribute the Modified Version using the same
+            licensing terms that apply to the copy that the licensee
+            received, and requires that the Source form of the Modified
+            Version, and of any works derived from it, be made freely
+            available in that license fees are prohibited but Distributor
+            Fees are allowed.
+
+    Distribution of Compiled Forms of the Standard Version
+    or Modified Versions without the Source
+
+    (5)  You may Distribute Compiled forms of the Standard Version without
+    the Source, provided that you include complete instructions on how to
+    get the Source of the Standard Version.  Such instructions must be
+    valid at the time of your distribution.  If these instructions, at any
+    time while you are carrying out such distribution, become invalid, you
+    must provide new instructions on demand or cease further distribution.
+    If you provide valid instructions or cease distribution within thirty
+    days after you become aware that the instructions are invalid, then
+    you do not forfeit any of your rights under this license.
+
+    (6)  You may Distribute a Modified Version in Compiled form without
+    the Source, provided that you comply with Section 4 with respect to
+    the Source of the Modified Version.
+
+    Aggregating or Linking the Package
+
+    (7)  You may aggregate the Package (either the Standard Version or
+    Modified Version) with other packages and Distribute the resulting
+    aggregation provided that you do not charge a licensing fee for the
+    Package.  Distributor Fees are permitted, and licensing fees for other
+    components in the aggregation are permitted. The terms of this license
+    apply to the use and Distribution of the Standard or Modified Versions
+    as included in the aggregation.
+
+    (8) You are permitted to link Modified and Standard Versions with
+    other works, to embed the Package in a larger work of your own, or to
+    build stand-alone binary or bytecode versions of applications that
+    include the Package, and Distribute the result without restriction,
+    provided the result does not expose a direct interface to the Package.
+
+    Items That are Not Considered Part of a Modified Version
+
+    (9) Works (including, but not limited to, modules and scripts) that
+    merely extend or make use of the Package, do not, by themselves, cause
+    the Package to be a Modified Version.  In addition, such works are not
+    considered parts of the Package itself, and are not subject to the
+    terms of this license.
+
+    General Provisions
+
+    (10)  Any use, modification, and distribution of the Standard or
+    Modified Versions is governed by this Artistic License. By using,
+    modifying or distributing the Package, you accept this license. Do not
+    use, modify, or distribute the Package, if you do not accept this
+    license.
+
+    (11)  If your Modified Version has been derived from a Modified
+    Version made by someone other than you, you are nevertheless required
+    to ensure that your Modified Version complies with the requirements of
+    this license.
+
+    (12)  This license does not grant you the right to use any trademark,
+    service mark, tradename, or logo of the Copyright Holder.
+
+    (13)  This license includes the non-exclusive, worldwide,
+    free-of-charge patent license to make, have made, use, offer to sell,
+    sell, import and otherwise transfer the Package with respect to any
+    patent claims licensable by the Copyright Holder that are necessarily
+    infringed by the Package. If you institute patent litigation
+    (including a cross-claim or counterclaim) against any party alleging
+    that the Package constitutes direct or contributory patent
+    infringement, then this Artistic License to you shall terminate on the
+    date that such litigation is filed.
+
+    (14)  Disclaimer of Warranty:
+    THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+    IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+    NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
+    LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
+    BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+    DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
+    ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+    --------
+  """
+
+- GYP, located at tools/gyp, is licensed as follows:
+  """
+    Copyright (c) 2009 Google Inc. All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+       * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+    copyright notice, this list of conditions and the following disclaimer
+    in the documentation and/or other materials provided with the
+    distribution.
+       * Neither the name of Google Inc. nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  """
+
+- marked, located at tools/doc/node_modules/marked, is licensed as follows:
+  """
+    Copyright (c) 2011-2014, Christopher Jeffrey (https://github.com/chjj/)
+
+    Permission is hereby granted, free of charge, to any person obtaining a copy
+    of this software and associated documentation files (the "Software"), to deal
+    in the Software without restriction, including without limitation the rights
+    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included in
+    all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+    THE SOFTWARE.
+  """
+
+- cpplint.py, located at tools/cpplint.py, is licensed as follows:
+  """
+    Copyright (c) 2009 Google Inc. All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+       * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+    copyright notice, this list of conditions and the following disclaimer
+    in the documentation and/or other materials provided with the
+    distribution.
+       * Neither the name of Google Inc. nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  """
+
+- ESLint, located at tools/eslint, is licensed as follows:
+  """
+    ESLint
+    Copyright jQuery Foundation and other contributors, https://jquery.org/
+
+    Permission is hereby granted, free of charge, to any person obtaining a copy
+    of this software and associated documentation files (the "Software"), to deal
+    in the Software without restriction, including without limitation the rights
+    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included in
+    all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+    THE SOFTWARE.
+  """
+
+- gtest, located at deps/gtest, is licensed as follows:
+  """
+    Copyright 2008, Google Inc.
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+        * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+        * Redistributions in binary form must reproduce the above
+    copyright notice, this list of conditions and the following disclaimer
+    in the documentation and/or other materials provided with the
+    distribution.
+        * Neither the name of Google Inc. nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  """
+
+- node-weak, located at test/gc/node_modules/weak, is licensed as follows:
+  """
+    Copyright (c) 2011, Ben Noordhuis <info@bnoordhuis.nl>
+
+    Permission to use, copy, modify, and/or distribute this software for any
+    purpose with or without fee is hereby granted, provided that the above
+    copyright notice and this permission notice appear in all copies.
+
+    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+  """
+
+- v8_inspector, located at deps/v8_inspector/third_party/v8_inspector, is licensed as follows:
+  """
+    // Copyright 2015 The Chromium Authors. All rights reserved.
+    //
+    // Redistribution and use in source and binary forms, with or without
+    // modification, are permitted provided that the following conditions are
+    // met:
+    //
+    //    * Redistributions of source code must retain the above copyright
+    // notice, this list of conditions and the following disclaimer.
+    //    * Redistributions in binary form must reproduce the above
+    // copyright notice, this list of conditions and the following disclaimer
+    // in the documentation and/or other materials provided with the
+    // distribution.
+    //    * Neither the name of Google Inc. nor the names of its
+    // contributors may be used to endorse or promote products derived from
+    // this software without specific prior written permission.
+    //
+    // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  """
+
+- jinja2, located at deps/v8_inspector/third_party/jinja2, is licensed as follows:
+  """
+    Copyright (c) 2009 by the Jinja Team, see AUTHORS for more details.
+    
+    Some rights reserved.
+    
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+    
+        * Redistributions of source code must retain the above copyright
+          notice, this list of conditions and the following disclaimer.
+    
+        * Redistributions in binary form must reproduce the above
+          copyright notice, this list of conditions and the following
+          disclaimer in the documentation and/or other materials provided
+          with the distribution.
+    
+        * The names of the contributors may not be used to endorse or
+          promote products derived from this software without specific
+          prior written permission.
+    
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  """
+
+- markupsafe, located at deps/v8_inspector/third_party/markupsafe, is licensed as follows:
+  """
+    Copyright (c) 2010 by Armin Ronacher and contributors.  See AUTHORS
+    for more details.
+
+    Some rights reserved.
+
+    Redistribution and use in source and binary forms of the software as well
+    as documentation, with or without modification, are permitted provided
+    that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * The names of the contributors may not be used to endorse or
+      promote products derived from this software without specific
+      prior written permission.
+
+    THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+    CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
+    NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+    OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+    DAMAGE.
+  """
diff --git a/third_party/node/OWNERS b/third_party/node/OWNERS
new file mode 100644
index 0000000..ca467ff7f
--- /dev/null
+++ b/third_party/node/OWNERS
@@ -0,0 +1,2 @@
+dbeam@chromium.org
+dpapad@chromium.org
diff --git a/third_party/node/README.chromium b/third_party/node/README.chromium
new file mode 100644
index 0000000..7f68e9e
--- /dev/null
+++ b/third_party/node/README.chromium
@@ -0,0 +1,17 @@
+Name: Node JS
+Short Name: node
+URL: https://github.com/nodejs/node
+Version: 6.9.4
+Revision: f098f8295c407e5a5126b501ed5f1aa80bd86106
+Date: Tue Dec 06 2016 18:07:35 GMT-0800 (PST)
+License: NodeJS
+License File: NOT_SHIPPED
+Security Critical: no
+
+Description:
+Node binaries and NPM modules necessary for buliding Chrome's WebUI.
+Use update_node_binaries to update Node binaries and update_npm_deps to
+update NPM dependencies.
+
+Local Modifications:
+No modifications.
diff --git a/third_party/node/linux/node-linux-x64.tar.gz.sha1 b/third_party/node/linux/node-linux-x64.tar.gz.sha1
new file mode 100644
index 0000000..68c1c85
--- /dev/null
+++ b/third_party/node/linux/node-linux-x64.tar.gz.sha1
@@ -0,0 +1 @@
+eb63c12c3b19b96b9142e572c3c2cfaa27e13191
diff --git a/third_party/node/mac/node-darwin-x64.tar.gz.sha1 b/third_party/node/mac/node-darwin-x64.tar.gz.sha1
new file mode 100644
index 0000000..e98a8045
--- /dev/null
+++ b/third_party/node/mac/node-darwin-x64.tar.gz.sha1
@@ -0,0 +1 @@
+b5ac19fd2f8afaa20f698972d2b0bb1056dc258a
diff --git a/third_party/node/node_modules.tar.gz.sha1 b/third_party/node/node_modules.tar.gz.sha1
new file mode 100644
index 0000000..d1b5761b
--- /dev/null
+++ b/third_party/node/node_modules.tar.gz.sha1
@@ -0,0 +1 @@
+ce62710ad9f651016248574ecd7b07e86dd01f2b
diff --git a/third_party/node/npm_exclude.txt b/third_party/node/npm_exclude.txt
new file mode 100644
index 0000000..3a8e6fc
--- /dev/null
+++ b/third_party/node/npm_exclude.txt
@@ -0,0 +1,15 @@
+*/.*
+*/example/
+*/examples/
+*.html
+hydrolysis/hydrolysis.js
+*/jsdoc2md/
+*.md
+*.png
+*.sh
+*.svg
+*/test/
+*.ts
+*/@types/
+*.woff
+*.yml
diff --git a/third_party/node/package.json b/third_party/node/package.json
new file mode 100644
index 0000000..3558ae7
--- /dev/null
+++ b/third_party/node/package.json
@@ -0,0 +1,11 @@
+{
+  "name": "webui-node-modules",
+  "version": "1.0.0",
+  "author": "dpapad@chromium.org",
+  "dependencies": {
+    "crisper": "2.0.2",
+    "polymer-css-build": "0.0.7",
+    "uglifyjs": "2.4.10",
+    "vulcanize": "1.15.2"
+  }
+}
diff --git a/third_party/node/update_node_binaries b/third_party/node/update_node_binaries
new file mode 100755
index 0000000..d6d6512
--- /dev/null
+++ b/third_party/node/update_node_binaries
@@ -0,0 +1,93 @@
+#!/bin/bash
+
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Script for updating Node binaries.
+# 1) Update NODE_VERSION variable below to the desired version.
+# 2) Run this script.
+# 3) Upload the binaries to the Google Storage bucket (commands to upload
+#    binaries are printed at step 2, look for "gsutil.py").
+# 4) Land a CL with the changes generated by this script.
+
+set -eu
+cd "$(dirname "$0")"
+
+BASE_URL="https://nodejs.org/dist"
+NODE_VERSION="v6.9.4"
+
+update_unix() {
+  local SUFFIX="$1"
+  local FOLDER="$2"
+  local FILENAME="node-${NODE_VERSION}-${SUFFIX}.tar.gz"
+  local URL="${BASE_URL}/${NODE_VERSION}/${FILENAME}"
+
+  rm -f "${FOLDER}/${FILENAME}"
+  wget -P "${FOLDER}/" "${URL}"
+
+  # Check SHASUMS256 of downloaded binary.
+  local sha256_expected
+  sha256_expected="$(grep "$FILENAME" SHASUMS256.txt | cut -d ' ' -f1)"
+  local sha256_actual
+  sha256_actual="$(sha256sum "${FOLDER}/${FILENAME}" | cut -d ' ' -f1)"
+
+  if [ "${sha256_expected}" != "${sha256_actual}" ]; then
+    echo "SHA256 mismatch. Exiting..."
+    exit 1
+  fi
+
+  # Unpack temporarily, delete NPM symlink and re-pack.
+  tar xfz "${FOLDER}/${FILENAME}" -C "${FOLDER}/"
+  rm "${FOLDER}/${FILENAME}"
+  rm "${FOLDER}/node-${NODE_VERSION}-${SUFFIX}/bin/npm"
+
+  # Drop the version info from the name, since it is redundant and would make
+  # rolling new versions more involved.
+  rm -rf "${FOLDER}/node-${SUFFIX}/"
+  mv "${FOLDER}/node-${NODE_VERSION}-${SUFFIX}/" "${FOLDER}/node-${SUFFIX}/"
+  tar cfz "${FOLDER}/node-${SUFFIX}.tar.gz" -C "${FOLDER}" "node-${SUFFIX}/"
+  local sha1
+  sha1="$(sha1sum ${FOLDER}/node-${SUFFIX}.tar.gz | cut -d ' ' -f1)"
+  echo "${sha1}" > "${FOLDER}/node-${SUFFIX}.tar.gz.sha1"
+  echo "Please execute manually the following:"
+  echo "> gsutil.py cp ${FOLDER}/node-${SUFFIX}.tar.gz gs://chromium-nodejs/${NODE_VERSION:1}/${sha1}"
+  echo "DONE updating for ${SUFFIX}."
+}
+
+update_win() {
+  local FILENAME="node.exe"
+  local FOLDER="win"
+  local WINDOWS_URL="${BASE_URL}/${NODE_VERSION}/win-x64/${FILENAME}"
+  rm -f "${FOLDER}/${FILENAME}"
+  wget -P "${FOLDER}/" "${WINDOWS_URL}"
+
+  # Check SHASUMS256 of downloaded binary.
+  local sha256_expected
+  sha256_expected="$(grep "win-x64/$FILENAME" SHASUMS256.txt | cut -d ' ' -f1)"
+  local sha256_actual
+  sha256_actual="$(sha256sum "${FOLDER}/${FILENAME}" | cut -d ' ' -f1)"
+
+  if [ "${sha256_expected}" != "${sha256_actual}" ]; then
+    echo "SHA256 mismatch. Exiting..."
+    exit 1
+  fi
+
+  local sha1
+  sha1="$(sha1sum ${FOLDER}/node.exe | cut -d ' ' -f1)"
+  echo "${sha1}" > "${FOLDER}/node.exe.sha1"
+  echo "Please execute manually the following:"
+  echo "> gsutil.py cp ${FOLDER}/node.exe gs://chromium-nodejs/${NODE_VERSION:1}/${sha1}"
+  echo "DONE updating Windows."
+}
+
+# First download checksum file.
+rm "SHASUMS256.txt"
+wget "https://nodejs.org/dist/latest-v6.x/SHASUMS256.txt"
+
+update_unix "darwin-x64" "mac"
+update_unix "linux-x64" "linux"
+update_win
+
+# Update DEPS to point to the new Google Storage bucket subfolder.
+sed -i "s@\(chromium-nodejs/\)\([0-9\.]\)\+@\1${NODE_VERSION:1}@" ../../DEPS
diff --git a/third_party/node/update_npm_deps b/third_party/node/update_npm_deps
new file mode 100755
index 0000000..187d05d
--- /dev/null
+++ b/third_party/node/update_npm_deps
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Script for updating WebUI's NPM deps.
+#  1) Update package.json file to point to the desired version.
+#  2) Run this script.
+#  3) Upload the compressed node_modules.tar.gz file to the Google Storage
+#     bucket (commands to upload binaries are printed at step 2).
+#  4) Land a CL with the changes generated by this script.
+
+set -eu
+cd "$(dirname "$0")"
+
+rm -rf node_modules
+
+npm install --no-bin-links --only=prod
+rsync -c --delete -r -q --exclude-from="npm_exclude.txt" \
+      --prune-empty-dirs "node_modules/" "node_modules_filtered/"
+
+echo -e "\n---------------------------------------------------------"
+echo "Before filtering:" size: $(du -h node_modules/ | tail -n1 | cut -f1) ", files: " $(find node_modules/ -type f | wc -l)
+rm -r node_modules
+mv node_modules_filtered node_modules
+
+echo "After filtering:" size: $(du -h node_modules/ | tail -n1 | cut -f1) ", files: " $(find node_modules/ -type f | wc -l)
+
+tar cfz node_modules.tar.gz node_modules
+echo "After compressing:" size: $(du -h node_modules.tar.gz | tail -n1 | cut -f1)
+
+sha1="$(sha1sum node_modules.tar.gz | cut -d ' ' -f1)"
+echo "${sha1}" > node_modules.tar.gz.sha1
+echo "Please run the following manually to update Google Storage bucket:"
+echo "> gsutil.py cp node_modules.tar.gz gs://chromium-nodejs/${sha1}"
+echo "DONE"
+echo -e "---------------------------------------------------------"
diff --git a/third_party/node/win/node.exe.sha1 b/third_party/node/win/node.exe.sha1
new file mode 100644
index 0000000..a30212e7
--- /dev/null
+++ b/third_party/node/win/node.exe.sha1
@@ -0,0 +1 @@
+a5217c3b78a04dd8da80d4ee145577ea536a6cfc
diff --git a/ui/ozone/platform/drm/common/drm_util.cc b/ui/ozone/platform/drm/common/drm_util.cc
index 0baf95e..fff21503 100644
--- a/ui/ozone/platform/drm/common/drm_util.cc
+++ b/ui/ozone/platform/drm/common/drm_util.cc
@@ -52,7 +52,7 @@
 
   DCHECK_GE(32, resources->count_crtcs);
   uint32_t best_crtc = 0;
-  int best_crtc_planes = 0;
+  int best_crtc_planes = -1;
 
   // Try to find an encoder for the connector.
   for (int i = 0; i < connector->count_encoders; ++i) {
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd
index 643b007..3160c1c 100644
--- a/ui/strings/ui_strings.grd
+++ b/ui/strings/ui_strings.grd
@@ -667,6 +667,9 @@
       <message name="IDS_DISPLAY_TOUCH_CALIBRATION_HINT_SUBLABEL_TEXT" desc="Message to inform the user what the next step is in touch calibration is">
         Tap the touch targets on your screen.
       </message>
+      <message name="IDS_DISPLAY_TOUCH_CALIBRATION_TAP_HERE_LABEL" desc="A message to notify the user about the significance of a circle on the screen during the touchscreen calibration.">
+        Tap here
+      </message>
 
       <!-- Display names -->
       <message name="IDS_DISPLAY_NAME_UNKNOWN" desc="The name used for a display whose name is unknown, which is shown in the display settings and ash tray.">