diff --git a/DEPS b/DEPS
index 3fb6298..da2a9e0 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': 'a86c017d368ab375f49ed8e16bc8d0897cf034a1',
+  'v8_revision': '7e6e8430ad10f7ec20095b857dd14eb9525c5872',
   # 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.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '597f96ee44a5845b23057d1576b3694480cc6fa8',
+  'catapult_revision': 'c9212cbf522072a1011063b212da5ee7baeb56df',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index cbb91a3..6781d1ac 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -700,6 +700,9 @@
     "//components/printing/common",
     "//components/printing/renderer",
     "//components/safe_browsing",
+    "//components/safe_browsing/browser",
+    "//components/safe_browsing/common",
+    "//components/safe_browsing/triggers",
     "//components/safe_browsing_db:safe_browsing_db_mobile",
     "//components/spellcheck:build_features",
     "//components/supervised_user_error_page",
diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc
index 0835b14..22d70ed 100644
--- a/android_webview/browser/aw_browser_context.cc
+++ b/android_webview/browser/aw_browser_context.cc
@@ -29,6 +29,7 @@
 #include "components/prefs/in_memory_pref_store.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/pref_service_factory.h"
+#include "components/safe_browsing/triggers/trigger_manager.h"
 #include "components/url_formatter/url_fixer.h"
 #include "components/user_prefs/user_prefs.h"
 #include "components/visitedlink/browser/visitedlink_master.h"
@@ -206,9 +207,13 @@
   web_restriction_provider_->SetAuthority(
       user_pref_service_->GetString(prefs::kWebRestrictionsAuthority));
 
-  safe_browsing_ui_manager_ = new AwSafeBrowsingUIManager();
+  safe_browsing_ui_manager_ =
+      new AwSafeBrowsingUIManager(GetAwURLRequestContext());
   safe_browsing_db_manager_ =
       new safe_browsing::RemoteSafeBrowsingDatabaseManager();
+  safe_browsing_trigger_manager_ =
+      base::MakeUnique<safe_browsing::TriggerManager>(
+          safe_browsing_ui_manager_.get());
 }
 
 void AwBrowserContext::OnWebRestrictionsAuthorityChanged() {
@@ -395,6 +400,12 @@
   return safe_browsing_db_manager_.get();
 }
 
+safe_browsing::TriggerManager* AwBrowserContext::GetSafeBrowsingTriggerManager()
+    const {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  return safe_browsing_trigger_manager_.get();
+}
+
 void AwBrowserContext::RebuildTable(
     const scoped_refptr<URLEnumerator>& enumerator) {
   // Android WebView rebuilds from WebChromeClient.getVisitedHistory. The client
diff --git a/android_webview/browser/aw_browser_context.h b/android_webview/browser/aw_browser_context.h
index 7e907d0..b2c89e47 100644
--- a/android_webview/browser/aw_browser_context.h
+++ b/android_webview/browser/aw_browser_context.h
@@ -42,6 +42,10 @@
 class VisitedLinkMaster;
 }
 
+namespace safe_browsing {
+class TriggerManager;
+}  // namespace safe_browsing
+
 namespace android_webview {
 
 class AwFormDatabaseService;
@@ -117,6 +121,7 @@
 
   AwSafeBrowsingUIManager* GetSafeBrowsingUIManager();
   safe_browsing::RemoteSafeBrowsingDatabaseManager* GetSafeBrowsingDBManager();
+  safe_browsing::TriggerManager* GetSafeBrowsingTriggerManager() const;
 
  private:
   void InitUserPrefService();
@@ -150,6 +155,7 @@
   PrefChangeRegistrar pref_change_registrar_;
 
   scoped_refptr<AwSafeBrowsingUIManager> safe_browsing_ui_manager_;
+  std::unique_ptr<safe_browsing::TriggerManager> safe_browsing_trigger_manager_;
   scoped_refptr<safe_browsing::RemoteSafeBrowsingDatabaseManager>
       safe_browsing_db_manager_;
   bool safe_browsing_db_manager_started_ = false;
diff --git a/android_webview/browser/aw_browser_main_parts.cc b/android_webview/browser/aw_browser_main_parts.cc
index 58e7802..78314275 100644
--- a/android_webview/browser/aw_browser_main_parts.cc
+++ b/android_webview/browser/aw_browser_main_parts.cc
@@ -8,6 +8,7 @@
 #include "android_webview/browser/aw_browser_terminator.h"
 #include "android_webview/browser/aw_content_browser_client.h"
 #include "android_webview/browser/aw_result_codes.h"
+#include "android_webview/browser/aw_safe_browsing_config_helper.h"
 #include "android_webview/browser/deferred_gpu_command_service.h"
 #include "android_webview/browser/net/aw_network_change_notifier_factory.h"
 #include "android_webview/common/aw_descriptors.h"
@@ -133,6 +134,15 @@
     }
   }
 
+  if (AwSafeBrowsingConfigHelper::GetSafeBrowsingEnabled()) {
+    base::FilePath safe_browsing_dir;
+    if (PathService::Get(android_webview::DIR_SAFE_BROWSING,
+                         &safe_browsing_dir)) {
+      if (!base::PathExists(safe_browsing_dir))
+        base::CreateDirectory(safe_browsing_dir);
+    }
+  }
+
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kWebViewSandboxedRenderer)) {
     // Create the renderers crash manager on the UI thread.
diff --git a/android_webview/browser/aw_safe_browsing_blocking_page.cc b/android_webview/browser/aw_safe_browsing_blocking_page.cc
index 7f505a3a..ea80eb1 100644
--- a/android_webview/browser/aw_safe_browsing_blocking_page.cc
+++ b/android_webview/browser/aw_safe_browsing_blocking_page.cc
@@ -4,7 +4,9 @@
 
 #include "android_webview/browser/aw_safe_browsing_blocking_page.h"
 
+#include "android_webview/browser/aw_browser_context.h"
 #include "android_webview/browser/aw_safe_browsing_ui_manager.h"
+#include "android_webview/browser/net/aw_url_request_context_getter.h"
 #include "components/security_interstitials/content/security_interstitial_controller_client.h"
 #include "components/security_interstitials/content/unsafe_resource.h"
 #include "components/security_interstitials/core/base_safe_browsing_error_ui.h"
@@ -43,6 +45,10 @@
         ui_manager->app_locale(), base::Time::NowFromSystemTime(), controller(),
         errorUiType == ErrorUiType::QUIET_GIANT));
   }
+
+  // TODO(timvolodine): invoke TriggerManager::StartCollectingThreatDetails(),
+  // (via AwBrowserContext, e.g.
+  // AwBrowserContext::FromWebContents(web_contents)), crbug.com/731747.
 }
 
 // static
diff --git a/android_webview/browser/aw_safe_browsing_ui_manager.cc b/android_webview/browser/aw_safe_browsing_ui_manager.cc
index cf33463..0dc54b9 100644
--- a/android_webview/browser/aw_safe_browsing_ui_manager.cc
+++ b/android_webview/browser/aw_safe_browsing_ui_manager.cc
@@ -5,15 +5,44 @@
 #include "android_webview/browser/aw_safe_browsing_ui_manager.h"
 
 #include "android_webview/browser/aw_safe_browsing_blocking_page.h"
+#include "android_webview/browser/net/aw_url_request_context_getter.h"
+#include "android_webview/common/aw_paths.h"
+#include "base/command_line.h"
+#include "base/path_service.h"
+#include "components/safe_browsing/base_ping_manager.h"
+#include "components/safe_browsing/base_ui_manager.h"
+#include "components/safe_browsing/browser/safe_browsing_url_request_context_getter.h"
+#include "components/safe_browsing/common/safebrowsing_constants.h"
+#include "components/safe_browsing/common/safebrowsing_switches.h"
 #include "content/public/browser/browser_thread.h"
 
 using content::BrowserThread;
 using content::WebContents;
 
+namespace {
+
+std::string GetProtocolConfigClientName() {
+  // Return a webview specific client name, see crbug.com/732373 for details.
+  return "android_webview";
+}
+
+}  // namespace
+
 namespace android_webview {
 
-AwSafeBrowsingUIManager::AwSafeBrowsingUIManager() {
+AwSafeBrowsingUIManager::AwSafeBrowsingUIManager(
+    AwURLRequestContextGetter* browser_url_request_context_getter) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  // TODO(timvolodine): verify this is what we want regarding the directory.
+  base::FilePath user_data_dir;
+  bool result =
+      PathService::Get(android_webview::DIR_SAFE_BROWSING, &user_data_dir);
+  DCHECK(result);
+
+  url_request_context_getter_ =
+      new safe_browsing::SafeBrowsingURLRequestContextGetter(
+          browser_url_request_context_getter, user_data_dir);
 }
 
 AwSafeBrowsingUIManager::~AwSafeBrowsingUIManager() {}
@@ -47,4 +76,32 @@
   return client->GetErrorUiType();
 }
 
+void AwSafeBrowsingUIManager::SendSerializedThreatDetails(
+    const std::string& serialized) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (!ping_manager_) {
+    // Lazy creation of ping manager, needs to happen on IO thread.
+    safe_browsing::SafeBrowsingProtocolConfig config;
+    config.client_name = GetProtocolConfigClientName();
+    base::CommandLine* cmdline = ::base::CommandLine::ForCurrentProcess();
+    config.disable_auto_update =
+        cmdline->HasSwitch(::safe_browsing::switches::kSbDisableAutoUpdate);
+    config.url_prefix = ::safe_browsing::kSbDefaultURLPrefix;
+    config.backup_connect_error_url_prefix =
+        ::safe_browsing::kSbBackupConnectErrorURLPrefix;
+    config.backup_http_error_url_prefix =
+        ::safe_browsing::kSbBackupHttpErrorURLPrefix;
+    config.backup_network_error_url_prefix =
+        ::safe_browsing::kSbBackupNetworkErrorURLPrefix;
+    ping_manager_ = ::safe_browsing::BasePingManager::Create(
+        url_request_context_getter_.get(), config);
+  }
+
+  if (!serialized.empty()) {
+    DVLOG(1) << "Sending serialized threat details";
+    ping_manager_->ReportThreatDetails(serialized);
+  }
+}
+
 }  // namespace android_webview
diff --git a/android_webview/browser/aw_safe_browsing_ui_manager.h b/android_webview/browser/aw_safe_browsing_ui_manager.h
index 79873c6..263f89a5 100644
--- a/android_webview/browser/aw_safe_browsing_ui_manager.h
+++ b/android_webview/browser/aw_safe_browsing_ui_manager.h
@@ -12,7 +12,13 @@
 #include "components/safe_browsing/base_ui_manager.h"
 #include "content/public/browser/web_contents.h"
 
+namespace safe_browsing {
+class BasePingManager;
+class SafeBrowsingURLRequestContextGetter;
+}  // namespace
+
 namespace android_webview {
+class AwURLRequestContextGetter;
 
 class AwSafeBrowsingUIManager : public safe_browsing::BaseUIManager {
  public:
@@ -28,19 +34,33 @@
   };
 
   // Construction needs to happen on the UI thread.
-  AwSafeBrowsingUIManager();
-
-  void DisplayBlockingPage(const UnsafeResource& resource) override;
+  explicit AwSafeBrowsingUIManager(
+      AwURLRequestContextGetter* browser_url_request_context_getter);
 
   // Gets the correct ErrorUiType for the web contents
   int GetErrorUiType(const UnsafeResource& resource) const;
 
+  // BaseUIManager methods:
+  void DisplayBlockingPage(const UnsafeResource& resource) override;
+
+  // Called on the IO thread by the ThreatDetails with the serialized
+  // protocol buffer, so the service can send it over.
+  void SendSerializedThreatDetails(const std::string& serialized) override;
+
  protected:
   ~AwSafeBrowsingUIManager() override;
 
   void ShowBlockingPageForResource(const UnsafeResource& resource) override;
 
  private:
+  // Provides phishing and malware statistics. Accessed on IO thread.
+  std::unique_ptr<safe_browsing::BasePingManager> ping_manager_;
+
+  // The SafeBrowsingURLRequestContextGetter used to access
+  // |url_request_context_|. Accessed on UI thread.
+  scoped_refptr<safe_browsing::SafeBrowsingURLRequestContextGetter>
+      url_request_context_getter_;
+
   DISALLOW_COPY_AND_ASSIGN(AwSafeBrowsingUIManager);
 };
 
diff --git a/android_webview/common/aw_paths.cc b/android_webview/common/aw_paths.cc
index 2e57b00..390a7be 100644
--- a/android_webview/common/aw_paths.cc
+++ b/android_webview/common/aw_paths.cc
@@ -21,6 +21,12 @@
       cur = cur.Append(FILE_PATH_LITERAL("WebView"))
                 .Append(FILE_PATH_LITERAL("Crash Reports"));
       break;
+    case DIR_SAFE_BROWSING:
+      if (!base::android::GetCacheDirectory(&cur))
+        return false;
+      cur = cur.Append(FILE_PATH_LITERAL("WebView"))
+                .Append(FILE_PATH_LITERAL("SafeBrowsing"));
+      break;
     default:
       return false;
   }
diff --git a/android_webview/common/aw_paths.h b/android_webview/common/aw_paths.h
index 7449e6b..52cea85 100644
--- a/android_webview/common/aw_paths.h
+++ b/android_webview/common/aw_paths.h
@@ -15,6 +15,9 @@
 
   DIR_CRASH_DUMPS = PATH_START,  // Directory where crash dumps are written.
 
+  DIR_SAFE_BROWSING,  // Directory where safe browsing related cookies are
+                      // stored.
+
   PATH_END
 };
 
diff --git a/android_webview/tools/system_webview_shell/test/data/webexposed/global-interface-listing-expected.txt b/android_webview/tools/system_webview_shell/test/data/webexposed/global-interface-listing-expected.txt
index dcee1c8..f29be24 100644
--- a/android_webview/tools/system_webview_shell/test/data/webexposed/global-interface-listing-expected.txt
+++ b/android_webview/tools/system_webview_shell/test/data/webexposed/global-interface-listing-expected.txt
@@ -5166,7 +5166,8 @@
     getter animationName
     getter elapsedTime
     method constructor
-interface WebKitCSSMatrix
+interface WebKitCSSMatrix : DOMMatrixReadOnly
+    attribute @@toStringTag
     getter a
     getter b
     getter c
@@ -5190,16 +5191,18 @@
     getter m43
     getter m44
     method constructor
-    method inverse
-    method multiply
-    method rotate
-    method rotateAxisAngle
-    method scale
+    method invertSelf
+    method multiplySelf
+    method preMultiplySelf
+    method rotateAxisAngleSelf
+    method rotateFromVectorSelf
+    method rotateSelf
+    method scale3dSelf
+    method scaleSelf
     method setMatrixValue
-    method skewX
-    method skewY
-    method toString
-    method translate
+    method skewXSelf
+    method skewYSelf
+    method translateSelf
     setter a
     setter b
     setter c
diff --git a/base/BUILD.gn b/base/BUILD.gn
index f1f42b8..f99f960 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2576,7 +2576,6 @@
       "android/java/src/org/chromium/base/PackageUtils.java",
       "android/java/src/org/chromium/base/PathService.java",
       "android/java/src/org/chromium/base/PathUtils.java",
-      "android/java/src/org/chromium/base/PerfTraceEvent.java",
       "android/java/src/org/chromium/base/PowerMonitor.java",
       "android/java/src/org/chromium/base/Promise.java",
       "android/java/src/org/chromium/base/ResourceExtractor.java",
@@ -2698,7 +2697,6 @@
       "test/android/javatests/src/org/chromium/base/test/util/MetricsUtils.java",
       "test/android/javatests/src/org/chromium/base/test/util/MinAndroidSdkLevel.java",
       "test/android/javatests/src/org/chromium/base/test/util/MinAndroidSdkLevelSkipCheck.java",
-      "test/android/javatests/src/org/chromium/base/test/util/PerfTest.java",
       "test/android/javatests/src/org/chromium/base/test/util/Restriction.java",
       "test/android/javatests/src/org/chromium/base/test/util/RestrictionSkipCheck.java",
       "test/android/javatests/src/org/chromium/base/test/util/RetryOnFailure.java",
diff --git a/base/android/java/src/org/chromium/base/PerfTraceEvent.java b/base/android/java/src/org/chromium/base/PerfTraceEvent.java
deleted file mode 100644
index 8a87773..0000000
--- a/base/android/java/src/org/chromium/base/PerfTraceEvent.java
+++ /dev/null
@@ -1,379 +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.
-
-package org.chromium.base;
-
-import android.os.Debug;
-import android.os.Debug.MemoryInfo;
-import android.util.Log;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import org.chromium.base.annotations.SuppressFBWarnings;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.PrintStream;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * PerfTraceEvent can be used like TraceEvent, but is intended for
- * performance measurement.  By limiting the types of tracing we hope
- * to minimize impact on measurement.
- *
- * All PerfTraceEvent events funnel into TraceEvent. When not doing
- * performance measurements, they act the same.  However,
- * PerfTraceEvents can be enabled even when TraceEvent is not.
- *
- * Unlike TraceEvent, PerfTraceEvent data is sent to the system log,
- * not to a trace file.
- *
- * Performance events need to have very specific names so we find
- * the right ones.  For example, we specify the name exactly in
- * the @TracePerf annotation.  Thus, unlike TraceEvent, we do not
- * support an implicit trace name based on the callstack.
- */
-@SuppressFBWarnings("CHROMIUM_SYNCHRONIZED_METHOD")
-public class PerfTraceEvent {
-    private static final int MAX_NAME_LENGTH = 40;
-    private static final String MEMORY_TRACE_NAME_SUFFIX = "_BZR_PSS";
-    private static File sOutputFile;
-
-    /** The event types understood by the perf trace scripts. */
-    private enum EventType {
-        START("S"),
-        FINISH("F"),
-        INSTANT("I");
-
-        // The string understood by the trace scripts.
-        private final String mTypeStr;
-
-        EventType(String typeStr) {
-            mTypeStr = typeStr;
-        }
-
-        @Override
-        public String toString() {
-            return mTypeStr;
-        }
-    }
-
-    private static boolean sEnabled;
-    private static boolean sTrackTiming = true;
-    private static boolean sTrackMemory;
-
-    // A list of performance trace event strings.
-    // Events are stored as a JSON dict much like TraceEvent.
-    // E.g. timestamp is in microseconds.
-    private static JSONArray sPerfTraceStrings;
-
-    // A filter for performance tracing.  Only events that match a
-    // string in the list are saved.  Presence of a filter does not
-    // necessarily mean perf tracing is enabled.
-    private static List<String> sFilter;
-
-    // Nanosecond start time of performance tracing.
-    private static long sBeginNanoTime;
-
-    /**
-     * Specifies what event names will be tracked.
-     *
-     * @param strings Event names we will record.
-     */
-    @VisibleForTesting
-    public static synchronized void setFilter(List<String> strings) {
-        sFilter = new LinkedList<String>(strings);
-    }
-
-    /**
-     * Enable or disable perf tracing.
-     * Disabling of perf tracing will dump trace data to the system log.
-     */
-    @VisibleForTesting
-    public static synchronized void setEnabled(boolean enabled) {
-        if (sEnabled == enabled) {
-            return;
-        }
-        if (enabled) {
-            sBeginNanoTime = System.nanoTime();
-            sPerfTraceStrings = new JSONArray();
-        } else {
-            dumpPerf();
-            sPerfTraceStrings = null;
-            sFilter = null;
-        }
-        sEnabled = enabled;
-    }
-
-    /**
-     * Enables memory tracking for all timing perf events tracked.
-     *
-     * <p>
-     * Only works when called in combination with {@link #setEnabled(boolean)}.
-     *
-     * <p>
-     * By enabling this feature, an additional perf event containing the memory usage will be
-     * logged whenever {@link #instant(String)}, {@link #begin(String)}, or {@link #end(String)}
-     * is called.
-     *
-     * @param enabled Whether to enable memory tracking for all perf events.
-     */
-    @VisibleForTesting
-    public static synchronized void setMemoryTrackingEnabled(boolean enabled) {
-        sTrackMemory = enabled;
-    }
-
-    /**
-     * Enables timing tracking for all perf events tracked.
-     *
-     * <p>
-     * Only works when called in combination with {@link #setEnabled(boolean)}.
-     *
-     * <p>
-     * If this feature is enabled, whenever {@link #instant(String)}, {@link #begin(String)},
-     * or {@link #end(String)} is called the time since start of tracking will be logged.
-     *
-     * @param enabled Whether to enable timing tracking for all perf events.
-     */
-    @VisibleForTesting
-    public static synchronized void setTimingTrackingEnabled(boolean enabled) {
-        sTrackTiming = enabled;
-    }
-
-    /**
-     * @return True if tracing is enabled, false otherwise.
-     * It is safe to call trace methods without checking if PerfTraceEvent
-     * is enabled.
-     */
-    @VisibleForTesting
-    public static synchronized boolean enabled() {
-        return sEnabled;
-    }
-
-    /**
-     * Record an "instant" perf trace event.  E.g. "screen update happened".
-     */
-    public static synchronized void instant(String name) {
-        // Instant doesn't really need/take an event id, but this should be okay.
-        final long eventId = name.hashCode();
-        TraceEvent.instant(name);
-        if (sEnabled && matchesFilter(name)) {
-            savePerfString(name, eventId, EventType.INSTANT, false);
-        }
-    }
-
-
-    /**
-     * Record an "begin" perf trace event.
-     * Begin trace events should have a matching end event.
-     */
-    @VisibleForTesting
-    public static synchronized void begin(String name) {
-        final long eventId = name.hashCode();
-        TraceEvent.startAsync(name, eventId);
-        if (sEnabled && matchesFilter(name)) {
-            // Done before calculating the starting perf data to ensure calculating the memory usage
-            // does not influence the timing data.
-            if (sTrackMemory) {
-                savePerfString(makeMemoryTraceNameFromTimingName(name), eventId, EventType.START,
-                        true);
-            }
-            if (sTrackTiming) {
-                savePerfString(name, eventId, EventType.START, false);
-            }
-        }
-    }
-
-    /**
-     * Record an "end" perf trace event, to match a begin event.  The
-     * time delta between begin and end is usually interesting to
-     * graph code.
-     */
-    @VisibleForTesting
-    public static synchronized void end(String name) {
-        final long eventId = name.hashCode();
-        TraceEvent.finishAsync(name, eventId);
-        if (sEnabled && matchesFilter(name)) {
-            if (sTrackTiming) {
-                savePerfString(name, eventId, EventType.FINISH, false);
-            }
-            // Done after calculating the ending perf data to ensure calculating the memory usage
-            // does not influence the timing data.
-            if (sTrackMemory) {
-                savePerfString(makeMemoryTraceNameFromTimingName(name), eventId, EventType.FINISH,
-                        true);
-            }
-        }
-    }
-
-    /**
-     * Record an "begin" memory trace event.
-     * Begin trace events should have a matching end event.
-     */
-    @VisibleForTesting
-    public static synchronized void begin(String name, MemoryInfo memoryInfo) {
-        final long eventId = name.hashCode();
-        TraceEvent.startAsync(name, eventId);
-        if (sEnabled && matchesFilter(name)) {
-            // Done before calculating the starting perf data to ensure calculating the memory usage
-            // does not influence the timing data.
-            long timestampUs = (System.nanoTime() - sBeginNanoTime) / 1000;
-            savePerfString(makeMemoryTraceNameFromTimingName(name), eventId, EventType.START,
-                    timestampUs, memoryInfo);
-            if (sTrackTiming) {
-                savePerfString(name, eventId, EventType.START, false);
-            }
-        }
-    }
-
-    /**
-     * Record an "end" memory trace event, to match a begin event.  The
-     * memory usage delta between begin and end is usually interesting to
-     * graph code.
-     */
-    @VisibleForTesting
-    public static synchronized void end(String name, MemoryInfo memoryInfo) {
-        final long eventId = name.hashCode();
-        TraceEvent.finishAsync(name, eventId);
-        if (sEnabled && matchesFilter(name)) {
-            if (sTrackTiming) {
-                savePerfString(name, eventId, EventType.FINISH, false);
-            }
-            // Done after calculating the instant perf data to ensure calculating the memory usage
-            // does not influence the timing data.
-            long timestampUs = (System.nanoTime() - sBeginNanoTime) / 1000;
-            savePerfString(makeMemoryTraceNameFromTimingName(name), eventId, EventType.FINISH,
-                    timestampUs, memoryInfo);
-        }
-    }
-
-    /**
-     * Determine if we are interested in this trace event.
-     * @return True if the name matches the allowed filter; else false.
-     */
-    private static boolean matchesFilter(String name) {
-        return sFilter != null ? sFilter.contains(name) : false;
-    }
-
-    /**
-     * Save a perf trace event as a JSON dict.  The format mirrors a TraceEvent dict.
-     *
-     * @param name The trace data
-     * @param id The id of the event
-     * @param type the type of trace event (I, S, F)
-     * @param includeMemory Whether to include current browser process memory usage in the trace.
-     */
-    private static void savePerfString(String name, long id, EventType type,
-            boolean includeMemory) {
-        long timestampUs = (System.nanoTime() - sBeginNanoTime) / 1000;
-        MemoryInfo memInfo = null;
-        if (includeMemory) {
-            memInfo = new MemoryInfo();
-            Debug.getMemoryInfo(memInfo);
-        }
-        savePerfString(name, id, type, timestampUs, memInfo);
-    }
-
-    /**
-     * Save a perf trace event as a JSON dict.  The format mirrors a TraceEvent dict.
-     *
-     * @param name The trace data
-     * @param id The id of the event
-     * @param type the type of trace event (I, S, F)
-     * @param timestampUs The time stamp at which this event was recorded
-     * @param memoryInfo Memory details to be included in this perf string, null if
-     *                   no memory details are to be included.
-     */
-    private static void savePerfString(String name, long id, EventType type, long timestampUs,
-            MemoryInfo memoryInfo) {
-        try {
-            JSONObject traceObj = new JSONObject();
-            traceObj.put("cat", "Java");
-            traceObj.put("ts", timestampUs);
-            traceObj.put("ph", type);
-            traceObj.put("name", name);
-            traceObj.put("id", id);
-            if (memoryInfo != null) {
-                int pss = memoryInfo.nativePss + memoryInfo.dalvikPss + memoryInfo.otherPss;
-                traceObj.put("mem", pss);
-            }
-            sPerfTraceStrings.put(traceObj);
-        } catch (JSONException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /**
-     * Generating a trace name for tracking memory based on the timing name passed in.
-     *
-     * @param name The timing name to use as a base for the memory perf name.
-     * @return The memory perf name to use.
-     */
-    public static String makeMemoryTraceNameFromTimingName(String name) {
-        return makeSafeTraceName(name, MEMORY_TRACE_NAME_SUFFIX);
-    }
-
-    /**
-     * Builds a name to be used in the perf trace framework.  The framework has length requirements
-     * for names, so this ensures the generated name does not exceed the maximum (trimming the
-     * base name if necessary).
-     *
-     * @param baseName The base name to use when generating the name.
-     * @param suffix The required suffix to be appended to the name.
-     * @return A name that is safe for the perf trace framework.
-     */
-    public static String makeSafeTraceName(String baseName, String suffix) {
-        int suffixLength = suffix.length();
-
-        if (baseName.length() + suffixLength > MAX_NAME_LENGTH) {
-            baseName = baseName.substring(0, MAX_NAME_LENGTH - suffixLength);
-        }
-        return baseName + suffix;
-    }
-
-    /**
-     * Sets a file to dump the results to.  If {@code file} is {@code null}, it will be dumped
-     * to STDOUT, otherwise the JSON performance data will be appended to {@code file}.  This should
-     * be called before the performance run starts.  When {@link #setEnabled(boolean)} is called
-     * with {@code false}, the perf data will be dumped.
-     *
-     * @param file Which file to append the performance data to.  If {@code null}, the performance
-     *             data will be sent to STDOUT.
-     */
-    @VisibleForTesting
-    public static synchronized void setOutputFile(File file) {
-        sOutputFile = file;
-    }
-
-    /**
-     * Dump all performance data we have saved up to the log.
-     * Output as JSON for parsing convenience.
-     */
-    private static void dumpPerf() {
-        String json = sPerfTraceStrings.toString();
-
-        if (sOutputFile == null) {
-            System.out.println(json);
-        } else {
-            try {
-                PrintStream stream = new PrintStream(new FileOutputStream(sOutputFile, true));
-                try {
-                    stream.print(json);
-                } finally {
-                    try {
-                        stream.close();
-                    } catch (Exception ex) {
-                        Log.e("PerfTraceEvent", "Unable to close perf trace output file.");
-                    }
-                }
-            } catch (FileNotFoundException ex) {
-                Log.e("PerfTraceEvent", "Unable to dump perf trace data to output file.");
-            }
-        }
-    }
-}
diff --git a/base/debug/activity_tracker.cc b/base/debug/activity_tracker.cc
index 04a293cd..3ff8928 100644
--- a/base/debug/activity_tracker.cc
+++ b/base/debug/activity_tracker.cc
@@ -1601,6 +1601,12 @@
   process_data_.SetString(key, group_name);
 }
 
+void GlobalActivityTracker::RecordException(const void* pc,
+                                            const void* origin,
+                                            uint32_t code) {
+  RecordExceptionImpl(pc, origin, code);
+}
+
 void GlobalActivityTracker::MarkDeleted() {
   allocator_->SetMemoryState(PersistentMemoryAllocator::MEMORY_DELETED);
 }
diff --git a/base/debug/activity_tracker.h b/base/debug/activity_tracker.h
index 0a37935..b8eeffd6 100644
--- a/base/debug/activity_tracker.h
+++ b/base/debug/activity_tracker.h
@@ -1021,6 +1021,7 @@
     return RecordExceptionImpl(::tracked_objects::GetProgramCounter(), origin,
                                code);
   }
+  void RecordException(const void* pc, const void* origin, uint32_t code);
 
   // Marks the tracked data as deleted.
   void MarkDeleted();
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/PerfTest.java b/base/test/android/javatests/src/org/chromium/base/test/util/PerfTest.java
deleted file mode 100644
index 9b3495c..0000000
--- a/base/test/android/javatests/src/org/chromium/base/test/util/PerfTest.java
+++ /dev/null
@@ -1,88 +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.
-
-package org.chromium.base.test.util;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * This annotation tells the test harness that this method will be used in a performance test.
- * This means that the test harness will use the parameters here to figure out which trace calls
- * to track specifically for this test.
- * <p>
- * Each of the lists ({@link #traceNames()}, {@link #graphNames()},
- * and {@link #seriesNames()}) should have the same number of
- * elements.
- * <p>
- * To write a performance test, you need to do the following:
- * <p><ol>
- * <li>Add TraceEvent calls to the code that you want to track.
- *   <ul>
- *   <li> For FPS, add a TraceEvent.instant call where you want to time and detect calls.
- *   <li> For code segment timing, add {@link org.chromium.base.TraceEvent#begin()}/
- * {@link org.chromium.base.TraceEvent#end()} calls around the code
- * segment (does not have to be in the same method).
- *   </ul>
- * <li> Write a Java Automated UI Test that instruments this code.
- * <li> Add this PerfTest annotation to the test method.
- *   <ul>
- *   <li> traceNames must be a list of the names of all of the TraceEvent calls you want to track.
- *   <li> graphNames must be a list, one for each traceName, of which graph the trace data should be
- *   placed in (does not have to be unique).
- *   <li> seriesNames must be a list, one for each traceName, of what the series should be called
- *   for this trace data (has to be unique per graphName).
- * <li> When checked in, the buildbots will automatically run this test and the results will show up
- * under the Java Automation UI Performance graph, where there will be tabs for each graphName
- * specified.
- * <li> To test your performance test, run the following command and you should see the performance
- * numbers printed to the console.
- * </ol>
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD})
-public @interface PerfTest {
-    /**
-     * @return A list of the trace calls to track.
-     */
-    public String[] traceNames();
-
-    /**
-     * @return A list, one for each traceName, that represents which graph this trace call should
-     *         be output on.  This does not have to be unique if there are multiple series per
-     *         graph.
-     */
-    public String[] graphNames();
-
-    /**
-     * @return A list, one for each traceName, that represents the series this trace call should be
-     *         on the corresponding graph.  This should be unique.
-     */
-    public String[] seriesNames();
-
-    /**
-     * @return Whether or not we should automatically start and stop tracing for the test.  This
-     *         makes it easier to run some tests where tracing is started and stopped at the
-     *         beginning and end of that particular test.
-     */
-    public boolean autoTrace() default false;
-
-    /**
-     * @return Whether this performance test should track memory usage in addition to time.  If
-     *         true, this will track memory usage when tracking time deltas or instants.  With each
-     *         graph defined in the annotation for tracking time, this will add an additional graph
-     *         suffixed with a memory identifier containing the same series as those tracking the
-     *         timing performance but instead will be tracking memory consumption.
-     */
-    public boolean traceMemory() default true;
-
-    /**
-     * @return Whether this performance test should track time or (optionally) only memory.  If
-     *         false, this will not automatically track time deltas or instants when logging
-     *         memory info.
-     */
-    public boolean traceTiming() default true;
-}
diff --git a/build/android/pylib/instrumentation/instrumentation_test_instance.py b/build/android/pylib/instrumentation/instrumentation_test_instance.py
index 6f31a33..c1253d9 100644
--- a/build/android/pylib/instrumentation/instrumentation_test_instance.py
+++ b/build/android/pylib/instrumentation/instrumentation_test_instance.py
@@ -34,7 +34,7 @@
     'SmallTest', 'MediumTest', 'LargeTest', 'EnormousTest', 'IntegrationTest']
 _EXCLUDE_UNLESS_REQUESTED_ANNOTATIONS = [
     'DisabledTest', 'FlakyTest']
-_VALID_ANNOTATIONS = set(['Manual', 'PerfTest'] + _DEFAULT_ANNOTATIONS +
+_VALID_ANNOTATIONS = set(['Manual'] + _DEFAULT_ANNOTATIONS +
                          _EXCLUDE_UNLESS_REQUESTED_ANNOTATIONS)
 _EXTRA_DRIVER_TEST_LIST = (
     'org.chromium.test.driver.OnDeviceInstrumentationDriver.TestList')
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 2b631c60..91fbb27 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -1207,6 +1207,8 @@
   subtree_property_changed_ = false;
   inputs_.update_rect = gfx::Rect();
 
+  if (mask_layer())
+    DCHECK_EQ(bounds().ToString(), mask_layer()->bounds().ToString());
   layer_tree_host_->RemoveLayerShouldPushProperties(this);
 }
 
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
index e55c86f4..bd4c508 100644
--- a/cc/layers/layer_unittest.cc
+++ b/cc/layers/layer_unittest.cc
@@ -324,8 +324,9 @@
   // Should be a different size than previous call, to ensure it marks tree
   // changed.
   gfx::Size arbitrary_size = gfx::Size(111, 222);
-  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1);
+  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(2);
   EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetBounds(arbitrary_size));
+  EXECUTE_AND_VERIFY_SUBTREE_CHANGED(dummy_layer1->SetBounds(arbitrary_size));
   EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET(
       root->PushPropertiesTo(root_impl.get());
       child->PushPropertiesTo(child_impl.get());
diff --git a/cc/test/data/mask_of_larger_layer.png b/cc/test/data/mask_of_larger_layer.png
deleted file mode 100644
index d133ebd..0000000
--- a/cc/test/data/mask_of_larger_layer.png
+++ /dev/null
Binary files differ
diff --git a/cc/trees/layer_tree_host_pixeltest_masks.cc b/cc/trees/layer_tree_host_pixeltest_masks.cc
index 6e013786..1e7cf22 100644
--- a/cc/trees/layer_tree_host_pixeltest_masks.cc
+++ b/cc/trees/layer_tree_host_pixeltest_masks.cc
@@ -149,41 +149,6 @@
       base::FilePath(FILE_PATH_LITERAL("mask_of_clipped_layer.png")));
 }
 
-TEST_P(LayerTreeHostMasksPixelTest, MaskOfLargerLayer) {
-  scoped_refptr<SolidColorLayer> background =
-      CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE);
-
-  scoped_refptr<SolidColorLayer> green = CreateSolidColorLayerWithBorder(
-      gfx::Rect(0, 0, 100, 100), kCSSGreen, 1, SK_ColorBLACK);
-  background->AddChild(green);
-
-  gfx::Size mask_bounds(50, 50);
-  MaskContentLayerClient client(mask_bounds);
-  scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client);
-  mask->SetBounds(mask_bounds);
-  mask->SetIsDrawable(true);
-  mask->SetLayerMaskType(mask_type_);
-  green->SetMaskLayer(mask.get());
-
-  if (raster_buffer_provider_type_ == RASTER_BUFFER_PROVIDER_TYPE_BITMAP) {
-    // Bitmap produces a sharper (but equivalent sized) mask.
-    float percentage_pixels_large_error = 40.0f;
-    float percentage_pixels_small_error = 0.0f;
-    float average_error_allowed_in_bad_pixels = 65.0f;
-    int large_error_allowed = 120;
-    int small_error_allowed = 0;
-    pixel_comparator_.reset(new FuzzyPixelComparator(
-        true,  // discard_alpha
-        percentage_pixels_large_error, percentage_pixels_small_error,
-        average_error_allowed_in_bad_pixels, large_error_allowed,
-        small_error_allowed));
-  }
-
-  RunPixelResourceTest(
-      background,
-      base::FilePath(FILE_PATH_LITERAL("mask_of_larger_layer.png")));
-}
-
 TEST_P(LayerTreeHostMasksPixelTest, MaskOfLayerNonExactTextureSize) {
   scoped_refptr<SolidColorLayer> background =
       CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE);
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 423f812..1bb3e30 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -1685,6 +1685,7 @@
     scoped_refptr<Layer> root = Layer::Create();
     root->SetBounds(gfx::Size(10, 10));
     child_layer_ = make_scoped_refptr(new UpdateCountingLayer);
+    child_layer_->SetBounds(gfx::Size(10, 10));
     mask_layer_ = make_scoped_refptr(new UpdateCountingLayer);
     mask_layer_->SetBounds(gfx::Size(10, 10));
     child_layer_->SetMaskLayer(mask_layer_.get());
@@ -6991,12 +6992,12 @@
     content_layer->AddChild(content_child_layer);
 
     std::unique_ptr<RecordingSource> recording_source =
-        FakeRecordingSource::CreateFilledRecordingSource(gfx::Size(100, 100));
+        FakeRecordingSource::CreateFilledRecordingSource(gfx::Size(50, 50));
     PaintFlags paint1, paint2;
     static_cast<FakeRecordingSource*>(recording_source.get())
-        ->add_draw_rect_with_flags(gfx::Rect(0, 0, 100, 90), paint1);
+        ->add_draw_rect_with_flags(gfx::Rect(0, 0, 50, 40), paint1);
     static_cast<FakeRecordingSource*>(recording_source.get())
-        ->add_draw_rect_with_flags(gfx::Rect(0, 90, 100, 10), paint2);
+        ->add_draw_rect_with_flags(gfx::Rect(0, 40, 50, 10), paint2);
     client_.set_fill_with_nonsolid_color(true);
     static_cast<FakeRecordingSource*>(recording_source.get())->Rerecord();
 
@@ -7023,7 +7024,7 @@
     content_child_layer->SetBounds(child_size);
     content_child_layer->SetPosition(gfx::PointF(20.f, 0.f));
 
-    gfx::Size mask_size(100, 100);
+    gfx::Size mask_size(50, 50);
     mask_layer->SetBounds(mask_size);
     mask_layer->SetLayerMaskType(Layer::LayerMaskType::MULTI_TEXTURE_MASK);
     mask_layer_id_ = mask_layer->id();
@@ -7241,131 +7242,6 @@
 
 SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeTestMultiTextureMaskLayerWithScaling);
 
-class LayerTreeTestMaskLayerWithDifferentBounds : public LayerTreeTest {
- protected:
-  void SetupTree() override {
-    // The mask layer has bounds 100x100 but is attached to a layer with bounds
-    // 50x50.
-
-    scoped_refptr<Layer> root = Layer::Create();
-
-    scoped_refptr<FakePictureLayer> content_layer =
-        FakePictureLayer::Create(&client_);
-    root->AddChild(content_layer);
-
-    std::unique_ptr<RecordingSource> recording_source =
-        FakeRecordingSource::CreateFilledRecordingSource(gfx::Size(100, 100));
-    PaintFlags paint1, paint2;
-    static_cast<FakeRecordingSource*>(recording_source.get())
-        ->add_draw_rect_with_flags(gfx::Rect(0, 0, 100, 90), paint1);
-    static_cast<FakeRecordingSource*>(recording_source.get())
-        ->add_draw_rect_with_flags(gfx::Rect(0, 90, 100, 10), paint2);
-    client_.set_fill_with_nonsolid_color(true);
-    static_cast<FakeRecordingSource*>(recording_source.get())->Rerecord();
-
-    scoped_refptr<FakePictureLayer> mask_layer =
-        FakePictureLayer::CreateWithRecordingSource(
-            &client_, std::move(recording_source));
-    content_layer->SetMaskLayer(mask_layer.get());
-
-    gfx::Size root_size(100, 100);
-    root->SetBounds(root_size);
-
-    gfx::Size layer_size(50, 50);
-    content_layer->SetBounds(layer_size);
-
-    gfx::Size mask_size(100, 100);
-    mask_layer->SetBounds(mask_size);
-    mask_layer->SetLayerMaskType(Layer::LayerMaskType::MULTI_TEXTURE_MASK);
-
-    layer_tree_host()->SetRootLayer(root);
-    LayerTreeTest::SetupTree();
-    client_.set_bounds(root->bounds());
-  }
-
-  void BeginTest() override { PostSetNeedsCommitToMainThread(); }
-
-  DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
-                                   LayerTreeHostImpl::FrameData* frame_data,
-                                   DrawResult draw_result) override {
-    EXPECT_EQ(2u, frame_data->render_passes.size());
-    RenderPass* root_pass = frame_data->render_passes.back().get();
-    EXPECT_EQ(2u, root_pass->quad_list.size());
-
-    // There's a solid color quad under everything.
-    EXPECT_EQ(DrawQuad::SOLID_COLOR, root_pass->quad_list.back()->material);
-
-    EXPECT_EQ(DrawQuad::RENDER_PASS, root_pass->quad_list.front()->material);
-    const RenderPassDrawQuad* render_pass_quad =
-        RenderPassDrawQuad::MaterialCast(root_pass->quad_list.front());
-    switch (host_impl->active_tree()->source_frame_number()) {
-      case 0:
-        // Check that the mask fills the surface.
-        EXPECT_EQ(gfx::Rect(0, 0, 50, 50).ToString(),
-                  render_pass_quad->rect.ToString());
-        if (host_impl->settings().enable_mask_tiling) {
-          EXPECT_EQ(gfx::RectF(0.f, 0.f, 50.f / 128.f, 50.f / 128.f).ToString(),
-                    render_pass_quad->mask_uv_rect.ToString());
-        } else {
-          EXPECT_EQ(gfx::RectF(0.f, 0.f, 1.f, 1.f).ToString(),
-                    render_pass_quad->mask_uv_rect.ToString());
-        }
-        break;
-      case 1:
-        // Applying a DSF should change the render surface size, but won't
-        // affect which part of the mask is used.
-        EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(),
-                  render_pass_quad->rect.ToString());
-        if (host_impl->settings().enable_mask_tiling) {
-          EXPECT_EQ(gfx::RectF(0.f, 0.f, 50.f / 128.f, 50.f / 128.f).ToString(),
-                    render_pass_quad->mask_uv_rect.ToString());
-        } else {
-          EXPECT_EQ(gfx::RectF(0.f, 0.f, 1.f, 1.f).ToString(),
-                    render_pass_quad->mask_uv_rect.ToString());
-        }
-        EndTest();
-        break;
-    }
-    return draw_result;
-  }
-
-  void DidCommit() override {
-    switch (layer_tree_host()->SourceFrameNumber()) {
-      case 1:
-        gfx::Size double_root_size(200, 200);
-        layer_tree_host()->SetViewportSize(double_root_size);
-        layer_tree_host()->SetDeviceScaleFactor(2.f);
-        break;
-    }
-  }
-
-  void AfterTest() override {}
-
-  FakeContentLayerClient client_;
-};
-
-class LayerTreeTestSingleTextureMaskLayerWithDifferentBounds
-    : public LayerTreeTestMaskLayerWithDifferentBounds {
- public:
-  void InitializeSettings(LayerTreeSettings* settings) override {
-    settings->enable_mask_tiling = false;
-  }
-};
-
-SINGLE_AND_MULTI_THREAD_TEST_F(
-    LayerTreeTestSingleTextureMaskLayerWithDifferentBounds);
-
-class LayerTreeTestMultiTextureMaskLayerWithDifferentBounds
-    : public LayerTreeTestMaskLayerWithDifferentBounds {
- public:
-  void InitializeSettings(LayerTreeSettings* settings) override {
-    settings->enable_mask_tiling = true;
-  }
-};
-
-SINGLE_AND_MULTI_THREAD_TEST_F(
-    LayerTreeTestMultiTextureMaskLayerWithDifferentBounds);
-
 class LayerTreeTestMaskWithNonExactTextureSize : public LayerTreeTest {
  protected:
   void SetupTree() override {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
index 4823a10..20f8297f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
@@ -22,7 +22,6 @@
 import android.widget.ArrayAdapter;
 import android.widget.ListPopupWindow;
 
-import org.chromium.base.PerfTraceEvent;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.SuppressFBWarnings;
 import org.chromium.chrome.R;
@@ -443,7 +442,6 @@
      * @return     Whether or not animations are done.
      */
     public boolean updateLayout(long time, long dt) {
-        PerfTraceEvent.instant("StripLayoutHelper:updateLayout");
         final boolean doneAnimating = onUpdateAnimation(time, false);
         updateStrip();
 
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index f25e2a9..21098432 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -856,8 +856,6 @@
     "page_load_metrics/observers/previews_page_load_metrics_observer.h",
     "page_load_metrics/observers/protocol_page_load_metrics_observer.cc",
     "page_load_metrics/observers/protocol_page_load_metrics_observer.h",
-    "page_load_metrics/observers/resource_tracking_page_load_metrics_observer.cc",
-    "page_load_metrics/observers/resource_tracking_page_load_metrics_observer.h",
     "page_load_metrics/observers/service_worker_page_load_metrics_observer.cc",
     "page_load_metrics/observers/service_worker_page_load_metrics_observer.h",
     "page_load_metrics/observers/subresource_filter_metrics_observer.cc",
diff --git a/chrome/browser/chrome_browser_field_trials_desktop.cc b/chrome/browser/chrome_browser_field_trials_desktop.cc
index caf4d3a0..64dd5a3 100644
--- a/chrome/browser/chrome_browser_field_trials_desktop.cc
+++ b/chrome/browser/chrome_browser_field_trials_desktop.cc
@@ -34,6 +34,7 @@
 #include "chrome/install_static/install_util.h"
 #include "components/browser_watcher/features.h"
 #include "components/browser_watcher/stability_data_names.h"
+#include "components/browser_watcher/stability_debugging.h"
 #include "components/browser_watcher/stability_metrics.h"
 #include "components/browser_watcher/stability_paths.h"
 #endif
@@ -188,6 +189,8 @@
     // of this, there is no need to flush it here.
     metrics::GlobalPersistentSystemProfile::GetInstance()
         ->RegisterPersistentAllocator(global_tracker->allocator());
+
+    browser_watcher::RegisterStabilityVEH();
   }
 }
 #endif  // defined(OS_WIN)
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index c6dfc3db..efc1b8e 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -1428,17 +1428,6 @@
   IncognitoModePrefs::InitializePlatformParentalControls();
 #endif
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-  if (!variations::GetVariationParamValue(
-      "LightSpeed", "EarlyInitStartup").empty()) {
-    // Try to compute this early on another thread so that we don't spend time
-    // during profile load initializing the extensions APIs.
-    BrowserThread::PostTask(BrowserThread::FILE_USER_BLOCKING, FROM_HERE,
-                            base::BindOnce(base::IgnoreResult(
-                                &extensions::FeatureProvider::GetAPIFeatures)));
-  }
-#endif
-
   // Android updates the metrics service dynamically depending on whether the
   // application is in the foreground or not. Do not start here.
 #if !defined(OS_ANDROID)
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 3449d44..ee5890e 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -3432,6 +3432,10 @@
   task_scheduler_util::MaybePerformBrowserTaskSchedulerRedirection();
 }
 
+base::FilePath ChromeContentBrowserClient::GetLoggingFileName() {
+  return logging::GetLogFileName();
+}
+
 // static
 void ChromeContentBrowserClient::SetDefaultQuotaSettingsForTesting(
     const storage::QuotaSettings* settings) {
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index c11d7b30..734f7e0 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -317,6 +317,7 @@
   std::unique_ptr<base::TaskScheduler::InitParams> GetTaskSchedulerInitParams()
       override;
   void PerformExperimentalTaskSchedulerRedirections() override;
+  base::FilePath GetLoggingFileName() override;
 
  private:
   friend class DisableWebRtcEncryptionFlagTest;
diff --git a/chrome/browser/chrome_content_browser_client_unittest.cc b/chrome/browser/chrome_content_browser_client_unittest.cc
index 853335e..53548f6 100644
--- a/chrome/browser/chrome_content_browser_client_unittest.cc
+++ b/chrome/browser/chrome_content_browser_client_unittest.cc
@@ -342,3 +342,11 @@
 
 }  // namespace content
 #endif  // !defined(OS_ANDROID)
+
+class ChromeContentBrowserClientGetLoggingFileTest : public testing::Test {};
+
+TEST_F(ChromeContentBrowserClientGetLoggingFileTest, GetLoggingFile) {
+  ChromeContentBrowserClient client;
+  base::FilePath log_file_name;
+  EXPECT_FALSE(client.GetLoggingFileName().empty());
+}
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 5d01795..01148fcd 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -278,12 +278,6 @@
         DBusThreadManager::Get()->GetPowerManagerClient());
 
     CrosDBusService::ServiceProviderList service_providers;
-    // TODO(derat): Remove this provider once all callers are using
-    // |proxy_resolution_service_| instead: http://crbug.com/703217
-    service_providers.push_back(
-        base::MakeUnique<ProxyResolutionServiceProvider>(
-            kLibCrosServiceInterface, kResolveNetworkProxy,
-            base::MakeUnique<ChromeProxyResolutionServiceProviderDelegate>()));
     if (GetAshConfig() == ash::Config::CLASSIC) {
       // TODO(crbug.com/629707): revisit this with mustash dbus work.
       service_providers.push_back(base::MakeUnique<DisplayPowerServiceProvider>(
@@ -313,8 +307,6 @@
         kNetworkProxyServiceName, dbus::ObjectPath(kNetworkProxyServicePath),
         CrosDBusService::CreateServiceProviderList(
             base::MakeUnique<ProxyResolutionServiceProvider>(
-                kNetworkProxyServiceInterface,
-                kNetworkProxyServiceResolveProxyMethod,
                 base::MakeUnique<
                     ChromeProxyResolutionServiceProviderDelegate>())));
 
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
index 675f657..f4bbeb0 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
@@ -1380,6 +1380,11 @@
     std::unique_ptr<user_manager::UserImage>* result_out,
     MovableOnDestroyCallbackHolder on_finish,
     std::unique_ptr<user_manager::UserImage> user_image) {
+  if (user_image->image().isNull()) {
+    LOG(ERROR) << "Failed to decode default wallpaper. ";
+    return;
+  }
+
   *result_out = std::move(user_image);
   SetWallpaper((*result_out)->image(), layout);
 }
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
index 8f1aa9c4..b7e1508 100644
--- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -360,7 +360,14 @@
   ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
 }
 
-IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, WebRequestDeclarative1) {
+// This test times out regularly on MSAN trybots. See http://crbug.com/733395.
+#if defined(MEMORY_SANITIZER)
+#define MAYBE_WebRequestDeclarative1 DISABLED_WebRequestDeclarative1
+#else
+#define MAYBE_WebRequestDeclarative1 WebRequestDeclarative1
+#endif
+IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest,
+                       MAYBE_WebRequestDeclarative1) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   ASSERT_TRUE(RunExtensionSubtest("webrequest", "test_declarative1.html"))
       << message_;
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
index 42a5568..a3ea43e 100644
--- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
+++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
@@ -349,28 +349,6 @@
   }
 }
 
-void NotifyUIThreadOfRequestStarted(
-    const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter,
-    const content::GlobalRequestID& request_id,
-    ResourceType resource_type,
-    bool is_download,
-    base::TimeTicks request_creation_time) {
-  content::WebContents* web_contents = web_contents_getter.Run();
-  if (!web_contents)
-    return;
-
-  if (!is_download) {
-    page_load_metrics::MetricsWebContentsObserver* metrics_observer =
-        page_load_metrics::MetricsWebContentsObserver::FromWebContents(
-            web_contents);
-
-    if (metrics_observer) {
-      metrics_observer->OnRequestStarted(request_id, resource_type,
-                                         request_creation_time);
-    }
-  }
-}
-
 void NotifyUIThreadOfRequestComplete(
     const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter,
     const content::ResourceRequestInfo::FrameTreeNodeIdGetter&
@@ -480,16 +458,6 @@
 
   const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request);
 
-  // TODO(petewil): Unify the safe browsing request and the metrics observer
-  // request if possible so we only have to cross to the main thread once.
-  // http://crbug.com/712312.
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::Bind(&NotifyUIThreadOfRequestStarted,
-                 info->GetWebContentsGetterForRequest(),
-                 info->GetGlobalRequestID(), info->GetResourceType(),
-                 info->IsDownload(), request->creation_time()));
-
   ProfileIOData* io_data = ProfileIOData::FromResourceContext(
       resource_context);
 
diff --git a/chrome/browser/metrics/chrome_metrics_services_manager_client.cc b/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
index b5a5ce6..f7835a84f0 100644
--- a/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
@@ -246,12 +246,6 @@
   return enabled_state_provider_->IsReportingEnabled();
 }
 
-bool ChromeMetricsServicesManagerClient::OnlyDoMetricsRecording() {
-  const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
-  return cmdline->HasSwitch(switches::kMetricsRecordingOnly) ||
-         cmdline->HasSwitch(switches::kEnableBenchmarking);
-}
-
 #if defined(OS_WIN)
 void ChromeMetricsServicesManagerClient::UpdateRunningServices(
     bool may_record,
diff --git a/chrome/browser/metrics/chrome_metrics_services_manager_client.h b/chrome/browser/metrics/chrome_metrics_services_manager_client.h
index ae5947d..24014ac 100644
--- a/chrome/browser/metrics/chrome_metrics_services_manager_client.h
+++ b/chrome/browser/metrics/chrome_metrics_services_manager_client.h
@@ -72,7 +72,6 @@
   CreateEntropyProvider() override;
   net::URLRequestContextGetter* GetURLRequestContext() override;
   bool IsMetricsReportingEnabled() override;
-  bool OnlyDoMetricsRecording() override;
 
 #if defined(OS_WIN)
   // On Windows, the client controls whether Crashpad can upload crash reports.
diff --git a/chrome/browser/metrics/metrics_service_browsertest.cc b/chrome/browser/metrics/metrics_service_browsertest.cc
index c2367bd..1e971e7 100644
--- a/chrome/browser/metrics/metrics_service_browsertest.cc
+++ b/chrome/browser/metrics/metrics_service_browsertest.cc
@@ -19,11 +19,11 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_paths.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/metrics/metrics_pref_names.h"
+#include "components/metrics/metrics_switches.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/base/filename_util.h"
@@ -72,7 +72,7 @@
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     // Enable the metrics service for testing (in recording-only mode).
-    command_line->AppendSwitch(switches::kMetricsRecordingOnly);
+    command_line->AppendSwitch(metrics::switches::kMetricsRecordingOnly);
   }
 
   // Open three tabs then navigate to |crashy_url| and wait for the renderer to
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
index feb2898..ef05b3f 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
@@ -283,24 +283,6 @@
   return nullptr;
 }
 
-void MetricsWebContentsObserver::OnRequestStarted(
-    const content::GlobalRequestID& request_id,
-    content::ResourceType resource_type,
-    base::TimeTicks creation_time) {
-  // Note for the main HTML page, the tracker may not exist yet, so
-  // OnStartedResource will not be called for the main page.  While the main
-  // page is not being tracked today, if we do decide to track it, we will need
-  // to first make sure that GlobalRequestID is set in an earlier callback.
-  // TODO(bmcquade): Evaluate whether moving tracker creation to
-  // DidStartNavigation would address this.
-  PageLoadTracker* tracker =
-      GetTrackerOrNullForRequest(request_id, resource_type, creation_time);
-  if (tracker) {
-    ExtraRequestStartInfo extra_request_start_info(resource_type);
-    tracker->OnStartedResource(extra_request_start_info);
-  }
-}
-
 void MetricsWebContentsObserver::OnRequestComplete(
     const GURL& url,
     const net::HostPortPair& host_port_pair,
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
index ad72ba5..a57a65e 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
@@ -104,12 +104,6 @@
   void WillProcessNavigationResponse(
       content::NavigationHandle* navigation_handle);
 
-  // A resource request started on the IO thread. This method is invoked on
-  // the UI thread.
-  void OnRequestStarted(const content::GlobalRequestID& request_id,
-                        content::ResourceType resource_type,
-                        base::TimeTicks creation_time);
-
   // A resource request completed on the IO thread. This method is invoked on
   // the UI thread.
   void OnRequestComplete(
diff --git a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc
index 00933a7..e01e847 100644
--- a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc
+++ b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc
@@ -90,12 +90,6 @@
     mock_timer->Fire();
 }
 
-void PageLoadMetricsObserverTestHarness::SimulateStartedResource(
-    const ExtraRequestStartInfo& info) {
-  observer_->OnRequestStarted(content::GlobalRequestID(), info.resource_type,
-                              base::TimeTicks::Now());
-}
-
 void PageLoadMetricsObserverTestHarness::SimulateLoadedResource(
     const ExtraRequestCompleteInfo& info) {
   observer_->OnRequestComplete(
diff --git a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h
index 79c2ce5..a5f3a03 100644
--- a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h
+++ b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h
@@ -56,9 +56,6 @@
                                        const mojom::PageLoadMetadata& metadata);
 
   // Simulates a loaded resource.
-  void SimulateStartedResource(const ExtraRequestStartInfo& info);
-
-  // Simulates a loaded resource.
   void SimulateLoadedResource(const ExtraRequestCompleteInfo& info);
 
   // Simulates a user input.
diff --git a/chrome/browser/page_load_metrics/observers/resource_tracking_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/resource_tracking_page_load_metrics_observer.cc
deleted file mode 100644
index 1f74a34..0000000
--- a/chrome/browser/page_load_metrics/observers/resource_tracking_page_load_metrics_observer.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// 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/page_load_metrics/observers/resource_tracking_page_load_metrics_observer.h"
-
-namespace page_load_metrics {
-
-ResourceTrackingPageLoadMetricsObserver::
-    ResourceTrackingPageLoadMetricsObserver()
-    : started_count_(0), completed_count_(0) {}
-ResourceTrackingPageLoadMetricsObserver::
-    ~ResourceTrackingPageLoadMetricsObserver() {}
-
-void ResourceTrackingPageLoadMetricsObserver::OnStartedResource(
-    const ExtraRequestStartInfo& extra_request_start_info) {
-  // TODO(petewiL): Store this by type.
-  ++started_count_;
-}
-
-void ResourceTrackingPageLoadMetricsObserver::OnLoadedResource(
-    const ExtraRequestCompleteInfo& extra_request_complete_info) {
-  // TODO(petewil): Check to see if the type of the request changed.  If it did,
-  // update the old and new types for the started type.  Then update by type for
-  // the completed type.
-  ++completed_count_;
-}
-
-void ResourceTrackingPageLoadMetricsObserver::GetCountsForTypeForTesting(
-    const content::ResourceType type,
-    int64_t* started_count,
-    int64_t* completed_count) {
-  if (started_count != nullptr)
-    *started_count = started_count_;
-  if (completed_count != nullptr)
-    *completed_count = completed_count_;
-}
-
-}  // namespace page_load_metrics
diff --git a/chrome/browser/page_load_metrics/observers/resource_tracking_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/resource_tracking_page_load_metrics_observer.h
deleted file mode 100644
index 58e8d87..0000000
--- a/chrome/browser/page_load_metrics/observers/resource_tracking_page_load_metrics_observer.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// 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 CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_RESOURCE_TRACKING_PAGE_LOAD_METRICS_OBSERVER_H_
-#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_RESOURCE_TRACKING_PAGE_LOAD_METRICS_OBSERVER_H_
-
-#include <stdint.h>
-
-#include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
-
-namespace page_load_metrics {
-
-class ResourceTrackingPageLoadMetricsObserver
-    : public page_load_metrics::PageLoadMetricsObserver {
- public:
-  ResourceTrackingPageLoadMetricsObserver();
-  ~ResourceTrackingPageLoadMetricsObserver() override;
-
-  // Called by the PageLoadMetrics framework when we start a new request, so we
-  // can update our data structures to be able to calculate a resource done
-  // percentage.
-  void OnStartedResource(
-      const ExtraRequestStartInfo& extra_request_start_info) override;
-
-  // Called by the PageLoadMetrics framework when we start a new request, so we
-  // can update our data structures to be able to calculate a resource done
-  // percentage.
-  void OnLoadedResource(
-      const ExtraRequestCompleteInfo& extra_request_complete_info) override;
-
-  // For the specified type, get the count of requests started and completed.
-  // TODO(petewil) Note that type is not used yet, code to use it is coming
-  // soon.
-  void GetCountsForTypeForTesting(const content::ResourceType type,
-                                  int64_t* started_count,
-                                  int64_t* completed_count);
-
- private:
-  // TODO(petewil): Some way to keep track of what we've seen
-  // TODO(petewil): Some way to inform our keeper of aggregate results when they
-  // change.
-  int64_t started_count_;
-  int64_t completed_count_;
-};
-
-}  // namespace page_load_metrics
-
-#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_RESOURCE_TRACKING_PAGE_LOAD_METRICS_OBSERVER_H_
diff --git a/chrome/browser/page_load_metrics/observers/resource_tracking_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/resource_tracking_page_load_metrics_observer_unittest.cc
deleted file mode 100644
index 7f8d5a2a..0000000
--- a/chrome/browser/page_load_metrics/observers/resource_tracking_page_load_metrics_observer_unittest.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-// 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/page_load_metrics/observers/resource_tracking_page_load_metrics_observer.h"
-
-#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
-
-namespace {
-const char kFakeUrl[] = "http://www.google.com/nothingotseehere.html";
-}  // namespace
-
-namespace page_load_metrics {
-
-class ResourceTrackingPageLoadMetricsObserverTest
-    : public page_load_metrics::PageLoadMetricsObserverTestHarness {
- public:
-  ResourceTrackingPageLoadMetricsObserverTest() : observer_(nullptr) {}
-
- protected:
-  void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) override {
-    std::unique_ptr<page_load_metrics::ResourceTrackingPageLoadMetricsObserver>
-        observer(
-            new page_load_metrics::ResourceTrackingPageLoadMetricsObserver());
-    // Keep track of the observer pointer so we can check it in the unit test.
-    observer_ = observer.get();
-    tracker->AddObserver(std::move(observer));
-  }
-
-  page_load_metrics::ResourceTrackingPageLoadMetricsObserver* observer() {
-    return observer_;
-  }
-
- private:
-  // observer_ is owned by the last created PageLoadTracker, and its lifetime is
-  // dictated by that tracker's lifetime.
-  page_load_metrics::ResourceTrackingPageLoadMetricsObserver* observer_;
-};
-
-TEST_F(ResourceTrackingPageLoadMetricsObserverTest, StartAndFinish) {
-  page_load_metrics::ExtraRequestStartInfo start_info_1{
-      content::ResourceType::RESOURCE_TYPE_IMAGE};
-
-  page_load_metrics::ExtraRequestStartInfo start_info_2{
-      content::ResourceType::RESOURCE_TYPE_IMAGE};
-
-  page_load_metrics::ExtraRequestCompleteInfo done_info{
-      GURL(),
-      net::HostPortPair(),
-      -1 /*frame_tree_node_id*/,
-      false /*was_cached*/,
-      1024 * 40 /* raw_body_bytes */,
-      1024 * 40 /* original_network_content_length */,
-      nullptr /* data reduction_proxy */,
-      content::ResourceType::RESOURCE_TYPE_IMAGE,
-      0,
-  };
-
-  // Start the navigation. This will create the page load tracker and register
-  // the observers.
-  NavigateAndCommit(GURL(kFakeUrl));
-
-  // Simulate starting two images, and completing one.
-  SimulateStartedResource(start_info_1);
-  SimulateStartedResource(start_info_2);
-  SimulateLoadedResource(done_info);
-
-  int64_t started = -1;
-  int64_t completed = -1;
-  EXPECT_NE(nullptr, observer());
-  observer()->GetCountsForTypeForTesting(
-      content::ResourceType::RESOURCE_TYPE_IMAGE, &started, &completed);
-  EXPECT_EQ(2, started);
-  EXPECT_EQ(1, completed);
-}
-
-}  // namespace page_load_metrics
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/page_load_metrics_observer.cc
index 54e0435..fc708c4 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_observer.cc
@@ -81,14 +81,6 @@
 
 ExtraRequestCompleteInfo::~ExtraRequestCompleteInfo() {}
 
-ExtraRequestStartInfo::ExtraRequestStartInfo(content::ResourceType found_type)
-    : resource_type(found_type) {}
-
-ExtraRequestStartInfo::ExtraRequestStartInfo(
-    const ExtraRequestStartInfo& other) = default;
-
-ExtraRequestStartInfo::~ExtraRequestStartInfo() {}
-
 FailedProvisionalLoadInfo::FailedProvisionalLoadInfo(base::TimeDelta interval,
                                                      net::Error error)
     : time_to_failed_provisional_load(interval), error(error) {}
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_observer.h b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
index 4117133..9ee8b5d 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
@@ -256,21 +256,6 @@
   int net_error;
 };
 
-// Container for various information about a started request within a page load.
-struct ExtraRequestStartInfo {
-  explicit ExtraRequestStartInfo(content::ResourceType type);
-
-  ExtraRequestStartInfo(const ExtraRequestStartInfo& other);
-
-  ~ExtraRequestStartInfo();
-
-  // The type of the request as gleaned from the DOM or the file extension. This
-  // may be less accurate than the type at request completion time, which has
-  // access to mime-type headers.  During XHRs, we sometimes see resources come
-  // back as a different type than we expected.
-  const content::ResourceType resource_type;
-};
-
 // Interface for PageLoadMetrics observers. All instances of this class are
 // owned by the PageLoadTracker tracking a page load.
 class PageLoadMetricsObserver {
@@ -459,10 +444,6 @@
       const FailedProvisionalLoadInfo& failed_provisional_load_info,
       const PageLoadExtraInfo& extra_info) {}
 
-  // Called whenever a request load begins.
-  virtual void OnStartedResource(
-      const ExtraRequestStartInfo& extra_request_start_info) {}
-
   // Called whenever a request is loaded for this page load. This comes
   // unfiltered from the ResourceDispatcherHost and may include blob requests
   // and data uris.
diff --git a/chrome/browser/page_load_metrics/page_load_tracker.cc b/chrome/browser/page_load_metrics/page_load_tracker.cc
index 9f83e20f..6847eeb 100644
--- a/chrome/browser/page_load_metrics/page_load_tracker.cc
+++ b/chrome/browser/page_load_metrics/page_load_tracker.cc
@@ -418,13 +418,6 @@
   }
 }
 
-void PageLoadTracker::OnStartedResource(
-    const ExtraRequestStartInfo& extra_request_start_info) {
-  for (const auto& observer : observers_) {
-    observer->OnStartedResource(extra_request_start_info);
-  }
-}
-
 void PageLoadTracker::OnLoadedResource(
     const ExtraRequestCompleteInfo& extra_request_complete_info) {
   for (const auto& observer : observers_) {
diff --git a/chrome/browser/page_load_metrics/page_load_tracker.h b/chrome/browser/page_load_metrics/page_load_tracker.h
index 4a849c77..f8185b0 100644
--- a/chrome/browser/page_load_metrics/page_load_tracker.h
+++ b/chrome/browser/page_load_metrics/page_load_tracker.h
@@ -191,9 +191,6 @@
 
   void NotifyClientRedirectTo(const PageLoadTracker& destination);
 
-  void OnStartedResource(
-      const ExtraRequestStartInfo& extra_request_started_info);
-
   void OnLoadedResource(
       const ExtraRequestCompleteInfo& extra_request_complete_info);
 
diff --git a/chrome/browser/resources/offline_pages/offline_internals.css b/chrome/browser/resources/offline_pages/offline_internals.css
index b37a1dc9b..534ae985 100644
--- a/chrome/browser/resources/offline_pages/offline_internals.css
+++ b/chrome/browser/resources/offline_pages/offline_internals.css
@@ -50,3 +50,8 @@
 #current-status {
   font-size: 15px;
 }
+
+.dump {
+  font-family: monospace;
+  white-space: pre-wrap;
+}
diff --git a/chrome/browser/resources/offline_pages/offline_internals.html b/chrome/browser/resources/offline_pages/offline_internals.html
index e2d5355..a90859c 100644
--- a/chrome/browser/resources/offline_pages/offline_internals.html
+++ b/chrome/browser/resources/offline_pages/offline_internals.html
@@ -60,7 +60,7 @@
       </thead>
       <tbody id="stored-pages"> </tbody>
     </table>
-    <div id="page-actions-info"></div>
+    <div id="page-actions-info" class="dump"></div>
 
     <h2>Request Queue</h2>
     <div>
@@ -78,7 +78,7 @@
       </thead>
       <tbody id="request-queue"> </tbody>
     </table>
-    <div id="request-queue-actions-info"></div>
+    <div id="request-queue-actions-info" class="dump"></div>
     <input id="url" type="url"
         placeholder="http://www.url1.com, http://www.url2.com, ...">
     <button id="add-to-queue">Load in background</button>
@@ -90,6 +90,17 @@
         <button id="schedule-nwake">Schedule NWake</button>
         <button id="cancel-nwake">Cancel NWake</button>
       </div>
+      <div>
+        <input id="generate-urls" type="text"
+            placeholder="http://www.url1.com, http://www.url2.com, ...">
+        <button id="generate-page-bundle">Generate Page Bundle</button>
+      </div>
+      <div>
+        <input id="operation-name" type="text"
+            placeholder="operations/1234-5678">
+        <button id="get-operation">Get Operation</button>
+      </div>
     </div>
+    <div id="prefetch-actions-info" class="dump"></div>
   </body>
 </html>
diff --git a/chrome/browser/resources/offline_pages/offline_internals.js b/chrome/browser/resources/offline_pages/offline_internals.js
index b01b0a322..0f03bcf 100644
--- a/chrome/browser/resources/offline_pages/offline_internals.js
+++ b/chrome/browser/resources/offline_pages/offline_internals.js
@@ -178,6 +178,14 @@
     browserProxy.getRequestQueue().then(fillRequestQueue);
   }
 
+   /**
+   * Callback for prefetch actions.
+   * @param {string} info The result of performing the prefetch actions.
+   */
+  function setPrefetchResult(info) {
+    $('prefetch-actions-info').textContent = info;
+  }
+
   /**
    * Downloads all the stored page and request queue information into a file.
    * TODO(chili): Create a CSV writer that can abstract out the line joining.
@@ -317,10 +325,18 @@
       }
     };
     $('schedule-nwake').onclick = function() {
-      browserProxy.scheduleNwake();
+      browserProxy.scheduleNwake().then(setPrefetchResult);
     };
     $('cancel-nwake').onclick = function() {
-      browserProxy.cancelNwake();
+      browserProxy.cancelNwake().then(setPrefetchResult);
+    };
+    $('generate-page-bundle').onclick = function() {
+      browserProxy.generatePageBundle($('generate-urls').value).
+          then(setPrefetchResult);
+    };
+    $('get-operation').onclick = function() {
+      browserProxy.getOperation($('operation-name').value).
+          then(setPrefetchResult);
     };
     if (!incognito)
       refreshAll();
diff --git a/chrome/browser/resources/offline_pages/offline_internals_browser_proxy.js b/chrome/browser/resources/offline_pages/offline_internals_browser_proxy.js
index 6e1ad1f..4066134 100644
--- a/chrome/browser/resources/offline_pages/offline_internals_browser_proxy.js
+++ b/chrome/browser/resources/offline_pages/offline_internals_browser_proxy.js
@@ -131,6 +131,20 @@
      * @return {!Promise} A promise firing when the task has been cancelled.
      */
     cancelNwake: function() {},
+
+    /**
+     * Sends and processes a request to generate page bundle.
+     * @param {string} urls A list of comma-separated URLs.
+     * @return {!Promise<string>} A string describing the result.
+     */
+    generatePageBundle: function(urls) {},
+
+    /**
+     * Sends and processes a request to get operation.
+     * @param {string} name Name of operation.
+     * @return {!Promise<string>} A string describing the result.
+     */
+    getOperation: function(name) {},
   };
 
   /**
@@ -205,6 +219,16 @@
     cancelNwake: function() {
       return cr.sendWithPromise('cancelNwake');
     },
+
+    /** @override */
+    generatePageBundle: function(urls) {
+      return cr.sendWithPromise('generatePageBundle', urls);
+    },
+
+    /** @override */
+    getOperation: function(name) {
+      return cr.sendWithPromise('getOperation', name);
+    },
   };
 
   return {
diff --git a/chrome/browser/resources/print_preview/data/destination_store.js b/chrome/browser/resources/print_preview/data/destination_store.js
index 7aa5974e..63d8b7a1 100644
--- a/chrome/browser/resources/print_preview/data/destination_store.js
+++ b/chrome/browser/resources/print_preview/data/destination_store.js
@@ -673,7 +673,11 @@
 
       if (origin == print_preview.DestinationOrigin.LOCAL ||
           origin == print_preview.DestinationOrigin.CROS) {
-        this.nativeLayer_.startGetLocalDestinationCapabilities(id);
+        this.nativeLayer_.getPrinterCapabilities(id).then(
+            this.onLocalDestinationCapabilitiesSet_.bind(this),
+            this.onGetCapabilitiesFail_.bind(this,
+                /** @type {print_preview.DestinationOrigin} */ (origin),
+                id));
         return true;
       }
 
@@ -959,17 +963,26 @@
       // Notify about selected destination change.
       cr.dispatchSimpleEvent(
           this, DestinationStore.EventType.DESTINATION_SELECT);
-      // Request destination capabilities, of not known yet.
+      // Request destination capabilities from backend, since they are not
+      // known yet.
       if (destination.capabilities == null) {
         if (destination.isPrivet) {
-          this.nativeLayer_.startGetPrivetDestinationCapabilities(
-              destination.id);
+          this.nativeLayer_.getPrivetPrinterCapabilities(destination.id).then(
+              this.onPrivetCapabilitiesSet_.bind(this),
+              this.onGetCapabilitiesFail_.bind(this, destination.origin,
+                                               destination.id));
         } else if (destination.isExtension) {
-          this.nativeLayer_.startGetExtensionDestinationCapabilities(
-              destination.id);
+          this.nativeLayer_.getExtensionPrinterCapabilities(destination.id)
+              .then(
+                  this.onExtensionCapabilitiesSet_.bind(this, destination.id),
+                  this.onGetCapabilitiesFail_.bind(this, destination.origin,
+                                                   destination.id)
+              );
         } else if (destination.isLocal) {
-          this.nativeLayer_.startGetLocalDestinationCapabilities(
-              destination.id);
+          this.nativeLayer_.getPrinterCapabilities(destination.id).then(
+              this.onLocalDestinationCapabilitiesSet_.bind(this),
+              this.onGetCapabilitiesFail_.bind(this, destination.origin,
+                                               destination.id));
         } else {
           assert(this.cloudPrintInterface_ != null,
                  'Cloud destination selected, but GCP is not enabled');
@@ -1337,26 +1350,10 @@
       var nativeLayerEventTarget = this.nativeLayer_.getEventTarget();
       this.tracker_.add(
           nativeLayerEventTarget,
-          print_preview.NativeLayer.EventType.CAPABILITIES_SET,
-          this.onLocalDestinationCapabilitiesSet_.bind(this));
-      this.tracker_.add(
-          nativeLayerEventTarget,
-          print_preview.NativeLayer.EventType.GET_CAPABILITIES_FAIL,
-          this.onGetCapabilitiesFail_.bind(this));
-      this.tracker_.add(
-          nativeLayerEventTarget,
           print_preview.NativeLayer.EventType.DESTINATIONS_RELOAD,
           this.onDestinationsReload_.bind(this));
       this.tracker_.add(
           nativeLayerEventTarget,
-          print_preview.NativeLayer.EventType.PRIVET_CAPABILITIES_SET,
-          this.onPrivetCapabilitiesSet_.bind(this));
-      this.tracker_.add(
-          nativeLayerEventTarget,
-          print_preview.NativeLayer.EventType.EXTENSION_CAPABILITIES_SET,
-          this.onExtensionCapabilitiesSet_.bind(this));
-      this.tracker_.add(
-          nativeLayerEventTarget,
           print_preview.NativeLayer.EventType.PROVISIONAL_DESTINATION_RESOLVED,
           this.handleProvisionalDestinationResolved_.bind(this));
     },
@@ -1419,14 +1416,14 @@
      * local destination. Updates the destination with new capabilities if the
      * destination already exists, otherwise it creates a new destination and
      * then updates its capabilities.
-     * @param {Event} event Contains the capabilities of the local print
-     *     destination.
+     * @param {print_preview.PrinterCapabilitiesResponse} settingsInfo Contains
+     *     information about and capabilities of the local print destination.
      * @private
      */
-    onLocalDestinationCapabilitiesSet_: function(event) {
-      var destinationId = event.settingsInfo['printerId'];
-      var printerName = event.settingsInfo['printerName'];
-      var printerDescription = event.settingsInfo['printerDescription'];
+    onLocalDestinationCapabilitiesSet_: function(settingsInfo) {
+      var destinationId = settingsInfo['printerId'];
+      var printerName = settingsInfo['printerName'];
+      var printerDescription = settingsInfo['printerDescription'];
       // PDF is special since we don't need to query the device for
       // capabilities.
       var origin = destinationId ==
@@ -1438,7 +1435,7 @@
           '');
       var destination = this.destinationMap_[key];
       var capabilities = DestinationStore.localizeCapabilities_(
-          event.settingsInfo.capabilities);
+          settingsInfo.capabilities);
       // Special case for PDF printer (until local printers capabilities are
       // reported in CDD format too).
       if (destinationId ==
@@ -1455,7 +1452,7 @@
           }
           destination.capabilities = capabilities;
         } else {
-          var isEnterprisePrinter = event.settingsInfo['cupsEnterprisePrinter'];
+          var isEnterprisePrinter = settingsInfo['cupsEnterprisePrinter'];
           destination = print_preview.LocalDestinationParser.parse(
               {deviceName: destinationId,
                printerName: printerName,
@@ -1477,15 +1474,17 @@
      * Called when a request to get a local destination's print capabilities
      * fails. If the destination is the initial destination, auto-select another
      * destination instead.
-     * @param {Event} event Contains the destination ID that failed.
+     * @param {print_preview.DestinationOrigin} origin The origin type of the
+     *     failed destination.
+     * @param {string} destinationId The destination ID that failed.
      * @private
      */
-    onGetCapabilitiesFail_: function(event) {
+    onGetCapabilitiesFail_: function(origin, destinationId) {
       console.warn('Failed to get print capabilities for printer ' +
-                    event.destinationId);
+                   destinationId);
       if (this.autoSelectMatchingDestination_ &&
           this.autoSelectMatchingDestination_.matchIdAndOrigin(
-              event.destinationId, event.destinationOrigin)) {
+              destinationId, origin)) {
         this.selectDefaultDestination_();
       }
     },
@@ -1575,14 +1574,15 @@
 
     /**
      * Called when capabilities for a privet printer are set.
-     * @param {Object} event Contains the capabilities and printer ID.
+     * @param {!print_preview.PrivetPrinterCapabilitiesResponse} printerInfo
+     *     Contains the privet printer's description and capabilities.
      * @private
      */
-    onPrivetCapabilitiesSet_: function(event) {
+    onPrivetCapabilitiesSet_: function(printerInfo) {
       var destinations =
-          print_preview.PrivetDestinationParser.parse(event.printer);
+          print_preview.PrivetDestinationParser.parse(printerInfo.printer);
       destinations.forEach(function(dest) {
-        dest.capabilities = event.capabilities;
+        dest.capabilities = printerInfo.capabilities;
         this.updateDestination_(dest);
       }, this);
     },
@@ -1617,18 +1617,19 @@
 
     /**
      * Called when capabilities for an extension managed printer are set.
-     * @param {Object} event Contains the printer's capabilities and ID.
+     * @param {string} printerId The printer Id.
+     * @param {!print_preview.Cdd} capabilities The printer's capabilities.
      * @private
      */
-    onExtensionCapabilitiesSet_: function(event) {
+    onExtensionCapabilitiesSet_: function(printerId, capabilities) {
       var destinationKey = this.getDestinationKey_(
           print_preview.DestinationOrigin.EXTENSION,
-          event.printerId,
+          printerId,
           '' /* account */);
       var destination = this.destinationMap_[destinationKey];
       if (!destination)
         return;
-      destination.capabilities = event.capabilities;
+      destination.capabilities = capabilities;
       this.updateDestination_(destination);
     },
 
diff --git a/chrome/browser/resources/print_preview/data/local_parsers.js b/chrome/browser/resources/print_preview/data/local_parsers.js
index 97cceab..cb34a4a 100644
--- a/chrome/browser/resources/print_preview/data/local_parsers.js
+++ b/chrome/browser/resources/print_preview/data/local_parsers.js
@@ -38,7 +38,8 @@
 
   /**
    * Parses a privet destination as one or more local printers.
-   * @param {!Object} destinationInfo Object that describes a privet printer.
+   * @param {!print_preview.PrivetPrinterDescription} destinationInfo Object
+   *     that describes a privet printer.
    * @return {!Array<!print_preview.Destination>} Parsed destination info.
    */
   PrivetDestinationParser.parse = function(destinationInfo) {
diff --git a/chrome/browser/resources/print_preview/native_layer.js b/chrome/browser/resources/print_preview/native_layer.js
index f259e5e..f4e6984 100644
--- a/chrome/browser/resources/print_preview/native_layer.js
+++ b/chrome/browser/resources/print_preview/native_layer.js
@@ -29,6 +29,37 @@
 /**
  * @typedef {{
  *   printerId: string,
+ *   printerName: string,
+ *   printerDescription: string,
+ *   cupsEnterprisePrinter: (boolean | undefined),
+ *   capabilities: !print_preview.Cdd,
+ * }}
+ */
+print_preview.PrinterCapabilitiesResponse;
+
+/**
+ * @typedef {{
+ *   serviceName: string,
+ *   name: string,
+ *   hasLocalPrinting: boolean,
+ *   isUnregistered: boolean,
+ *   cloudID: string,
+ * }}
+ * @see PrintPreviewHandler::FillPrinterDescription in print_preview_handler.cc
+ */
+print_preview.PrivetPrinterDescription;
+
+/**
+ * @typedef {{
+ *   printer: !print_preview.PrivetPrinterDescription,
+ *   capabilities: !print_preview.Cdd,
+ * }}
+ */
+print_preview.PrivetPrinterCapabilitiesResponse;
+
+/**
+ * @typedef {{
+ *   printerId: string,
  *   success: boolean,
  *   capabilities: Object,
  * }}
@@ -45,14 +76,6 @@
   function NativeLayer() {
     // Bind global handlers
     global.setUseCloudPrint = this.onSetUseCloudPrint_.bind(this);
-    global.updateWithPrinterCapabilities =
-        this.onUpdateWithPrinterCapabilities_.bind(this);
-    global.failedToGetPrinterCapabilities =
-        this.onFailedToGetPrinterCapabilities_.bind(this);
-    global.failedToGetPrivetPrinterCapabilities =
-      this.onFailedToGetPrivetPrinterCapabilities_.bind(this);
-    global.failedToGetExtensionPrinterCapabilities =
-        this.onFailedToGetExtensionPrinterCapabilities_.bind(this);
     global.reloadPrintersList = this.onReloadPrintersList_.bind(this);
     global.printToCloud = this.onPrintToCloud_.bind(this);
     global.fileSelectionCancelled =
@@ -69,11 +92,7 @@
     global.onDidPreviewPage = this.onDidPreviewPage_.bind(this);
     global.updatePrintPreview = this.onUpdatePrintPreview_.bind(this);
     global.onDidGetAccessToken = this.onDidGetAccessToken_.bind(this);
-    global.onPrivetCapabilitiesSet =
-        this.onPrivetCapabilitiesSet_.bind(this);
     global.onPrivetPrintFailed = this.onPrivetPrintFailed_.bind(this);
-    global.onExtensionCapabilitiesSet =
-        this.onExtensionCapabilitiesSet_.bind(this);
     global.onEnableManipulateSettingsForTest =
         this.onEnableManipulateSettingsForTest_.bind(this);
     global.printPresetOptionsFromDocument =
@@ -115,14 +134,12 @@
    */
   NativeLayer.EventType = {
     ACCESS_TOKEN_READY: 'print_preview.NativeLayer.ACCESS_TOKEN_READY',
-    CAPABILITIES_SET: 'print_preview.NativeLayer.CAPABILITIES_SET',
     CLOUD_PRINT_ENABLE: 'print_preview.NativeLayer.CLOUD_PRINT_ENABLE',
     DESTINATIONS_RELOAD: 'print_preview.NativeLayer.DESTINATIONS_RELOAD',
     DISABLE_SCALING: 'print_preview.NativeLayer.DISABLE_SCALING',
     FILE_SELECTION_CANCEL: 'print_preview.NativeLayer.FILE_SELECTION_CANCEL',
     FILE_SELECTION_COMPLETE:
         'print_preview.NativeLayer.FILE_SELECTION_COMPLETE',
-    GET_CAPABILITIES_FAIL: 'print_preview.NativeLayer.GET_CAPABILITIES_FAIL',
     MANIPULATE_SETTINGS_FOR_TEST:
         'print_preview.NativeLayer.MANIPULATE_SETTINGS_FOR_TEST',
     PAGE_COUNT_READY: 'print_preview.NativeLayer.PAGE_COUNT_READY',
@@ -134,12 +151,7 @@
         'print_preview.NativeLayer.PREVIEW_GENERATION_FAIL',
     PRINT_TO_CLOUD: 'print_preview.NativeLayer.PRINT_TO_CLOUD',
     SETTINGS_INVALID: 'print_preview.NativeLayer.SETTINGS_INVALID',
-    PRIVET_PRINTER_CHANGED: 'print_preview.NativeLayer.PRIVET_PRINTER_CHANGED',
-    PRIVET_CAPABILITIES_SET:
-        'print_preview.NativeLayer.PRIVET_CAPABILITIES_SET',
     PRIVET_PRINT_FAILED: 'print_preview.NativeLayer.PRIVET_PRINT_FAILED',
-    EXTENSION_CAPABILITIES_SET:
-        'print_preview.NativeLayer.EXTENSION_CAPABILITIES_SET',
     PRINT_PRESET_OPTIONS: 'print_preview.NativeLayer.PRINT_PRESET_OPTIONS',
     PROVISIONAL_DESTINATION_RESOLVED:
         'print_preview.NativeLayer.PROVISIONAL_DESTINATION_RESOLVED'
@@ -241,15 +253,6 @@
     },
 
     /**
-     * Requests the privet destination's printing capabilities. A
-     * PRIVET_CAPABILITIES_SET event will be dispatched in response.
-     * @param {string} destinationId ID of the destination.
-     */
-    startGetPrivetDestinationCapabilities: function(destinationId) {
-      chrome.send('getPrivetPrinterCapabilities', [destinationId]);
-    },
-
-    /**
      * Request a list of extension printers. Printers are reported as they are
      * found by a series of 'extension-printers-added' events.
      * @return {!Promise} Will be resolved when all extension managed printers
@@ -260,22 +263,37 @@
     },
 
     /**
-     * Requests an extension destination's printing capabilities. A
-     * EXTENSION_CAPABILITIES_SET event will be dispatched in response.
-     * @param {string} destinationId The ID of the destination whose
-     *     capabilities are requested.
+     * Requests the destination's printing capabilities. Returns a promise that
+     * will be resolved with the capabilities if they are obtained successfully.
+     * @param {string} destinationId ID of the destination.
+     * @return {!Promise<!print_preview.PrinterCapabilitiesResponse>}
      */
-    startGetExtensionDestinationCapabilities: function(destinationId) {
-      chrome.send('getExtensionPrinterCapabilities', [destinationId]);
+    getPrinterCapabilities: function(destinationId) {
+      return cr.sendWithPromise('getPrinterCapabilities', destinationId);
     },
 
     /**
-     * Requests the destination's printing capabilities. A CAPABILITIES_SET
-     * event will be dispatched in response.
+     * Requests the privet destination's printing capabilities. Returns a
+     * promise that will be resolved with capabilities and printer information
+     * if capabilities are obtained successfully.
      * @param {string} destinationId ID of the destination.
+     * @return {!Promise<!print_preview.PrivetPrinterCapabilitiesResponse>}
      */
-    startGetLocalDestinationCapabilities: function(destinationId) {
-      chrome.send('getPrinterCapabilities', [destinationId]);
+    getPrivetPrinterCapabilities: function(destinationId) {
+      return cr.sendWithPromise('getPrivetPrinterCapabilities', destinationId);
+    },
+
+    /**
+     * Requests the extension destination's printing capabilities. Returns a
+     * promise that will be resolved with the ID and capabilities if
+     * capabilities are obtained successfully.
+     * @param {string} destinationId The ID of the destination whose
+     *     capabilities are requested.
+     * @return {!Promise<!print_preview.Cdd>}
+     */
+    getExtensionPrinterCapabilities: function(destinationId) {
+      return cr.sendWithPromise('getExtensionPrinterCapabilities',
+                                destinationId);
     },
 
     /**
@@ -549,65 +567,6 @@
       this.eventTarget_.dispatchEvent(cloudPrintEnableEvent);
     },
 
-    /**
-     * Called when native layer gets settings information for a requested local
-     * destination.
-     * @param {Object} settingsInfo printer setting information.
-     * @private
-     */
-    onUpdateWithPrinterCapabilities_: function(settingsInfo) {
-      assert(settingsInfo.capabilities,
-          'Capabilities update without capabilites');
-      var capsSetEvent = new Event(NativeLayer.EventType.CAPABILITIES_SET);
-      capsSetEvent.settingsInfo = settingsInfo;
-      this.eventTarget_.dispatchEvent(capsSetEvent);
-    },
-
-    /**
-     * Called when native layer gets settings information for a requested local
-     * destination.
-     * @param {string} destinationId Printer affected by error.
-     * @private
-     */
-    onFailedToGetPrinterCapabilities_: function(destinationId) {
-      var getCapsFailEvent = new Event(
-          NativeLayer.EventType.GET_CAPABILITIES_FAIL);
-      getCapsFailEvent.destinationId = destinationId;
-      getCapsFailEvent.destinationOrigin =
-          print_preview.DestinationOrigin.LOCAL;
-      this.eventTarget_.dispatchEvent(getCapsFailEvent);
-    },
-
-    /**
-     * Called when native layer gets settings information for a requested privet
-     * destination.
-     * @param {string} destinationId Printer affected by error.
-     * @private
-     */
-    onFailedToGetPrivetPrinterCapabilities_: function(destinationId) {
-      var getCapsFailEvent = new Event(
-          NativeLayer.EventType.GET_CAPABILITIES_FAIL);
-      getCapsFailEvent.destinationId = destinationId;
-      getCapsFailEvent.destinationOrigin =
-          print_preview.DestinationOrigin.PRIVET;
-      this.eventTarget_.dispatchEvent(getCapsFailEvent);
-    },
-
-    /**
-     * Called when native layer fails to get settings information for a
-     * requested extension destination.
-     * @param {string} destinationId Printer affected by error.
-     * @private
-     */
-    onFailedToGetExtensionPrinterCapabilities_: function(destinationId) {
-      var getCapsFailEvent = new Event(
-          NativeLayer.EventType.GET_CAPABILITIES_FAIL);
-      getCapsFailEvent.destinationId = destinationId;
-      getCapsFailEvent.destinationOrigin =
-          print_preview.DestinationOrigin.EXTENSION;
-      this.eventTarget_.dispatchEvent(getCapsFailEvent);
-    },
-
     /** Reloads the printer list. */
     onReloadPrintersList_: function() {
       cr.dispatchSimpleEvent(this.eventTarget_,
@@ -774,19 +733,6 @@
     },
 
     /**
-     * @param {Object} printer Specifies information about the printer that was
-     *    added.
-     * @private
-     */
-    onPrivetCapabilitiesSet_: function(printer, capabilities) {
-      var privetCapabilitiesSetEvent =
-            new Event(NativeLayer.EventType.PRIVET_CAPABILITIES_SET);
-      privetCapabilitiesSetEvent.printer = printer;
-      privetCapabilitiesSetEvent.capabilities = capabilities;
-      this.eventTarget_.dispatchEvent(privetCapabilitiesSetEvent);
-    },
-
-    /**
      * @param {string} http_error The HTTP response code or -1 if not an HTTP
      *    error.
      * @private
@@ -799,20 +745,6 @@
     },
 
     /**
-     * Called when an extension responds to a request for an extension printer
-     * capabilities.
-     * @param {string} printerId The printer's ID.
-     * @param {!Object} capabilities The reported printer capabilities.
-     */
-    onExtensionCapabilitiesSet_: function(printerId,
-                                          capabilities) {
-      var event = new Event(NativeLayer.EventType.EXTENSION_CAPABILITIES_SET);
-      event.printerId = printerId;
-      event.capabilities = capabilities;
-      this.eventTarget_.dispatchEvent(event);
-    },
-
-    /**
      * Called when Chrome reports that attempt to resolve a provisional
      * destination failed.
      * @param {string} destinationId The provisional destination ID.
diff --git a/chrome/browser/resources/settings/people_page/compiled_resources2.gyp b/chrome/browser/resources/settings/people_page/compiled_resources2.gyp
index cc2a935..246c892 100644
--- a/chrome/browser/resources/settings/people_page/compiled_resources2.gyp
+++ b/chrome/browser/resources/settings/people_page/compiled_resources2.gyp
@@ -205,6 +205,7 @@
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior',
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:util',
         'sync_browser_proxy',
       ],
       'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
diff --git a/chrome/browser/resources/settings/people_page/sync_page.html b/chrome/browser/resources/settings/people_page/sync_page.html
index 262894d..9d6fba8 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.html
+++ b/chrome/browser/resources/settings/people_page/sync_page.html
@@ -1,6 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/html/util.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
diff --git a/chrome/browser/resources/settings/people_page/sync_page.js b/chrome/browser/resources/settings/people_page/sync_page.js
index 8078686..f404436 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.js
+++ b/chrome/browser/resources/settings/people_page/sync_page.js
@@ -216,8 +216,8 @@
 
     // Focus the password input box if password is needed to start sync.
     if (this.syncPrefs.passphraseRequired) {
-      // Async to allow the dom-if templates to render first.
-      this.async(function() {
+      // Wait for the dom-if templates to render and subpage to become visible.
+      listenOnce(document, 'show-container', function() {
         var input = /** @type {!PaperInputElement} */ (
             this.$$('#existingPassphraseInput'));
         input.inputElement.focus();
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc
index bf6795ad..ae8d200b 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service.cc
@@ -77,29 +77,6 @@
 
 namespace safe_browsing {
 
-namespace {
-
-// The default URL prefix where browser fetches chunk updates, hashes,
-// and reports safe browsing hits and malware details.
-const char kSbDefaultURLPrefix[] =
-    "https://safebrowsing.google.com/safebrowsing";
-
-// The backup URL prefix used when there are issues establishing a connection
-// with the server at the primary URL.
-const char kSbBackupConnectErrorURLPrefix[] =
-    "https://alt1-safebrowsing.google.com/safebrowsing";
-
-// The backup URL prefix used when there are HTTP-specific issues with the
-// server at the primary URL.
-const char kSbBackupHttpErrorURLPrefix[] =
-    "https://alt2-safebrowsing.google.com/safebrowsing";
-
-// The backup URL prefix used when there are local network specific issues.
-const char kSbBackupNetworkErrorURLPrefix[] =
-    "https://alt3-safebrowsing.google.com/safebrowsing";
-
-}  // namespace
-
 // static
 SafeBrowsingServiceFactory* SafeBrowsingService::factory_ = NULL;
 
diff --git a/chrome/browser/ssl/security_state_tab_helper.cc b/chrome/browser/ssl/security_state_tab_helper.cc
index 4313182..c4286f5 100644
--- a/chrome/browser/ssl/security_state_tab_helper.cc
+++ b/chrome/browser/ssl/security_state_tab_helper.cc
@@ -15,6 +15,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/security_state/content/content_utils.h"
 #include "components/ssl_config/ssl_config_prefs.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
@@ -39,7 +40,14 @@
 SecurityStateTabHelper::SecurityStateTabHelper(
     content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents),
-      logged_http_warning_on_current_navigation_(false) {}
+      logged_http_warning_on_current_navigation_(false),
+      is_incognito_(false) {
+  content::BrowserContext* context = web_contents->GetBrowserContext();
+  if (context->IsOffTheRecord() &&
+      !Profile::FromBrowserContext(context)->IsGuestSession()) {
+    is_incognito_ = true;
+  }
+}
 
 SecurityStateTabHelper::~SecurityStateTabHelper() {}
 
@@ -113,12 +121,24 @@
 
 void SecurityStateTabHelper::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
-  if (navigation_handle->IsInMainFrame() &&
-      !navigation_handle->IsSameDocument()) {
-    // Only reset the console message flag for main-frame navigations,
-    // and not for same-document navigations like reference fragments and
-    // pushState.
-    logged_http_warning_on_current_navigation_ = false;
+  // Ignore subframe navigations, same-document navigations, and navigations
+  // that did not commit (e.g. HTTP/204 or file downloads).
+  if (!navigation_handle->IsInMainFrame() ||
+      navigation_handle->IsSameDocument() ||
+      !navigation_handle->HasCommitted()) {
+    return;
+  }
+
+  logged_http_warning_on_current_navigation_ = false;
+
+  security_state::SecurityInfo security_info;
+  GetSecurityInfo(&security_info);
+  if (security_info.incognito_downgraded_security_level) {
+    web_contents()->GetMainFrame()->AddMessageToConsole(
+        content::CONSOLE_MESSAGE_LEVEL_WARNING,
+        "This page was loaded non-securely in an incognito mode browser. A "
+        "warning has been added to the URL bar. For more information, see "
+        "https://goo.gl/y8SRRv.");
   }
 }
 
@@ -199,5 +219,7 @@
   // information is still being initialized, thus no need to check for that.
   state->malicious_content_status = GetMaliciousContentStatus();
 
+  state->is_incognito = is_incognito_;
+
   return state;
 }
diff --git a/chrome/browser/ssl/security_state_tab_helper.h b/chrome/browser/ssl/security_state_tab_helper.h
index f44e7f8..a15167b 100644
--- a/chrome/browser/ssl/security_state_tab_helper.h
+++ b/chrome/browser/ssl/security_state_tab_helper.h
@@ -50,9 +50,9 @@
   std::unique_ptr<security_state::VisibleSecurityState>
   GetVisibleSecurityState() const;
 
-  // True if a console message has been logged about an omnibox warning that
-  // will be shown in future versions of Chrome for insecure HTTP pages. This
-  // message should only be logged once per main-frame navigation.
+  // True if a console message has been logged about an omnibox warning shown
+  // when sensitive input fields are shown on insecure HTTP pages. This message
+  // should only be logged once per main-frame navigation.
   bool logged_http_warning_on_current_navigation_;
 
   // The time that a console or omnibox warning was shown for insecure
@@ -62,6 +62,9 @@
   // histogramming.
   base::Time time_of_http_warning_on_current_navigation_;
 
+  // True if this browser tab is in non-guest Incognito mode.
+  bool is_incognito_;
+
   DISALLOW_COPY_AND_ASSIGN(SecurityStateTabHelper);
 };
 
diff --git a/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc b/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc
index 07981b7..cf5a8ac 100644
--- a/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_browser_tests.cc
@@ -9,11 +9,16 @@
 #include "base/macros.h"
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_command_line.h"
 #include "base/threading/sequenced_worker_pool.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/profiles/profile_window.h"
 #include "chrome/browser/ssl/cert_verifier_browser_test.h"
 #include "chrome/browser/ssl/ssl_blocking_page.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
@@ -372,6 +377,21 @@
   DISALLOW_COPY_AND_ASSIGN(SecurityStateTabHelperTest);
 };
 
+// Same as SecurityStateTabHelperTest, but with Incognito enabled.
+class SecurityStateTabHelperIncognitoTest : public SecurityStateTabHelperTest {
+ public:
+  SecurityStateTabHelperIncognitoTest() : SecurityStateTabHelperTest() {}
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    SecurityStateTabHelperTest::SetUpCommandLine(command_line);
+    // Test should run Incognito.
+    command_line->AppendSwitch(switches::kIncognito);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SecurityStateTabHelperIncognitoTest);
+};
+
 class DidChangeVisibleSecurityStateTest : public InProcessBrowserTest {
  public:
   DidChangeVisibleSecurityStateTest()
@@ -933,6 +953,7 @@
                    embedded_test_server()->GetURL("/title1.html").host()));
   }
 
+ private:
   DISALLOW_COPY_AND_ASSIGN(SecurityStateLoadingTest);
 };
 
@@ -983,6 +1004,17 @@
   ASSERT_TRUE(entry);
   EXPECT_TRUE(entry->GetSSL().content_status &
               content::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP);
+
+  {
+    // Ensure the warning is still present when HTTPBad Phase 2 flag is enabled.
+    base::test::ScopedCommandLine scoped_command_line;
+    scoped_command_line.GetProcessCommandLine()->AppendSwitchASCII(
+        security_state::switches::kMarkHttpAs,
+        security_state::switches::kMarkHttpAsNonSecureWhileIncognito);
+
+    helper->GetSecurityInfo(&security_info);
+    EXPECT_EQ(security_state::HTTP_SHOW_WARNING, security_info.security_level);
+  }
 }
 
 // Tests that when a visible password field is detected on a blob URL, the
@@ -1243,7 +1275,7 @@
   delegate->tab_strip_model()->ActivateTabAt(index, true);
   ASSERT_EQ(contents, delegate->tab_strip_model()->GetActiveWebContents());
 
-  // Navigate to an HTTP page. Use a non-local hostname so that is it
+  // Navigate to an HTTP page. Use a non-local hostname so that it is
   // not considered secure.
   GURL http_url =
       GetURLWithNonLocalHostname(embedded_test_server(), "/title1.html");
@@ -1311,7 +1343,7 @@
   delegate->tab_strip_model()->ActivateTabAt(index, true);
   ASSERT_EQ(contents, delegate->tab_strip_model()->GetActiveWebContents());
 
-  // Navigate to an HTTP page. Use a non-local hostname so that is it
+  // Navigate to an HTTP page. Use a non-local hostname so that it is
   // not considered secure.
   GURL http_url = GetURLWithNonLocalHostname(embedded_test_server(),
                                              "/ssl/page_with_frame.html");
@@ -1352,6 +1384,7 @@
       contents, "document.getElementById('navFrame').src = '/title2.html';"));
   subframe_observer.Wait();
   contents->OnCreditCardInputShownOnHttp();
+  helper->GetSecurityInfo(&security_info);
   EXPECT_EQ(security_state::HTTP_SHOW_WARNING, security_info.security_level);
 
   // Do a main frame navigation and then trigger HTTP_SHOW_WARNING
@@ -1392,7 +1425,7 @@
   delegate->tab_strip_model()->ActivateTabAt(index, true);
   ASSERT_EQ(contents, delegate->tab_strip_model()->GetActiveWebContents());
 
-  // Navigate to an HTTP page. Use a non-local hostname so that is it
+  // Navigate to an HTTP page. Use a non-local hostname so that it is
   // not considered secure.
   GURL http_url =
       GetURLWithNonLocalHostname(embedded_test_server(), "/title1.html");
@@ -1428,6 +1461,7 @@
   EXPECT_TRUE(content::ExecuteScript(
       contents, "history.pushState({ foo: 'bar' }, 'foo', 'bar');"));
   contents->OnCreditCardInputShownOnHttp();
+  helper->GetSecurityInfo(&security_info);
   EXPECT_EQ(security_state::HTTP_SHOW_WARNING, security_info.security_level);
 
   // Do a main frame navigation and then trigger HTTP_SHOW_WARNING
@@ -1614,6 +1648,262 @@
   EXPECT_TRUE(observer.latest_explanations().summary.empty());
 }
 
+// Tests that the security level of a HTTP page in Incognito mode is downgraded
+// to HTTP_SHOW_WARNING when MarkHttpAsNonSecureWhileIncognito is enabled.
+IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperIncognitoTest,
+                       SecurityLevelDowngradedForHTTPInIncognito) {
+  // Set the mode using the command line flag rather than the field trial to
+  // ensure that fieldtrial_testing_config.json does not interfere.
+  base::test::ScopedCommandLine scoped_command_line;
+  scoped_command_line.GetProcessCommandLine()->AppendSwitchASCII(
+      security_state::switches::kMarkHttpAs,
+      security_state::switches::kMarkHttpAsNonSecureWhileIncognito);
+
+  ConsoleWebContentsDelegate* delegate = new ConsoleWebContentsDelegate(
+      Browser::CreateParams(browser()->profile(), true));
+  content::WebContents* original_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  content::WebContents* contents =
+      content::WebContents::Create(content::WebContents::CreateParams(
+          original_contents->GetBrowserContext()));
+  ASSERT_TRUE(contents);
+  ASSERT_TRUE(contents->GetBrowserContext()->IsOffTheRecord());
+  contents->SetDelegate(delegate);
+  delegate->tab_strip_model()->AppendWebContents(contents, true);
+  int index = delegate->tab_strip_model()->GetIndexOfWebContents(contents);
+  delegate->tab_strip_model()->ActivateTabAt(index, true);
+  ASSERT_EQ(contents, delegate->tab_strip_model()->GetActiveWebContents());
+
+  SecurityStyleTestObserver observer(contents);
+
+  SecurityStateTabHelper* helper =
+      SecurityStateTabHelper::FromWebContents(contents);
+  ASSERT_TRUE(helper);
+
+  // Navigate to an HTTP page. Use a non-local hostname so that it is
+  // not considered secure.
+  GURL http_url =
+      GetURLWithNonLocalHostname(embedded_test_server(), "/title1.html");
+  ui_test_utils::NavigateToURL(delegate, http_url);
+  content::NavigationEntry* entry = contents->GetController().GetVisibleEntry();
+  ASSERT_TRUE(entry);
+  EXPECT_EQ(http_url, entry->GetURL());
+
+  security_state::SecurityInfo security_info;
+  helper->GetSecurityInfo(&security_info);
+  EXPECT_TRUE(security_info.incognito_downgraded_security_level);
+  EXPECT_EQ(security_state::HTTP_SHOW_WARNING, security_info.security_level);
+  EXPECT_EQ(1u, observer.latest_explanations().neutral_explanations.size());
+  EXPECT_EQ(blink::kWebSecurityStyleNeutral, observer.latest_security_style());
+
+  // Check that the expected console message is present.
+  ASSERT_NO_FATAL_FAILURE(CheckForOneHttpWarningConsoleMessage(delegate));
+
+  // Ensure that same-page pushstate does not add another notice.
+  EXPECT_TRUE(content::ExecuteScript(
+      contents, "history.pushState({ foo: 'bar' }, 'foo', 'bar');"));
+  EXPECT_EQ(1u, observer.latest_explanations().neutral_explanations.size());
+  EXPECT_EQ(blink::kWebSecurityStyleNeutral, observer.latest_security_style());
+  // Check that no additional console message is present.
+  ASSERT_NO_FATAL_FAILURE(CheckForOneHttpWarningConsoleMessage(delegate));
+}
+
+// Tests that additional HTTP_SHOW_WARNING console messages are not
+// printed after aborted navigations.
+IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperIncognitoTest,
+                       ConsoleMessageNotPrintedForAbortedNavigation) {
+  // Set the mode using the command line flag rather than the field trial to
+  // ensure that fieldtrial_testing_config.json does not interfere.
+  base::test::ScopedCommandLine scoped_command_line;
+  scoped_command_line.GetProcessCommandLine()->AppendSwitchASCII(
+      security_state::switches::kMarkHttpAs,
+      security_state::switches::kMarkHttpAsNonSecureWhileIncognito);
+
+  ConsoleWebContentsDelegate* delegate = new ConsoleWebContentsDelegate(
+      Browser::CreateParams(browser()->profile(), true));
+  content::WebContents* original_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  content::WebContents* contents =
+      content::WebContents::Create(content::WebContents::CreateParams(
+          original_contents->GetBrowserContext()));
+  ASSERT_TRUE(contents);
+  ASSERT_TRUE(contents->GetBrowserContext()->IsOffTheRecord());
+  contents->SetDelegate(delegate);
+  delegate->tab_strip_model()->AppendWebContents(contents, true);
+  int index = delegate->tab_strip_model()->GetIndexOfWebContents(contents);
+  delegate->tab_strip_model()->ActivateTabAt(index, true);
+  ASSERT_EQ(contents, delegate->tab_strip_model()->GetActiveWebContents());
+
+  SecurityStyleTestObserver observer(contents);
+
+  SecurityStateTabHelper* helper =
+      SecurityStateTabHelper::FromWebContents(contents);
+  ASSERT_TRUE(helper);
+
+  // Navigate to an HTTP page. Use a non-local hostname so that it is
+  // not considered secure.
+  GURL http_url =
+      GetURLWithNonLocalHostname(embedded_test_server(), "/title1.html");
+  ui_test_utils::NavigateToURL(delegate, http_url);
+
+  security_state::SecurityInfo security_info;
+  helper->GetSecurityInfo(&security_info);
+  EXPECT_TRUE(security_info.incognito_downgraded_security_level);
+  EXPECT_EQ(security_state::HTTP_SHOW_WARNING, security_info.security_level);
+  EXPECT_EQ(blink::kWebSecurityStyleNeutral, observer.latest_security_style());
+  EXPECT_EQ(1u, observer.latest_explanations().neutral_explanations.size());
+
+  // Check that the expected console message is present.
+  ASSERT_NO_FATAL_FAILURE(CheckForOneHttpWarningConsoleMessage(delegate));
+  delegate->ClearConsoleMessages();
+
+  // Perform a navigation that does not commit.
+  // The embedded test server returns a HTTP/204 only for local URLs, so
+  // we cannot use GetURLWithNonLocalHostname() here.
+  GURL http204_url = embedded_test_server()->GetURL("/nocontent");
+  ui_test_utils::NavigateToURL(delegate, http204_url);
+
+  // No change is expected in the security state.
+  EXPECT_TRUE(security_info.incognito_downgraded_security_level);
+  EXPECT_EQ(security_state::HTTP_SHOW_WARNING, security_info.security_level);
+  EXPECT_EQ(blink::kWebSecurityStyleNeutral, observer.latest_security_style());
+  EXPECT_EQ(1u, observer.latest_explanations().neutral_explanations.size());
+
+  // No additional console logging should occur.
+  EXPECT_TRUE(delegate->console_messages().empty());
+}
+
+// Tests that the security level of a HTTP page in Guest mode is not downgraded
+// to HTTP_SHOW_WARNING when MarkHttpAsNonSecureWhileIncognito is enabled.
+#if defined(OS_CHROMEOS)
+// Guest mode cannot be readily browser-tested on ChromeOS.
+#define MAYBE_SecurityLevelNotDowngradedForHTTPInGuestMode \
+  DISABLED_SecurityLevelNotDowngradedForHTTPInGuestMode
+#else
+#define MAYBE_SecurityLevelNotDowngradedForHTTPInGuestMode \
+  SecurityLevelNotDowngradedForHTTPInGuestMode
+#endif
+IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperTest,
+                       MAYBE_SecurityLevelNotDowngradedForHTTPInGuestMode) {
+  base::test::ScopedCommandLine scoped_command_line;
+  scoped_command_line.GetProcessCommandLine()->AppendSwitchASCII(
+      security_state::switches::kMarkHttpAs,
+      security_state::switches::kMarkHttpAsNonSecureWhileIncognito);
+
+  // Create a new browser in Guest Mode.
+  EXPECT_EQ(1U, BrowserList::GetInstance()->size());
+  content::WindowedNotificationObserver browser_creation_observer(
+      chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+      content::NotificationService::AllSources());
+  profiles::SwitchToGuestProfile(ProfileManager::CreateCallback());
+  browser_creation_observer.Wait();
+  EXPECT_EQ(2U, BrowserList::GetInstance()->size());
+  Profile* guest = g_browser_process->profile_manager()->GetProfileByPath(
+      ProfileManager::GetGuestProfilePath());
+  Browser* guest_browser = chrome::FindAnyBrowser(guest, true);
+  ASSERT_TRUE(guest_browser);
+
+  ConsoleWebContentsDelegate* delegate = new ConsoleWebContentsDelegate(
+      Browser::CreateParams(guest_browser->profile(), true));
+  content::WebContents* original_contents =
+      guest_browser->tab_strip_model()->GetActiveWebContents();
+  content::WebContents* contents =
+      content::WebContents::Create(content::WebContents::CreateParams(
+          original_contents->GetBrowserContext()));
+  ASSERT_TRUE(contents);
+  ASSERT_TRUE(contents->GetBrowserContext()->IsOffTheRecord());
+  contents->SetDelegate(delegate);
+  delegate->tab_strip_model()->AppendWebContents(contents, true);
+  int index = delegate->tab_strip_model()->GetIndexOfWebContents(contents);
+  delegate->tab_strip_model()->ActivateTabAt(index, true);
+  ASSERT_EQ(contents, delegate->tab_strip_model()->GetActiveWebContents());
+
+  SecurityStyleTestObserver observer(contents);
+
+  SecurityStateTabHelper* helper =
+      SecurityStateTabHelper::FromWebContents(contents);
+  ASSERT_TRUE(helper);
+
+  // Navigate to an HTTP page. Use a non-local hostname so that it is
+  // not considered secure.
+  GURL http_url =
+      GetURLWithNonLocalHostname(embedded_test_server(), "/title1.html");
+  ui_test_utils::NavigateToURL(delegate, http_url);
+
+  security_state::SecurityInfo security_info;
+  helper->GetSecurityInfo(&security_info);
+  EXPECT_FALSE(security_info.incognito_downgraded_security_level);
+  EXPECT_EQ(security_state::NONE, security_info.security_level);
+  EXPECT_EQ(0u, observer.latest_explanations().neutral_explanations.size());
+  EXPECT_EQ(blink::kWebSecurityStyleNeutral, observer.latest_security_style());
+
+  // No console notification should occur.
+  EXPECT_TRUE(delegate->console_messages().empty());
+}
+
+// Tests that the security level of a HTTP page is NEUTRAL when MarkHttpAs is
+// not set.
+IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperIncognitoTest,
+                       SecurityLevelNeutralByDefaultForHTTP) {
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(contents);
+
+  ASSERT_TRUE(contents->GetBrowserContext()->IsOffTheRecord());
+
+  SecurityStyleTestObserver observer(contents);
+
+  SecurityStateTabHelper* helper =
+      SecurityStateTabHelper::FromWebContents(contents);
+  ASSERT_TRUE(helper);
+
+  // Navigate to an HTTP page. Use a non-local hostname so that it is
+  // not considered secure.
+  GURL http_url =
+      GetURLWithNonLocalHostname(embedded_test_server(), "/title1.html");
+  ui_test_utils::NavigateToURL(browser(), http_url);
+
+  security_state::SecurityInfo security_info;
+  helper->GetSecurityInfo(&security_info);
+  EXPECT_FALSE(security_info.incognito_downgraded_security_level);
+  EXPECT_EQ(security_state::NONE, security_info.security_level);
+  EXPECT_EQ(0u, observer.latest_explanations().neutral_explanations.size());
+  EXPECT_EQ(blink::kWebSecurityStyleNeutral, observer.latest_security_style());
+}
+
+// Tests that the security level of a HTTP page is downgraded to DANGEROUS when
+// MarkHttpAsDangerous is enabled.
+IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperIncognitoTest,
+                       SecurityLevelDangerousWhenMarkHttpAsDangerous) {
+  base::test::ScopedCommandLine scoped_command_line;
+  scoped_command_line.GetProcessCommandLine()->AppendSwitchASCII(
+      security_state::switches::kMarkHttpAs,
+      security_state::switches::kMarkHttpAsDangerous);
+
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(contents);
+  ASSERT_TRUE(contents->GetBrowserContext()->IsOffTheRecord());
+
+  SecurityStyleTestObserver observer(contents);
+
+  SecurityStateTabHelper* helper =
+      SecurityStateTabHelper::FromWebContents(contents);
+  ASSERT_TRUE(helper);
+
+  // Navigate to an HTTP page. Use a non-local hostname so that it is
+  // not considered secure.
+  GURL http_url =
+      GetURLWithNonLocalHostname(embedded_test_server(), "/title1.html");
+  ui_test_utils::NavigateToURL(browser(), http_url);
+
+  security_state::SecurityInfo security_info;
+  helper->GetSecurityInfo(&security_info);
+  EXPECT_FALSE(security_info.incognito_downgraded_security_level);
+  EXPECT_EQ(security_state::DANGEROUS, security_info.security_level);
+  EXPECT_EQ(blink::kWebSecurityStyleInsecure, observer.latest_security_style());
+}
+
 // Visit a valid HTTPS page, then a broken HTTPS page, and then go back,
 // and test that the observed security style matches.
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index f163c14..2321021 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3289,6 +3289,8 @@
       "app_list/profile_loader.cc",
       "app_list/profile_loader.h",
       "app_list/profile_store.h",
+      "app_list/search/answer_card_result.cc",
+      "app_list/search/answer_card_result.h",
       "app_list/search/answer_card_search_provider.cc",
       "app_list/search/answer_card_search_provider.h",
       "app_list/search/app_result.cc",
diff --git a/chrome/browser/ui/app_list/search/answer_card_result.cc b/chrome/browser/ui/app_list/search/answer_card_result.cc
new file mode 100644
index 0000000..38665ee
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/answer_card_result.cc
@@ -0,0 +1,79 @@
+// 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/app_list/search/answer_card_result.h"
+
+#include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "third_party/WebKit/public/platform/WebMouseEvent.h"
+
+namespace app_list {
+
+AnswerCardResult::AnswerCardResult(Profile* profile,
+                                   AppListControllerDelegate* list_controller,
+                                   const std::string& result_url,
+                                   const base::string16& result_title,
+                                   views::View* web_view,
+                                   content::WebContents* web_contents)
+    : WebContentsObserver(web_contents),
+      profile_(profile),
+      list_controller_(list_controller),
+      mouse_event_callback_(base::Bind(&AnswerCardResult::HandleMouseEvent,
+                                       base::Unretained(this))) {
+  set_display_type(DISPLAY_CARD);
+  set_id(result_url);
+  set_relevance(1);
+  set_view(web_view);
+  set_title(result_title);
+  // web_contents may be null if the result is being duplicated after the
+  // search provider's WebContents was destroyed.
+  if (web_contents) {
+    content::RenderViewHost* const rvh = web_contents->GetRenderViewHost();
+    if (rvh) {
+      rvh->GetWidget()->AddMouseEventCallback(mouse_event_callback_);
+    }
+  }
+}
+
+AnswerCardResult::~AnswerCardResult() {
+  // WebContentsObserver::web_contents() returns nullptr after destruction of
+  // WebContents.
+  if (web_contents()) {
+    content::RenderViewHost* const rvh = web_contents()->GetRenderViewHost();
+    if (rvh) {
+      rvh->GetWidget()->RemoveMouseEventCallback(mouse_event_callback_);
+    }
+  }
+}
+
+std::unique_ptr<SearchResult> AnswerCardResult::Duplicate() const {
+  return base::MakeUnique<AnswerCardResult>(profile_, list_controller_, id(),
+                                            title(), view(), web_contents());
+}
+
+void AnswerCardResult::Open(int event_flags) {
+  list_controller_->OpenURL(profile_, GURL(id()), ui::PAGE_TRANSITION_GENERATED,
+                            ui::DispositionFromEventFlags(event_flags));
+}
+
+bool AnswerCardResult::HandleMouseEvent(const blink::WebMouseEvent& event) {
+  switch (event.GetType()) {
+    case blink::WebInputEvent::kMouseMove:
+    case blink::WebInputEvent::kMouseEnter:
+      if (!is_mouse_in_view())
+        SetIsMouseInView(true);
+      break;
+    case blink::WebInputEvent::kMouseLeave:
+      if (is_mouse_in_view())
+        SetIsMouseInView(false);
+      break;
+    default:
+      break;
+  }
+
+  return false;
+}
+
+}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/answer_card_result.h b/chrome/browser/ui/app_list/search/answer_card_result.h
new file mode 100644
index 0000000..2f7e56bc
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/answer_card_result.h
@@ -0,0 +1,50 @@
+// 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 CHROME_BROWSER_UI_APP_LIST_SEARCH_ANSWER_CARD_RESULT_H_
+#define CHROME_BROWSER_UI_APP_LIST_SEARCH_ANSWER_CARD_RESULT_H_
+
+#include <memory>
+#include <string>
+
+#include "content/public/browser/render_widget_host.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "ui/app_list/search_result.h"
+
+class AppListControllerDelegate;
+class Profile;
+
+namespace app_list {
+
+// Result of AnswerCardSearchProvider.
+class AnswerCardResult : public SearchResult,
+                         public content::WebContentsObserver {
+ public:
+  AnswerCardResult(Profile* profile,
+                   AppListControllerDelegate* list_controller,
+                   const std::string& result_url,
+                   const base::string16& result_title,
+                   views::View* web_view,
+                   content::WebContents* web_contents);
+
+  ~AnswerCardResult() override;
+
+  // SearchResult overrides:
+  std::unique_ptr<SearchResult> Duplicate() const override;
+
+  void Open(int event_flags) override;
+
+ private:
+  bool HandleMouseEvent(const blink::WebMouseEvent& event);
+
+  Profile* const profile_;                            // Unowned
+  AppListControllerDelegate* const list_controller_;  // Unowned
+  const content::RenderWidgetHost::MouseEventCallback mouse_event_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(AnswerCardResult);
+};
+
+}  // namespace app_list
+
+#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_ANSWER_CARD_RESULT_H_
diff --git a/chrome/browser/ui/app_list/search/answer_card_result_unittest.cc b/chrome/browser/ui/app_list/search/answer_card_result_unittest.cc
new file mode 100644
index 0000000..8825d16
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/answer_card_result_unittest.cc
@@ -0,0 +1,175 @@
+// 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/ui/app_list/search/answer_card_result.h"
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/app_list/app_list_test_util.h"
+#include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/web_contents.h"
+#include "third_party/WebKit/public/platform/WebMouseEvent.h"
+#include "ui/app_list/search_result_observer.h"
+#include "ui/views/view.h"
+
+namespace app_list {
+namespace test {
+
+namespace {
+
+constexpr char kResultUrl[] = "http://google.com/search?q=weather";
+constexpr char kResultTitle[] = "The weather is fine";
+
+}  // namespace
+
+class AnswerCardResultTest : public AppListTestBase,
+                             public app_list::SearchResultObserver {
+ public:
+  AnswerCardResultTest() {}
+
+  void DeleteWebContents() { web_contents_.reset(nullptr); }
+
+  std::unique_ptr<AnswerCardResult> CreateResult(
+      const std::string& result_url,
+      const base::string16& result_title) const {
+    return base::MakeUnique<AnswerCardResult>(
+        profile_.get(), app_list_controller_delegate_.get(), result_url,
+        result_title, view_.get(), web_contents_.get());
+  }
+
+  const GURL& GetLastOpenedUrl() const {
+    return app_list_controller_delegate_->last_opened_url();
+  }
+
+  void VerifyLastMouseHoverEvent(bool expected_mouse_in_view,
+                                 SearchResult* result) {
+    ASSERT_TRUE(received_hover_event_);
+    EXPECT_EQ(expected_mouse_in_view, result->is_mouse_in_view());
+    received_hover_event_ = false;
+  }
+
+  void InjectMouseEvent(blink::WebInputEvent::Type type) {
+    web_contents_->GetRenderViewHost()->GetWidget()->ForwardMouseEvent(
+        blink::WebMouseEvent(type, blink::WebInputEvent::kNoModifiers,
+                             blink::WebInputEvent::kTimeStampForTesting));
+  }
+
+  views::View* view() const { return view_.get(); }
+
+  // AppListTestBase overrides:
+  void SetUp() override {
+    AppListTestBase::SetUp();
+
+    web_contents_.reset(
+        content::WebContents::Create(content::WebContents::CreateParams(
+            profile_.get(), content::SiteInstance::Create(profile_.get()))));
+    view_ = base::MakeUnique<views::View>();
+    app_list_controller_delegate_ =
+        base::MakeUnique<::test::TestAppListControllerDelegate>();
+  }
+
+  void TearDown() override {
+    ASSERT_FALSE(received_hover_event_);
+    AppListTestBase::TearDown();
+  }
+
+  // SearchResultObserver overrides:
+  void OnViewHoverStateChanged() override {
+    ASSERT_FALSE(received_hover_event_);
+    received_hover_event_ = true;
+  }
+
+ private:
+  std::unique_ptr<views::View> view_;
+  std::unique_ptr<content::WebContents> web_contents_;
+  bool received_hover_event_ = false;
+  std::unique_ptr<::test::TestAppListControllerDelegate>
+      app_list_controller_delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(AnswerCardResultTest);
+};
+
+TEST_F(AnswerCardResultTest, Basic) {
+  std::unique_ptr<AnswerCardResult> result =
+      CreateResult(kResultUrl, base::ASCIIToUTF16(kResultTitle));
+
+  EXPECT_EQ(kResultUrl, result->id());
+  EXPECT_EQ(base::ASCIIToUTF16(kResultTitle), result->title());
+  EXPECT_EQ(SearchResult::DISPLAY_CARD, result->display_type());
+  EXPECT_EQ(1, result->relevance());
+  EXPECT_EQ(view(), result->view());
+
+  result->Open(ui::EF_NONE);
+  EXPECT_EQ(kResultUrl, GetLastOpenedUrl().spec());
+
+  std::unique_ptr<SearchResult> result1 = result->Duplicate();
+
+  EXPECT_EQ(kResultUrl, result1->id());
+  EXPECT_EQ(base::ASCIIToUTF16(kResultTitle), result1->title());
+  EXPECT_EQ(SearchResult::DISPLAY_CARD, result1->display_type());
+  EXPECT_EQ(1, result1->relevance());
+  EXPECT_EQ(view(), result1->view());
+}
+
+TEST_F(AnswerCardResultTest, NullWebContents) {
+  DeleteWebContents();
+
+  // Shouldn't crash with null WebContents.
+  std::unique_ptr<AnswerCardResult> result =
+      CreateResult(kResultUrl, base::ASCIIToUTF16(kResultTitle));
+  std::unique_ptr<SearchResult> result1 = result->Duplicate();
+}
+
+TEST_F(AnswerCardResultTest, EarlyDeleteWebContents) {
+  // Shouldn't crash with WebContents gets deleted while search result exists.
+  std::unique_ptr<AnswerCardResult> result =
+      CreateResult(kResultUrl, base::ASCIIToUTF16(kResultTitle));
+
+  DeleteWebContents();
+
+  result->Duplicate();
+}
+
+TEST_F(AnswerCardResultTest, MouseEvents) {
+  std::unique_ptr<SearchResult> result;
+  {
+    // Duplicate the result, so that we know that the copy still generates
+    // events.
+    std::unique_ptr<AnswerCardResult> result_original =
+        CreateResult(kResultUrl, base::ASCIIToUTF16(kResultTitle));
+    result = result_original->Duplicate();
+  }
+
+  result->AddObserver(this);
+
+  ASSERT_FALSE(result->is_mouse_in_view());
+
+  InjectMouseEvent(blink::WebInputEvent::kMouseLeave);
+  ASSERT_FALSE(result->is_mouse_in_view());
+
+  InjectMouseEvent(blink::WebInputEvent::kMouseEnter);
+  VerifyLastMouseHoverEvent(true, result.get());
+
+  InjectMouseEvent(blink::WebInputEvent::kMouseEnter);
+  // Not using VerifyLastMouseHoverEvent will effectively check that second
+  // Enter event didn't invoke a callback.
+  ASSERT_TRUE(result->is_mouse_in_view());
+
+  InjectMouseEvent(blink::WebInputEvent::kMouseLeave);
+  VerifyLastMouseHoverEvent(false, result.get());
+
+  InjectMouseEvent(blink::WebInputEvent::kMouseLeave);
+  // Not using VerifyLastMouseHoverEvent will effectively check that second
+  // Leave event didn't invoke a callback.
+  ASSERT_FALSE(result->is_mouse_in_view());
+
+  InjectMouseEvent(blink::WebInputEvent::kMouseMove);
+  VerifyLastMouseHoverEvent(true, result.get());
+}
+
+}  // namespace test
+}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/answer_card_search_provider.cc b/chrome/browser/ui/app_list/search/answer_card_search_provider.cc
index 19f01330..8dcb330 100644
--- a/chrome/browser/ui/app_list/search/answer_card_search_provider.cc
+++ b/chrome/browser/ui/app_list/search/answer_card_search_provider.cc
@@ -9,6 +9,7 @@
 #include "base/metrics/user_metrics.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/app_list/search/answer_card_result.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "content/public/browser/navigation_handle.h"
@@ -19,11 +20,9 @@
 #include "content/public/common/renderer_preferences.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_status_code.h"
-#include "third_party/WebKit/public/platform/WebMouseEvent.h"
 #include "ui/app_list/app_list_features.h"
 #include "ui/app_list/app_list_model.h"
 #include "ui/app_list/search_box_model.h"
-#include "ui/app_list/search_result.h"
 #include "ui/views/controls/webview/web_contents_set_background_color.h"
 #include "ui/views/controls/webview/webview.h"
 #include "ui/views/widget/widget.h"
@@ -76,87 +75,15 @@
   DISALLOW_COPY_AND_ASSIGN(SearchAnswerWebView);
 };
 
-class SearchAnswerResult : public SearchResult,
-                           public content::WebContentsObserver {
- public:
-  SearchAnswerResult(Profile* profile,
-                     const std::string& result_url,
-                     const base::string16& result_title,
-                     views::View* web_view,
-                     content::WebContents* web_contents)
-      : WebContentsObserver(web_contents),
-        profile_(profile),
-        mouse_event_callback_(base::Bind(&SearchAnswerResult::HandleMouseEvent,
-                                         base::Unretained(this))) {
-    set_display_type(DISPLAY_CARD);
-    set_id(result_url);
-    set_relevance(1);
-    set_view(web_view);
-    set_title(result_title);
-    // web_contents may be null if the result is being duplicated after the
-    // search provider's WebContents was destroyed.
-    if (web_contents) {
-      content::RenderViewHost* const rvh = web_contents->GetRenderViewHost();
-      if (rvh) {
-        rvh->GetWidget()->AddMouseEventCallback(mouse_event_callback_);
-      }
-    }
-  }
-
-  ~SearchAnswerResult() override {
-    // WebContentsObserver::web_contents() returns nullptr after destruction of
-    // WebContents.
-    if (web_contents()) {
-      content::RenderViewHost* const rvh = web_contents()->GetRenderViewHost();
-      if (rvh) {
-        rvh->GetWidget()->RemoveMouseEventCallback(mouse_event_callback_);
-      }
-    }
-  }
-
-  // SearchResult overrides:
-  std::unique_ptr<SearchResult> Duplicate() const override {
-    return base::MakeUnique<SearchAnswerResult>(profile_, id(), title(), view(),
-                                                web_contents());
-  }
-
-  void Open(int event_flags) override {
-    chrome::NavigateParams params(profile_, GURL(id()),
-                                  ui::PAGE_TRANSITION_GENERATED);
-    params.disposition = ui::DispositionFromEventFlags(event_flags);
-    chrome::Navigate(&params);
-  }
-
- private:
-  bool HandleMouseEvent(const blink::WebMouseEvent& event) {
-    switch (event.GetType()) {
-      case blink::WebInputEvent::kMouseMove:
-      case blink::WebInputEvent::kMouseEnter:
-        if (!is_mouse_in_view())
-          SetIsMouseInView(true);
-        break;
-      case blink::WebInputEvent::kMouseLeave:
-        if (is_mouse_in_view())
-          SetIsMouseInView(false);
-        break;
-      default:
-        break;
-    }
-
-    return false;
-  }
-
-  Profile* const profile_;
-  const content::RenderWidgetHost::MouseEventCallback mouse_event_callback_;
-};
-
 }  // namespace
 
 AnswerCardSearchProvider::AnswerCardSearchProvider(
     Profile* profile,
-    app_list::AppListModel* model)
+    app_list::AppListModel* model,
+    AppListControllerDelegate* list_controller)
     : profile_(profile),
       model_(model),
+      list_controller_(list_controller),
       web_view_(base::MakeUnique<SearchAnswerWebView>(profile)),
       web_contents_(
           content::WebContents::Create(content::WebContents::CreateParams(
@@ -356,9 +283,10 @@
   SearchProvider::Results results;
   if (is_available) {
     results.reserve(1);
-    results.emplace_back(base::MakeUnique<SearchAnswerResult>(
-        profile_, result_url_, base::UTF8ToUTF16(result_title_),
-        web_view_.get(), web_contents_.get()));
+    results.emplace_back(base::MakeUnique<AnswerCardResult>(
+        profile_, list_controller_, result_url_,
+        base::UTF8ToUTF16(result_title_), web_view_.get(),
+        web_contents_.get()));
   }
   SwapResults(&results);
 }
diff --git a/chrome/browser/ui/app_list/search/answer_card_search_provider.h b/chrome/browser/ui/app_list/search/answer_card_search_provider.h
index d2996e40..4a989cba 100644
--- a/chrome/browser/ui/app_list/search/answer_card_search_provider.h
+++ b/chrome/browser/ui/app_list/search/answer_card_search_provider.h
@@ -14,6 +14,7 @@
 #include "ui/app_list/search_provider.h"
 #include "url/gurl.h"
 
+class AppListControllerDelegate;
 class Profile;
 
 namespace app_list {
@@ -36,7 +37,9 @@
                                  public content::WebContentsDelegate,
                                  public content::WebContentsObserver {
  public:
-  AnswerCardSearchProvider(Profile* profile, app_list::AppListModel* model);
+  AnswerCardSearchProvider(Profile* profile,
+                           app_list::AppListModel* model,
+                           AppListControllerDelegate* list_controller);
 
   ~AnswerCardSearchProvider() override;
 
@@ -77,6 +80,9 @@
   // Unowned pointer to app list model.
   app_list::AppListModel* const model_;
 
+  // Unowned pointer to app list controller.
+  AppListControllerDelegate* const list_controller_;
+
   // Web view for the web contents managed by this class.
   const std::unique_ptr<views::WebView> web_view_;
 
diff --git a/chrome/browser/ui/app_list/search/search_controller_factory.cc b/chrome/browser/ui/app_list/search/search_controller_factory.cc
index 6220d78..4753768 100644
--- a/chrome/browser/ui/app_list/search/search_controller_factory.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_factory.cc
@@ -88,9 +88,9 @@
       webstore_group_id,
       base::MakeUnique<WebstoreProvider>(profile, list_controller));
   if (features::IsAnswerCardEnabled()) {
-    controller->AddProvider(
-        answer_card_group_id,
-        base::MakeUnique<AnswerCardSearchProvider>(profile, model));
+    controller->AddProvider(answer_card_group_id,
+                            base::MakeUnique<AnswerCardSearchProvider>(
+                                profile, model, list_controller));
   }
   if (IsSuggestionsSearchProviderEnabled()) {
     size_t suggestions_group_id =
diff --git a/chrome/browser/ui/bookmarks/bookmark_bar_constants.h b/chrome/browser/ui/bookmarks/bookmark_bar_constants.h
index f80b23f..dddb974 100644
--- a/chrome/browser/ui/bookmarks/bookmark_bar_constants.h
+++ b/chrome/browser/ui/bookmarks/bookmark_bar_constants.h
@@ -20,9 +20,9 @@
 // points) because of the visual overlap with the main toolbar. When using this
 // to compute values other than the actual height of the toolbar, be sure to add
 // |kVisualHeightOffset|.
-const int kMinimumBookmarkBarHeight = 25;
+const int kMinimumBookmarkBarHeight = 26;
 #elif defined(TOOLKIT_VIEWS)
-const int kMinimumBookmarkBarHeight = 27;
+const int kMinimumBookmarkBarHeight = 28;
 #endif
 
 }  // namespace chrome
diff --git a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.cc b/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.cc
index b5ef1a0..5e41940f 100644
--- a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.cc
+++ b/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.cc
@@ -12,6 +12,9 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profiles_state.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/grit/chromium_strings.h"
@@ -20,6 +23,7 @@
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/signin_error_controller.h"
 #include "third_party/libphonenumber/phonenumber_api.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/color_utils.h"
@@ -75,14 +79,22 @@
      IDS_BOOKMARK_BUBBLE_DESKTOP_TO_IOS_PROMO_TITLE_V3}};
 
 bool IsEligibleForIOSPromotion(
-    PrefService* prefs,
-    const syncer::SyncService* sync_service,
+    Profile* profile,
     desktop_ios_promotion::PromotionEntryPoint entry_point) {
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kForceDesktopIOSPromotion)) {
     return true;
   }
 
+  // Don't show promotion if there has been authentication error, because this
+  // will prevent the recovery phone number from showing.
+  const SigninErrorController* signin_error_controller =
+      profiles::GetSigninErrorController(profile);
+  if (signin_error_controller && signin_error_controller->HasError())
+    return false;
+
+  const browser_sync::ProfileSyncService* sync_service =
+      ProfileSyncServiceFactory::GetForProfile(profile);
   // Promotion should only show for english locale.
   PrefService* local_state = g_browser_process->local_state();
   std::string locale = base::i18n::GetConfiguredLocale();
@@ -111,6 +123,7 @@
   if (is_dismissed || show_count >= impression_cap)
     return false;
 
+  PrefService* prefs = profile->GetPrefs();
   // Don't show the promotion if the user have used any entry point to recieve
   // SMS on the last 7 days.
   double last_impression = prefs->GetDouble(prefs::kIOSPromotionLastImpression);
diff --git a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h b/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h
index 4adcc342..2253a32 100644
--- a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h
+++ b/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h
@@ -10,16 +10,12 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/image/image_skia.h"
 
-namespace syncer {
-class SyncService;
-}
-
 namespace user_prefs {
 class PrefRegistrySyncable;
 }
 
 class PrefRegistrySimple;
-class PrefService;
+class Profile;
 
 namespace desktop_ios_promotion {
 
@@ -71,8 +67,7 @@
     {prefs::kNumberHistoryPageIOSPromoShown,
      prefs::kHistoryPageIOSPromoDismissed}};
 
-bool IsEligibleForIOSPromotion(PrefService* prefs,
-                               const syncer::SyncService* sync_service,
+bool IsEligibleForIOSPromotion(Profile* profile,
                                PromotionEntryPoint entry_point);
 
 // Returns the SMS ID to be used with send SMS API call.
diff --git a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util_unittest.cc b/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util_unittest.cc
index 3d726fd..f28b062 100644
--- a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util_unittest.cc
+++ b/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util_unittest.cc
@@ -4,32 +4,63 @@
 
 #include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h"
 
+#include <memory>
+
 #include "base/i18n/rtl.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/test/scoped_feature_list.h"
+#include "chrome/browser/prefs/browser_prefs.h"
+#include "chrome/browser/signin/signin_error_controller_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/profile_sync_test_util.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
 #include "components/browser_sync/profile_sync_service.h"
+#include "components/browser_sync/test_profile_sync_service.h"
 #include "components/prefs/pref_service.h"
+#include "components/signin/core/browser/fake_auth_status_provider.h"
+#include "components/signin/core/browser/signin_manager.h"
 #include "components/sync/driver/fake_sync_service.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+
+typedef GoogleServiceAuthError AuthError;
 
 namespace {
 
-class TestSyncService : public syncer::FakeSyncService {
+class TestSyncService : public browser_sync::TestProfileSyncService {
  public:
-  // FakeSyncService overrides.
+  explicit TestSyncService(Profile* profile)
+      : browser_sync::TestProfileSyncService(
+            CreateProfileSyncServiceParamsForTest(profile)) {}
+
   bool IsSyncAllowed() const override { return is_sync_allowed_; }
 
   void set_sync_allowed(bool sync_allowed) { is_sync_allowed_ = sync_allowed; }
 
  private:
   bool is_sync_allowed_ = true;
+  DISALLOW_COPY_AND_ASSIGN(TestSyncService);
 };
 
+std::unique_ptr<KeyedService> BuildFakeSyncService(
+    content::BrowserContext* context) {
+  return base::MakeUnique<TestSyncService>(
+      static_cast<TestingProfile*>(context));
+}
+
+constexpr int kSMSEntrypointSavePasswordBubble =
+    1
+    << static_cast<int>(desktop_ios_promotion::PromotionEntryPoint::SAVE_PASSWORD_BUBBLE);
+
+constexpr int kSMSEntrypointBookmarksBubble =
+    1
+    << static_cast<int>(desktop_ios_promotion::PromotionEntryPoint::BOOKMARKS_BUBBLE);
+
 }  // namespace
 
 class DesktopIOSPromotionUtilTest : public testing::Test {
@@ -41,11 +72,23 @@
     local_state_.reset(new TestingPrefServiceSimple);
     TestingBrowserProcess::GetGlobal()->SetLocalState(local_state_.get());
     desktop_ios_promotion::RegisterLocalPrefs(local_state_->registry());
-    pref_service_ = new sync_preferences::TestingPrefServiceSyncable();
-    desktop_ios_promotion::RegisterProfilePrefs(pref_service_->registry());
+    auto pref_service =
+        base::MakeUnique<sync_preferences::TestingPrefServiceSyncable>();
+    chrome::RegisterUserProfilePrefs(pref_service->registry());
+    TestingProfile::Builder profile_builder;
+    profile_builder.SetPrefService(std::move(pref_service));
+    profile_builder.AddTestingFactory(ProfileSyncServiceFactory::GetInstance(),
+                                      &BuildFakeSyncService);
+    profile_ = profile_builder.Build();
+    sync_service_ = static_cast<TestSyncService*>(
+        ProfileSyncServiceFactory::GetForProfile(profile_.get()));
+    mock_signin_ = static_cast<SigninManagerBase*>(
+        SigninManagerFactory::GetForProfile(profile_.get()));
+    mock_signin_->SetAuthenticatedAccountInfo("test", "test");
   }
 
   void TearDown() override {
+    profile_.reset();
     // Ensure that g_accept_requests gets set back to true after test execution.
     TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
     local_state_.reset();
@@ -53,10 +96,11 @@
 
   PrefService* local_state() { return local_state_.get(); }
 
-  TestSyncService* sync_service() { return &fake_sync_service_; }
-  sync_preferences::TestingPrefServiceSyncable* prefs() {
-    return pref_service_;
-  }
+  TestSyncService* sync_service() { return sync_service_; }
+
+  PrefService* prefs() { return profile_->GetPrefs(); }
+
+  Profile* profile() { return profile_.get(); }
 
   double GetDoubleNDayOldDate(int days) {
     base::Time time_result =
@@ -64,12 +108,12 @@
     return time_result.ToDoubleT();
   }
 
- protected:
-  std::unique_ptr<TestingPrefServiceSimple> local_state_;
-  sync_preferences::TestingPrefServiceSyncable* pref_service_;
-  TestSyncService fake_sync_service_;
-
  private:
+  TestSyncService* sync_service_ = nullptr;
+  content::TestBrowserThreadBundle thread_bundle_;
+  std::unique_ptr<TestingPrefServiceSimple> local_state_;
+  SigninManagerBase* mock_signin_ = nullptr;
+  std::unique_ptr<TestingProfile> profile_;
   DISALLOW_COPY_AND_ASSIGN(DesktopIOSPromotionUtilTest);
 };
 
@@ -77,8 +121,8 @@
   desktop_ios_promotion::PromotionEntryPoint entry_point =
       desktop_ios_promotion::PromotionEntryPoint::SAVE_PASSWORD_BUBBLE;
   // By default the promo is off.
-  EXPECT_FALSE(desktop_ios_promotion::IsEligibleForIOSPromotion(
-      prefs(), nullptr, entry_point));
+  EXPECT_FALSE(
+      desktop_ios_promotion::IsEligibleForIOSPromotion(profile(), entry_point));
 
   // Enable the promotion and assign the entry_point finch parameter.
   base::FieldTrialList field_trial_list(nullptr);
@@ -101,6 +145,7 @@
   std::string locales[] = {"en-US", "en-CA", "en-AU", "es-US"};
   constexpr struct {
     bool is_sync_allowed;
+    bool signin_error;
     int locale_index;
     bool is_dismissed;
     int show_count;
@@ -110,36 +155,44 @@
     bool promo_done;
     bool result;
   } kTestData[] = {
-      // {sync allowed, locale, dismissed before, impression count, seen days
+      // {sync allowed, signin error exist, locale, dismissed before, impression
+      // count, seen days
       // ago, bitmask with entry points seen, is user eligible, flow was
       // completed before, expected result }
-      {false, 0, false, 0, 1, 0, false, false, false},
-      {false, 1, false, 0, 3, 0, true, false, false},
-      {true, 3, false, 0, 4, 0, true, false, false},
-      {true, 2, false, 0, 10, 0, true, false, false},
-      {true, 0, true, 1, 3, 0, true, false, false},
-      {true, 0, false, 3, 1, 0, true, false, false},
-      {true, 0, false, 1, 3,
-       1 << static_cast<int>(
-           desktop_ios_promotion::PromotionEntryPoint::SAVE_PASSWORD_BUBBLE),
-       true, false, false},
-      {true, 0, false, 0, 4,
-       1 << static_cast<int>(
-           desktop_ios_promotion::PromotionEntryPoint::BOOKMARKS_BUBBLE),
-       true, false, false},
-      {true, 0, false, 1, 10, 0, false, false, false},
-      {true, 0, false, 0, 1, 0, true, true, false},
-      {true, 1, false, 1, 1, 0, true, false, true},
-      {true, 1, false, 0, 2, 0, true, false, true},
-      {true, 0, false, 0, 8,
-       1 << static_cast<int>(
-           desktop_ios_promotion::PromotionEntryPoint::SAVE_PASSWORD_BUBBLE),
-       true, false, true},
+      {false, false, 0, false, 0, 1, 0, false, false, false},
+      {false, false, 1, false, 0, 3, 0, true, false, false},
+      {true, false, 3, false, 0, 4, 0, true, false, false},
+      {true, false, 2, false, 0, 10, 0, true, false, false},
+      {true, false, 0, true, 1, 3, 0, true, false, false},
+      {true, false, 0, false, 3, 1, 0, true, false, false},
+      {true, false, 0, false, 1, 3, kSMSEntrypointSavePasswordBubble, true,
+       false, false},
+      {true, false, 0, false, 0, 4, kSMSEntrypointBookmarksBubble, true, false,
+       false},
+      {true, false, 0, false, 1, 10, 0, false, false, false},
+      {true, false, 0, false, 0, 1, 0, true, true, false},
+      {true, true, 1, false, 1, 1, 0, true, false, false},
+      {true, false, 1, false, 1, 1, 0, true, false, true},
+      {true, false, 1, false, 0, 2, 0, true, false, true},
+      {true, true, 0, false, 0, 8, kSMSEntrypointSavePasswordBubble, true,
+       false, false},
+      {true, false, 0, false, 0, 8, kSMSEntrypointSavePasswordBubble, true,
+       false, true},
   };
   std::string locale = base::i18n::GetConfiguredLocale();
+  FakeAuthStatusProvider auth_provider(
+      SigninErrorControllerFactory::GetForProfile(profile()));
+
   for (const auto& test_case : kTestData) {
     SCOPED_TRACE(testing::Message("#test_case = ") << (&test_case - kTestData));
     sync_service()->set_sync_allowed(test_case.is_sync_allowed);
+    const GoogleServiceAuthError error(
+        test_case.signin_error
+            ? GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS
+            : GoogleServiceAuthError::NONE);
+
+    auth_provider.SetAuthError("test", error);
+
     local_state()->SetBoolean(prefs::kSavePasswordsBubbleIOSPromoDismissed,
                               test_case.is_dismissed);
     local_state()->SetInteger(prefs::kNumberSavePasswordsBubbleIOSPromoShown,
@@ -153,7 +206,7 @@
                         test_case.is_user_eligible);
     prefs()->SetBoolean(prefs::kIOSPromotionDone, test_case.promo_done);
     EXPECT_EQ(test_case.result,
-              IsEligibleForIOSPromotion(prefs(), sync_service(), entry_point));
+              IsEligibleForIOSPromotion(profile(), entry_point));
   }
   base::i18n::SetICUDefaultLocale(locale);
 }
diff --git a/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc b/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
index 6d0dce9..d5cd143 100644
--- a/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
@@ -461,7 +461,7 @@
 #if defined(OS_WIN)
   // Desktop to mobile promotion only enabled on windows.
   if (desktop_ios_promotion::IsEligibleForIOSPromotion(
-          prefs, sync_service,
+          GetProfile(),
           desktop_ios_promotion::PromotionEntryPoint::SAVE_PASSWORD_BUBBLE)) {
     interaction_keeper_->ReportInteractions(this);
     title_brand_link_range_ = gfx::Range();
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
index ed2397d..34d93b7 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
@@ -34,10 +34,8 @@
 #include "ui/views/window/dialog_client_view.h"
 
 #if defined(OS_WIN)
-#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.h"
 #include "chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_footnote_view.h"
-#include "components/browser_sync/profile_sync_service.h"
 #endif
 
 using base::UserMetricsAction;
@@ -202,7 +200,8 @@
 views::View* BookmarkBubbleView::CreateFootnoteView() {
 #if defined(OS_WIN)
   if (!is_showing_ios_promotion_ &&
-      IsIOSPromotionEligible(
+      desktop_ios_promotion::IsEligibleForIOSPromotion(
+          profile_,
           desktop_ios_promotion::PromotionEntryPoint::BOOKMARKS_FOOTNOTE)) {
     footnote_view_ = new DesktopIOSPromotionFootnoteView(profile_, this);
     return footnote_view_;
@@ -230,7 +229,8 @@
 bool BookmarkBubbleView::Accept() {
 #if defined(OS_WIN)
   using desktop_ios_promotion::PromotionEntryPoint;
-  if (IsIOSPromotionEligible(PromotionEntryPoint::BOOKMARKS_BUBBLE)) {
+  if (desktop_ios_promotion::IsEligibleForIOSPromotion(
+          profile_, PromotionEntryPoint::BOOKMARKS_BUBBLE)) {
     ShowIOSPromotion(PromotionEntryPoint::BOOKMARKS_BUBBLE);
     return false;
   }
@@ -416,16 +416,6 @@
 }
 
 #if defined(OS_WIN)
-
-bool BookmarkBubbleView::IsIOSPromotionEligible(
-    desktop_ios_promotion::PromotionEntryPoint entry_point) {
-  PrefService* prefs = profile_->GetPrefs();
-  const browser_sync::ProfileSyncService* sync_service =
-      ProfileSyncServiceFactory::GetForProfile(profile_);
-  return desktop_ios_promotion::IsEligibleForIOSPromotion(prefs, sync_service,
-                                                          entry_point);
-}
-
 void BookmarkBubbleView::ShowIOSPromotion(
     desktop_ios_promotion::PromotionEntryPoint entry_point) {
   DCHECK(!is_showing_ios_promotion_);
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h
index 8adbc07..e60642fe 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h
@@ -119,10 +119,6 @@
   void ApplyEdits();
 
 #if defined(OS_WIN)
-  // Check eligibility to show the iOS promotion from a specific entry point.
-  bool IsIOSPromotionEligible(
-      desktop_ios_promotion::PromotionEntryPoint entry_point);
-
   // Shows the iOS promotion.
   void ShowIOSPromotion(desktop_ios_promotion::PromotionEntryPoint entry_point);
 #endif
diff --git a/chrome/browser/ui/views/frame/browser_view_unittest.cc b/chrome/browser/ui/views/frame/browser_view_unittest.cc
index 3a13912..de6477ba7 100644
--- a/chrome/browser/ui/views/frame/browser_view_unittest.cc
+++ b/chrome/browser/ui/views/frame/browser_view_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/macros.h"
 #include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/ui/bookmarks/bookmark_bar_constants.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
 #include "chrome/browser/ui/views/frame/browser_view_layout.h"
@@ -114,6 +115,9 @@
   BookmarkBarView* bookmark_bar = browser_view()->GetBookmarkBarView();
   EXPECT_FALSE(bookmark_bar->visible());
   EXPECT_FALSE(bookmark_bar->IsDetached());
+  EXPECT_EQ(devtools_web_view->y(), bookmark_bar->height());
+  EXPECT_EQ(chrome::kMinimumBookmarkBarHeight,
+            bookmark_bar->GetMinimumSize().height());
   chrome::ExecuteCommand(browser, IDC_SHOW_BOOKMARK_BAR);
   EXPECT_TRUE(bookmark_bar->visible());
   EXPECT_FALSE(bookmark_bar->IsDetached());
diff --git a/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc b/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc
index 624d469..5fc23338 100644
--- a/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc
+++ b/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc
@@ -55,7 +55,6 @@
   ~PermissionsBubbleDialogDelegateView() override;
 
   void CloseBubble();
-  void SizeToContents();
 
   // BubbleDialogDelegateView:
   bool ShouldShowCloseButton() const override;
@@ -69,6 +68,7 @@
   int GetDefaultDialogButton() const override;
   int GetDialogButtons() const override;
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
+  void SizeToContents() override;
 
   // Updates the anchor's arrow and view. Also repositions the bubble so it's
   // displayed in the correct location.
diff --git a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
index 4d403d8a..ab599f61 100644
--- a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
+++ b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
@@ -14,29 +14,30 @@
 #include "base/guid.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
 #include "base/values.h"
 #include "chrome/browser/android/offline_pages/offline_page_model_factory.h"
 #include "chrome/browser/android/offline_pages/prefetch/prefetch_background_task.h"
 #include "chrome/browser/android/offline_pages/request_coordinator_factory.h"
 #include "chrome/browser/offline_pages/prefetch/prefetch_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/chrome_content_client.h"
 #include "components/offline_pages/core/client_namespace_constants.h"
 #include "components/offline_pages/core/offline_page_feature.h"
+#include "components/offline_pages/core/prefetch/generate_page_bundle_request.h"
+#include "components/offline_pages/core/prefetch/get_operation_request.h"
 #include "components/offline_pages/core/prefetch/prefetch_service.h"
 #include "content/public/browser/web_ui.h"
 #include "net/base/network_change_notifier.h"
 
 namespace offline_internals {
 
-OfflineInternalsUIMessageHandler::OfflineInternalsUIMessageHandler()
-    : offline_page_model_(nullptr),
-      request_coordinator_(nullptr),
-      prefetch_service_(nullptr),
-      weak_ptr_factory_(this) {}
+namespace {
 
-OfflineInternalsUIMessageHandler::~OfflineInternalsUIMessageHandler() {}
-
-std::string OfflineInternalsUIMessageHandler::GetStringFromDeletePageResult(
+std::string GetStringFromDeletePageResult(
     offline_pages::DeletePageResult value) {
   switch (value) {
     case offline_pages::DeletePageResult::SUCCESS:
@@ -56,7 +57,7 @@
   return "Unknown";
 }
 
-std::string OfflineInternalsUIMessageHandler::GetStringFromDeleteRequestResults(
+std::string GetStringFromDeleteRequestResults(
     const offline_pages::MultipleItemStatuses& results) {
   // If any requests failed, return "failure", else "success".
   for (const auto& result : results) {
@@ -67,10 +68,62 @@
   return "Success";
 }
 
-std::string OfflineInternalsUIMessageHandler::GetStringFromSavePageStatus() {
+std::string GetStringFromSavePageStatus() {
   return "Available";
 }
 
+std::string GetStringFromPrefetchRequestStatus(
+    offline_pages::PrefetchRequestStatus status) {
+  switch (status) {
+    case offline_pages::PrefetchRequestStatus::SUCCESS:
+      return "Success";
+    case offline_pages::PrefetchRequestStatus::SHOULD_RETRY_WITHOUT_BACKOFF:
+      return "Retry w/out backoff";
+    case offline_pages::PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF:
+      return "Retry w/ backoff";
+    case offline_pages::PrefetchRequestStatus::SHOULD_SUSPEND:
+      return "Suspend";
+    default:
+      NOTREACHED();
+      return "Unknown";
+  }
+}
+
+std::string GetStringRenderPageInfoList(
+    const std::vector<offline_pages::RenderPageInfo>& pages) {
+  std::string str("[\n");
+  bool first = true;
+  for (const auto& page : pages) {
+    if (first)
+      first = false;
+    else
+      str += ",\n";
+    str += base::StringPrintf(
+        "  {\n"
+        "    url: \"%s\",\n"
+        "    redirect_url: \"%s\",\n"
+        "    status: %d,\n"
+        "    body_name: \"%s\",\n"
+        "    body_length: %lld\n"
+        "  }",
+        page.url.c_str(), page.redirect_url.c_str(),
+        static_cast<int>(page.status), page.body_name.c_str(),
+        static_cast<long long>(page.body_length));
+  }
+  str += "\n]";
+  return str;
+}
+
+}  // namespace
+
+OfflineInternalsUIMessageHandler::OfflineInternalsUIMessageHandler()
+    : offline_page_model_(nullptr),
+      request_coordinator_(nullptr),
+      prefetch_service_(nullptr),
+      weak_ptr_factory_(this) {}
+
+OfflineInternalsUIMessageHandler::~OfflineInternalsUIMessageHandler() {}
+
 void OfflineInternalsUIMessageHandler::HandleDeleteSelectedPages(
     const base::ListValue* args) {
   std::string callback_id;
@@ -257,6 +310,55 @@
   ResolveJavascriptCallback(*callback_id, base::Value("Cancelled."));
 }
 
+void OfflineInternalsUIMessageHandler::HandleGeneratePageBundle(
+    const base::ListValue* args) {
+  AllowJavascript();
+  std::string callback_id;
+  CHECK(args->GetString(0, &callback_id));
+
+  std::string data;
+  CHECK(args->GetString(1, &data));
+  std::vector<std::string> page_urls = base::SplitStringUsingSubstr(
+      data, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+
+  generate_page_bundle_request_.reset(
+      new offline_pages::GeneratePageBundleRequest(
+          GetUserAgent(), "GCM ID", 1000000, page_urls, chrome::GetChannel(),
+          Profile::FromWebUI(web_ui())->GetRequestContext(),
+          base::Bind(
+              &OfflineInternalsUIMessageHandler::HandlePrefetchRequestCallback,
+              weak_ptr_factory_.GetWeakPtr(), callback_id)));
+}
+
+void OfflineInternalsUIMessageHandler::HandleGetOperation(
+    const base::ListValue* args) {
+  AllowJavascript();
+  std::string callback_id;
+  CHECK(args->GetString(0, &callback_id));
+
+  std::string name;
+  CHECK(args->GetString(1, &name));
+  base::TrimWhitespaceASCII(name, base::TRIM_ALL, &name);
+
+  get_operation_request_.reset(new offline_pages::GetOperationRequest(
+      name, chrome::GetChannel(),
+      Profile::FromWebUI(web_ui())->GetRequestContext(),
+      base::Bind(
+          &OfflineInternalsUIMessageHandler::HandlePrefetchRequestCallback,
+          weak_ptr_factory_.GetWeakPtr(), callback_id)));
+}
+
+void OfflineInternalsUIMessageHandler::HandlePrefetchRequestCallback(
+    std::string callback_id,
+    offline_pages::PrefetchRequestStatus status,
+    const std::string& operation_name,
+    const std::vector<offline_pages::RenderPageInfo>& pages) {
+  ResolveJavascriptCallback(
+      base::Value(callback_id),
+      base::Value(GetStringFromPrefetchRequestStatus(status) + "\n" +
+                  operation_name + "\n" + GetStringRenderPageInfoList(pages)));
+}
+
 void OfflineInternalsUIMessageHandler::HandleSetRecordRequestQueue(
     const base::ListValue* args) {
   AllowJavascript();
@@ -400,6 +502,14 @@
       "cancelNwake",
       base::Bind(&OfflineInternalsUIMessageHandler::HandleCancelNwake,
                  weak_ptr_factory_.GetWeakPtr()));
+  web_ui()->RegisterMessageCallback(
+      "generatePageBundle",
+      base::Bind(&OfflineInternalsUIMessageHandler::HandleGeneratePageBundle,
+                 weak_ptr_factory_.GetWeakPtr()));
+  web_ui()->RegisterMessageCallback(
+      "getOperation",
+      base::Bind(&OfflineInternalsUIMessageHandler::HandleGetOperation,
+                 weak_ptr_factory_.GetWeakPtr()));
 
   // Get the offline page model associated with this web ui.
   Profile* profile = Profile::FromWebUI(web_ui());
diff --git a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h
index 9fc42c6..61da1724 100644
--- a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h
+++ b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.h
@@ -15,11 +15,14 @@
 #include "components/offline_pages/core/background/save_page_request.h"
 #include "components/offline_pages/core/offline_page_model.h"
 #include "components/offline_pages/core/offline_store_types.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
 namespace offline_pages {
 class PrefetchService;
 enum class GetRequestsResult;
+class GeneratePageBundleRequest;
+class GetOperationRequest;
 }
 
 namespace offline_internals {
@@ -73,6 +76,12 @@
   // Cancels an NWake signal.
   void HandleCancelNwake(const base::ListValue* args);
 
+  // Sends and processes the request to generate page bundle.
+  void HandleGeneratePageBundle(const base::ListValue* args);
+
+  // Sends and processes a request to get the info about an operation.
+  void HandleGetOperation(const base::ListValue* args);
+
   // Callback for async GetAllPages calls.
   void HandleStoredPagesCallback(
       std::string callback_id,
@@ -93,16 +102,18 @@
       std::string callback_id,
       const offline_pages::MultipleItemStatuses& results);
 
-  // Turns a DeletePageResult enum into logical string.
-  std::string GetStringFromDeletePageResult(
-      offline_pages::DeletePageResult value);
+  // Callback for GeneratePageBundle/GetOperation request calls.
+  void HandlePrefetchRequestCallback(
+      std::string callback_id,
+      offline_pages::PrefetchRequestStatus status,
+      const std::string& operation_name,
+      const std::vector<offline_pages::RenderPageInfo>& pages);
 
-  // Summarizes the MultipleItemStatuses vector with a string.
-  std::string GetStringFromDeleteRequestResults(
-      const offline_pages::MultipleItemStatuses& result);
-
-  // Turns a SavePageRequest::Status into logical string.
-  std::string GetStringFromSavePageStatus();
+  // Callback for GetOperation calls.
+  void HandleGetOperationCallback(
+      std::string callback_id,
+      offline_pages::PrefetchRequestStatus status,
+      const std::vector<offline_pages::RenderPageInfo>& pages);
 
   // Offline page model to call methods on.
   offline_pages::OfflinePageModel* offline_page_model_;
@@ -113,6 +124,10 @@
   // Prefetch service for prefetching service logs and actions.
   offline_pages::PrefetchService* prefetch_service_;
 
+  std::unique_ptr<offline_pages::GeneratePageBundleRequest>
+      generate_page_bundle_request_;
+  std::unique_ptr<offline_pages::GetOperationRequest> get_operation_request_;
+
   // Factory for creating references in callbacks.
   base::WeakPtrFactory<OfflineInternalsUIMessageHandler> weak_ptr_factory_;
 
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc b/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
index 2814faff..2d32a54 100644
--- a/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
@@ -173,7 +173,7 @@
       ->DispatchGetCapabilityRequested(
           destination_id,
           base::Bind(&ExtensionPrinterHandler::WrapGetCapabilityCallback,
-                     weak_ptr_factory_.GetWeakPtr(), callback, destination_id));
+                     weak_ptr_factory_.GetWeakPtr(), callback));
 }
 
 void ExtensionPrinterHandler::StartPrint(
@@ -300,9 +300,8 @@
 
 void ExtensionPrinterHandler::WrapGetCapabilityCallback(
     const PrinterHandler::GetCapabilityCallback& callback,
-    const std::string& destination_id,
     const base::DictionaryValue& capability) {
-  callback.Run(destination_id, capability);
+  callback.Run(capability);
 }
 
 void ExtensionPrinterHandler::WrapPrintCallback(
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler.h b/chrome/browser/ui/webui/print_preview/extension_printer_handler.h
index 183d172..266cc117 100644
--- a/chrome/browser/ui/webui/print_preview/extension_printer_handler.h
+++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler.h
@@ -102,7 +102,6 @@
       bool done);
   void WrapGetCapabilityCallback(
       const PrinterHandler::GetCapabilityCallback& callback,
-      const std::string& destination_id,
       const base::DictionaryValue& capability);
   void WrapPrintCallback(const PrinterHandler::PrintCallback& callback,
                          bool success,
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc b/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
index 668f8d6d6..94d1ab7 100644
--- a/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
@@ -203,12 +203,9 @@
 // Used as a callback to StartGetCapability in tests.
 // Increases |*call_count| and records values returned by StartGetCapability.
 void RecordCapability(size_t* call_count,
-                      std::string* destination_id_out,
                       std::unique_ptr<base::DictionaryValue>* capability_out,
-                      const std::string& destination_id,
                       const base::DictionaryValue& capability) {
   ++(*call_count);
-  *destination_id_out = destination_id;
   capability_out->reset(capability.DeepCopy());
 }
 
@@ -609,12 +606,10 @@
 
 TEST_F(ExtensionPrinterHandlerTest, GetCapability) {
   size_t call_count = 0;
-  std::string destination_id;
   std::unique_ptr<base::DictionaryValue> capability;
 
   extension_printer_handler_->StartGetCapability(
-      kPrinterId,
-      base::Bind(&RecordCapability, &call_count, &destination_id, &capability));
+      kPrinterId, base::Bind(&RecordCapability, &call_count, &capability));
 
   EXPECT_EQ(0u, call_count);
 
@@ -631,7 +626,6 @@
   fake_api->TriggerNextGetCapabilityCallback(*original_capability);
 
   EXPECT_EQ(1u, call_count);
-  EXPECT_EQ(kPrinterId, destination_id);
   ASSERT_TRUE(capability.get());
   EXPECT_TRUE(capability->Equals(original_capability.get()))
       << *capability << ", expected: " << *original_capability;
@@ -639,12 +633,10 @@
 
 TEST_F(ExtensionPrinterHandlerTest, GetCapability_Reset) {
   size_t call_count = 0;
-  std::string destination_id;
   std::unique_ptr<base::DictionaryValue> capability;
 
   extension_printer_handler_->StartGetCapability(
-      kPrinterId,
-      base::Bind(&RecordCapability, &call_count, &destination_id, &capability));
+      kPrinterId, base::Bind(&RecordCapability, &call_count, &capability));
 
   EXPECT_EQ(0u, call_count);
 
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index e0a11e502..16b30f20 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -682,7 +682,7 @@
   AllowJavascript();
 
   if (!PrivetPrintingEnabled()) {
-    RejectJavascriptCallback(base::Value(callback_id), base::Value(false));
+    RejectJavascriptCallback(base::Value(callback_id), base::Value());
   }
 #if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
   using local_discovery::ServiceDiscoverySharedClient;
@@ -701,20 +701,30 @@
     printer_lister_->Stop();
   }
   ResolveJavascriptCallback(base::Value(privet_callback_id_), base::Value());
+  privet_callback_id_ = "";
 #endif
 }
 
 void PrintPreviewHandler::HandleGetPrivetPrinterCapabilities(
     const base::ListValue* args) {
-#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
-  std::string name;
-  bool success = args->GetString(0, &name);
-  DCHECK(success);
+  AllowJavascript();
 
-  CreatePrivetHTTP(
-      name, base::Bind(&PrintPreviewHandler::PrivetCapabilitiesUpdateClient,
-                       weak_factory_.GetWeakPtr()));
+  std::string callback_id;
+  std::string printer_name;
+  if (!args->GetString(0, &callback_id) || !args->GetString(1, &printer_name) ||
+      callback_id.empty() || printer_name.empty()) {
+    RejectJavascriptCallback(base::Value(callback_id), base::Value());
+    return;
+  }
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
+  if (CreatePrivetHTTP(
+          printer_name,
+          base::Bind(&PrintPreviewHandler::PrivetCapabilitiesUpdateClient,
+                     weak_factory_.GetWeakPtr(), callback_id))) {
+    return;
+  }
 #endif
+  RejectJavascriptCallback(base::Value(callback_id), base::Value());
 }
 
 void PrintPreviewHandler::HandleGetExtensionPrinters(
@@ -747,15 +757,21 @@
 
 void PrintPreviewHandler::HandleGetExtensionPrinterCapabilities(
     const base::ListValue* args) {
-  std::string printer_id;
-  bool ok = args->GetString(0, &printer_id);
-  DCHECK(ok);
+  AllowJavascript();
+
+  std::string callback_id;
+  std::string printer_name;
+  if (!args->GetString(0, &callback_id) || !args->GetString(1, &printer_name) ||
+      callback_id.empty() || printer_name.empty()) {
+    RejectJavascriptCallback(base::Value(callback_id), base::Value());
+    return;
+  }
 
   EnsureExtensionPrinterHandlerSet();
   extension_printer_handler_->StartGetCapability(
-      printer_id,
+      printer_name,
       base::Bind(&PrintPreviewHandler::OnGotExtensionPrinterCapabilities,
-                 weak_factory_.GetWeakPtr()));
+                 weak_factory_.GetWeakPtr(), callback_id));
 }
 
 void PrintPreviewHandler::HandleGetPreview(const base::ListValue* args) {
@@ -1071,10 +1087,15 @@
 
 void PrintPreviewHandler::HandleGetPrinterCapabilities(
     const base::ListValue* args) {
+  AllowJavascript();
+
+  std::string callback_id;
   std::string printer_name;
-  bool ret = args->GetString(0, &printer_name);
-  if (!ret || printer_name.empty())
+  if (!args->GetString(0, &callback_id) || !args->GetString(1, &printer_name) ||
+      callback_id.empty() || printer_name.empty()) {
+    RejectJavascriptCallback(base::Value(callback_id), base::Value());
     return;
+  }
 
   if (printer_name == kLocalPdfPrinterId) {
     auto printer_info = base::MakeUnique<base::DictionaryValue>();
@@ -1082,13 +1103,13 @@
     printer_info->Set(
         printing::kPrinterCapabilities,
         GetPdfCapabilities(g_browser_process->GetApplicationLocale()));
-    SendPrinterCapabilities(printer_name, std::move(printer_info));
+    SendPrinterCapabilities(callback_id, printer_name, std::move(printer_info));
     return;
   }
 
   printing::PrinterSetupCallback cb =
       base::Bind(&PrintPreviewHandler::SendPrinterCapabilities,
-                 weak_factory_.GetWeakPtr(), printer_name);
+                 weak_factory_.GetWeakPtr(), callback_id, printer_name);
 
   printer_backend_proxy()->ConfigurePrinterAndFetchCapabilities(printer_name,
                                                                 cb);
@@ -1307,19 +1328,18 @@
 }
 
 void PrintPreviewHandler::SendPrinterCapabilities(
+    const std::string& callback_id,
     const std::string& printer_name,
     std::unique_ptr<base::DictionaryValue> settings_info) {
   // Check that |settings_info| is valid.
   if (settings_info && settings_info->Get("capabilities", nullptr)) {
     VLOG(1) << "Get printer capabilities finished";
-    web_ui()->CallJavascriptFunctionUnsafe("updateWithPrinterCapabilities",
-                                           *settings_info);
+    ResolveJavascriptCallback(base::Value(callback_id), *settings_info);
     return;
   }
 
   VLOG(1) << "Get printer capabilities failed";
-  web_ui()->CallJavascriptFunctionUnsafe("failedToGetPrinterCapabilities",
-                                         base::Value(printer_name));
+  RejectJavascriptCallback(base::Value(callback_id), base::Value());
 }
 
 void PrintPreviewHandler::SendPrinterSetup(
@@ -1570,21 +1590,30 @@
 }
 
 void PrintPreviewHandler::PrivetCapabilitiesUpdateClient(
+    const std::string& callback_id,
     std::unique_ptr<cloud_print::PrivetHTTPClient> http_client) {
-  if (!PrivetUpdateClient(std::move(http_client)))
+  if (!PrivetUpdateClient(callback_id, std::move(http_client)))
     return;
 
   privet_capabilities_operation_ =
       privet_http_client_->CreateCapabilitiesOperation(
           base::Bind(&PrintPreviewHandler::OnPrivetCapabilities,
-                     weak_factory_.GetWeakPtr()));
+                     weak_factory_.GetWeakPtr(), callback_id));
   privet_capabilities_operation_->Start();
 }
 
 bool PrintPreviewHandler::PrivetUpdateClient(
+    const std::string& callback_id,
     std::unique_ptr<cloud_print::PrivetHTTPClient> http_client) {
   if (!http_client) {
-    SendPrivetCapabilitiesError(privet_http_resolution_->GetName());
+    if (callback_id.empty()) {
+      // This was an attempt to print to a privet printer and has failed.
+      base::Value http_code_value(-1);
+      web_ui()->CallJavascriptFunctionUnsafe("onPrivetPrintFailed",
+                                             http_code_value);
+    } else {  // Capabilities update failed
+      RejectJavascriptCallback(base::Value(callback_id), base::Value());
+    }
     privet_http_resolution_.reset();
     return false;
   }
@@ -1604,7 +1633,7 @@
     std::string capabilities,
     gfx::Size page_size,
     std::unique_ptr<cloud_print::PrivetHTTPClient> http_client) {
-  if (!PrivetUpdateClient(std::move(http_client)))
+  if (!PrivetUpdateClient("", std::move(http_client)))
     return;
 
   StartPrivetLocalPrint(print_ticket, capabilities, page_size);
@@ -1645,49 +1674,52 @@
   privet_local_print_operation_->Start();
 }
 
-
 void PrintPreviewHandler::OnPrivetCapabilities(
+    const std::string& callback_id,
     const base::DictionaryValue* capabilities) {
   std::string name = privet_capabilities_operation_->GetHTTPClient()->GetName();
 
   if (!capabilities || capabilities->HasKey(cloud_print::kPrivetKeyError) ||
       !printer_lister_) {
-    SendPrivetCapabilitiesError(name);
+    RejectJavascriptCallback(base::Value(callback_id), base::Value());
     return;
   }
 
-  base::DictionaryValue printer_info;
   const cloud_print::DeviceDescription* description =
       printer_lister_->GetDeviceDescription(name);
 
   if (!description) {
-    SendPrivetCapabilitiesError(name);
+    RejectJavascriptCallback(base::Value(callback_id), base::Value());
     return;
   }
 
-  FillPrinterDescription(name, *description, true, &printer_info);
-
-  web_ui()->CallJavascriptFunctionUnsafe("onPrivetCapabilitiesSet",
-                                         printer_info, *capabilities);
+  std::unique_ptr<base::DictionaryValue> printer_info =
+      base::MakeUnique<base::DictionaryValue>();
+  FillPrinterDescription(name, *description, true, printer_info.get());
+  base::DictionaryValue printer_info_and_caps;
+  printer_info_and_caps.SetDictionary("printer", std::move(printer_info));
+  std::unique_ptr<base::DictionaryValue> capabilities_copy =
+      capabilities->CreateDeepCopy();
+  printer_info_and_caps.SetDictionary("capabilities",
+                                      std::move(capabilities_copy));
+  ResolveJavascriptCallback(base::Value(callback_id), printer_info_and_caps);
 
   privet_capabilities_operation_.reset();
 }
 
-void PrintPreviewHandler::SendPrivetCapabilitiesError(
-    const std::string& device_name) {
-  base::Value name_value(device_name);
-  web_ui()->CallJavascriptFunctionUnsafe("failedToGetPrivetPrinterCapabilities",
-                                         name_value);
-}
-
 void PrintPreviewHandler::PrintToPrivetPrinter(const std::string& device_name,
                                                const std::string& ticket,
                                                const std::string& capabilities,
                                                const gfx::Size& page_size) {
-  CreatePrivetHTTP(
-      device_name,
-      base::Bind(&PrintPreviewHandler::PrivetLocalPrintUpdateClient,
-                 weak_factory_.GetWeakPtr(), ticket, capabilities, page_size));
+  if (!CreatePrivetHTTP(
+          device_name,
+          base::Bind(&PrintPreviewHandler::PrivetLocalPrintUpdateClient,
+                     weak_factory_.GetWeakPtr(), ticket, capabilities,
+                     page_size))) {
+    base::Value http_code_value(-1);
+    web_ui()->CallJavascriptFunctionUnsafe("onPrivetPrintFailed",
+                                           http_code_value);
+  }
 }
 
 bool PrintPreviewHandler::CreatePrivetHTTP(
@@ -1697,10 +1729,8 @@
   const cloud_print::DeviceDescription* device_description =
       printer_lister_ ? printer_lister_->GetDeviceDescription(name) : NULL;
 
-  if (!device_description) {
-    SendPrivetCapabilitiesError(name);
+  if (!device_description)
     return false;
-  }
 
   privet_http_factory_ =
       cloud_print::PrivetHTTPAsynchronousFactory::CreateInstance(
@@ -1776,16 +1806,13 @@
 }
 
 void PrintPreviewHandler::OnGotExtensionPrinterCapabilities(
-    const std::string& printer_id,
+    const std::string& callback_id,
     const base::DictionaryValue& capabilities) {
   if (capabilities.empty()) {
-    web_ui()->CallJavascriptFunctionUnsafe(
-        "failedToGetExtensionPrinterCapabilities", base::Value(printer_id));
+    RejectJavascriptCallback(base::Value(callback_id), base::Value());
     return;
   }
-
-  web_ui()->CallJavascriptFunctionUnsafe("onExtensionCapabilitiesSet",
-                                         base::Value(printer_id), capabilities);
+  ResolveJavascriptCallback(base::Value(callback_id), capabilities);
 }
 
 void PrintPreviewHandler::OnExtensionPrintResult(bool success,
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.h b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
index 61133eb6..f1884810 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -230,6 +230,7 @@
   // printer capabilities information. If |settings_info| is empty, sends
   // error notification to the Web UI instead.
   void SendPrinterCapabilities(
+      const std::string& callback_id,
       const std::string& printer_name,
       std::unique_ptr<base::DictionaryValue> settings_info);
 
@@ -285,8 +286,10 @@
   void StartPrivetLister(const scoped_refptr<
       local_discovery::ServiceDiscoverySharedClient>& client);
   void StopPrivetLister();
-  void OnPrivetCapabilities(const base::DictionaryValue* capabilities);
+  void OnPrivetCapabilities(const std::string& callback_id,
+                            const base::DictionaryValue* capabilities);
   void PrivetCapabilitiesUpdateClient(
+      const std::string& callback_id,
       std::unique_ptr<cloud_print::PrivetHTTPClient> http_client);
   void PrivetLocalPrintUpdateClient(
       std::string print_ticket,
@@ -294,6 +297,7 @@
       gfx::Size page_size,
       std::unique_ptr<cloud_print::PrivetHTTPClient> http_client);
   bool PrivetUpdateClient(
+      const std::string& callback_id,
       std::unique_ptr<cloud_print::PrivetHTTPClient> http_client);
   void StartPrivetLocalPrint(const std::string& print_ticket,
                              const std::string& capabilities,
@@ -337,10 +341,10 @@
 
   // Called when an extension reports the set of print capabilites for a
   // printer.
-  // |printer_id|: The id of the printer whose capabilities are reported.
+  // |callback_id|: The Javascript callback to reject or resolve
   // |capabilities|: The printer capabilities.
   void OnGotExtensionPrinterCapabilities(
-      const std::string& printer_id,
+      const std::string& callback_id,
       const base::DictionaryValue& capabilities);
 
   // Called when an extension print job is completed.
diff --git a/chrome/browser/ui/webui/print_preview/printer_handler.h b/chrome/browser/ui/webui/print_preview/printer_handler.h
index ccf12af9e2..58b215f6 100644
--- a/chrome/browser/ui/webui/print_preview/printer_handler.h
+++ b/chrome/browser/ui/webui/print_preview/printer_handler.h
@@ -35,8 +35,7 @@
   using GetPrintersCallback =
       base::Callback<void(const base::ListValue& printers, bool done)>;
   using GetCapabilityCallback =
-      base::Callback<void(const std::string& printer_id,
-                          const base::DictionaryValue& capability)>;
+      base::Callback<void(const base::DictionaryValue& capability)>;
   using PrintCallback =
       base::Callback<void(bool success, const std::string& error)>;
   using GetPrinterInfoCallback =
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index a76c765..2a8196d 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -569,13 +569,6 @@
 // Forces the maximum disk space to be used by the media cache, in bytes.
 const char kMediaCacheSize[]                = "media-cache-size";
 
-// Enables the recording of metrics reports but disables reporting. In contrast
-// to kDisableMetrics, this executes all the code that a normal client would
-// use for reporting, except the report is dropped rather than sent to the
-// server. This is useful for finding issues in the metrics code during UI and
-// performance tests.
-const char kMetricsRecordingOnly[]          = "metrics-recording-only";
-
 // Allows setting a different destination ID for connection-monitoring GCM
 // messages. Useful when running against a non-prod management server.
 const char kMonitoringDestinationID[]       = "monitoring-destination-id";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index bd0145c..dec100fe 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -167,7 +167,6 @@
 extern const char kLoadMediaRouterComponentExtension[];
 extern const char kMakeDefaultBrowser[];
 extern const char kMediaCacheSize[];
-extern const char kMetricsRecordingOnly[];
 extern const char kMonitoringDestinationID[];
 extern const char kNetLogCaptureMode[];
 extern const char kNoDefaultBrowserCheck[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 3d183dc..7e40976 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3153,7 +3153,6 @@
     "../browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h",
     "../browser/page_load_metrics/observers/previews_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/protocol_page_load_metrics_observer_unittest.cc",
-    "../browser/page_load_metrics/observers/resource_tracking_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/service_worker_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/subresource_filter_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer_unittest.cc",
@@ -4868,6 +4867,7 @@
       "../browser/ui/app_list/arc/arc_app_unittest.cc",
       "../browser/ui/app_list/extension_app_model_builder_unittest.cc",
       "../browser/ui/app_list/profile_loader_unittest.cc",
+      "../browser/ui/app_list/search/answer_card_result_unittest.cc",
       "../browser/ui/app_list/search/app_search_provider_unittest.cc",
       "../browser/ui/app_list/search/history_unittest.cc",
       "../browser/ui/app_list/search/launcher_search/launcher_search_icon_image_loader_unittest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestCaseBase.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestCaseBase.java
index e0919ad..60899e8 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestCaseBase.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestCaseBase.java
@@ -377,13 +377,6 @@
     }
 
     @Override
-    protected void runTest() throws Throwable {
-        String perfTagAnalysisString = mTestCommon.setupPotentialPerfTest();
-        super.runTest();
-        mTestCommon.endPerfTest(perfTagAnalysisString);
-    }
-
-    @Override
     protected Map<String, BaseParameter> createAvailableParameters() {
         Map<String, BaseParameter> availableParameters = super.createAvailableParameters();
         availableParameters.put(AddFakeAccountToAppParameter.PARAMETER_TAG,
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestCommon.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestCommon.java
index c26b1ac..219ebce5e 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestCommon.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestCommon.java
@@ -23,11 +23,8 @@
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ApplicationStatus.ActivityStateListener;
 import org.chromium.base.Log;
-import org.chromium.base.PerfTraceEvent;
 import org.chromium.base.ThreadUtils;
-import org.chromium.base.annotations.SuppressFBWarnings;
 import org.chromium.base.test.util.CallbackHelper;
-import org.chromium.base.test.util.PerfTest;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
@@ -60,11 +57,9 @@
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.ui.base.PageTransition;
 
-import java.io.File;
 import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.Method;
 import java.util.Calendar;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
@@ -668,122 +663,6 @@
                 mCallback.getActivity(), expectedScale);
     }
 
-    /**
-     * This method creates a special string that tells the python test harness what
-     * trace calls to track for this particular test run.  It can support multiple trace calls for
-     * each test and will make a new graph entry for all of them.  It should be noted that this
-     * method eats all exceptions.  This is so that it can never be the cause of a test failure.
-     * We still need to call this method even if we know the test will not run (ie: willTestRun is
-     * false).  This is because this method lets the python test harness know not to expect any
-     * perf output in this case.  In the case that the autoTrace parameter is set for the current
-     * test method, this will also start the PerfTrace facility automatically.
-     *
-     * @return A specially formatted string that contains which JSON perf markers to look at. This
-     *         will be analyzed by the perf test harness.
-     */
-    @SuppressFBWarnings({
-            "REC_CATCH_EXCEPTION", "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE", })
-    String setupPotentialPerfTest() {
-        File perfFile = mCallback.getInstrumentation().getTargetContext().getFileStreamPath(
-                PERF_OUTPUT_FILE);
-        perfFile.delete();
-        PerfTraceEvent.setOutputFile(perfFile);
-
-        String perfAnnotationString = "";
-
-        try {
-            Method method = getClass().getMethod(mCallback.getTestName(), (Class[]) null);
-            PerfTest annotation = method.getAnnotation(PerfTest.class);
-            if (annotation != null) {
-                StringBuilder annotationData = new StringBuilder();
-                annotationData.append(String.format(PERF_ANNOTATION_FORMAT, method.getName()));
-
-                // Grab the minimum number of trace calls we will track (if names(),
-                // graphNames(), and graphValues() do not have the same number of elements, we
-                // will track as many as we can given the data available.
-                final int maxIndex = Math.min(annotation.traceNames().length,
-                        Math.min(annotation.graphNames().length, annotation.seriesNames().length));
-
-                List<String> allNames = new LinkedList<>();
-                for (int i = 0; i < maxIndex; ++i) {
-                    // Prune out all of ',' and ';' from the strings.  Replace them with '-'.
-                    String name = annotation.traceNames()[i].replaceAll("[,;]", "-");
-                    allNames.add(name);
-                    String graphName = annotation.graphNames()[i].replaceAll("[,;]", "-");
-                    String seriesName = annotation.seriesNames()[i].replaceAll("[,;]", "-");
-                    if (annotation.traceTiming()) {
-                        annotationData.append(name)
-                                .append(",")
-                                .append(graphName)
-                                .append(",")
-                                .append(seriesName)
-                                .append(';');
-                    }
-
-                    // If memory tracing is enabled, add an additional graph for each one
-                    // defined to track timing perf that will track the corresponding memory
-                    // usage.
-                    // Keep the series name the same, but just append a memory identifying
-                    // prefix to the graph.
-                    if (annotation.traceMemory()) {
-                        String memName = PerfTraceEvent.makeMemoryTraceNameFromTimingName(name);
-                        String memGraphName = PerfTraceEvent.makeSafeTraceName(
-                                graphName, MEMORY_TRACE_GRAPH_SUFFIX);
-                        annotationData.append(memName)
-                                .append(",")
-                                .append(memGraphName)
-                                .append(",")
-                                .append(seriesName)
-                                .append(';');
-                        allNames.add(memName);
-                    }
-                }
-                // We only record perf trace events for the names explicitly listed.
-                PerfTraceEvent.setFilter(allNames);
-
-                // Figure out if we should automatically start or stop the trace.
-                if (annotation.autoTrace()) {
-                    PerfTraceEvent.setEnabled(true);
-                }
-                PerfTraceEvent.setTimingTrackingEnabled(annotation.traceTiming());
-                PerfTraceEvent.setMemoryTrackingEnabled(annotation.traceMemory());
-
-                perfAnnotationString = annotationData.toString();
-            }
-        } catch (Exception ex) {
-            // Eat exception here.
-        }
-
-        return perfAnnotationString;
-    }
-
-    /**
-     * This handles cleaning up the performance component of this test if it was a UI Perf test.
-     * This includes potentially shutting down PerfTraceEvent.  This method eats all exceptions so
-     * that it can never be the cause of a test failure.  The test harness will wait for
-     * {@code perfTagAnalysisString} to show up in the logcat before processing the JSON perf file,
-     * giving this method the chance to flush and dump the performance data before the harness reads
-     * it.
-     *
-     * @param perfTagAnalysisString A specially formatted string that tells the perf test harness
-     *                              which perf tags to analyze.
-     */
-    void endPerfTest(String perfTagAnalysisString) {
-        try {
-            Method method = getClass().getMethod(mCallback.getTestName(), (Class[]) null);
-            PerfTest annotation = method.getAnnotation(PerfTest.class);
-            if (annotation != null) {
-                if (PerfTraceEvent.enabled()) {
-                    PerfTraceEvent.setEnabled(false);
-                }
-
-                System.out.println(perfTagAnalysisString);
-            }
-        } catch (NoSuchMethodException ex) {
-            // Eat exception here.
-        }
-    }
-
     public interface ChromeTestCommonCallback<T extends ChromeActivity> {
         String getTestName();
         void setActivity(T t);
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java
index 32db790..0ab29e9 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeActivityTestRule.java
@@ -59,14 +59,7 @@
                 base.evaluate();
             }
         }, description);
-        return new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-                String perfTagAnalysisString = mTestCommon.setupPotentialPerfTest();
-                superBase.evaluate();
-                mTestCommon.endPerfTest(perfTagAnalysisString);
-            }
-        };
+        return superBase;
     }
 
     /**
diff --git a/chrome/test/data/webui/print_preview/native_layer_stub.js b/chrome/test/data/webui/print_preview/native_layer_stub.js
index 51f80161..73ba1244 100644
--- a/chrome/test/data/webui/print_preview/native_layer_stub.js
+++ b/chrome/test/data/webui/print_preview/native_layer_stub.js
@@ -14,6 +14,7 @@
         'getPrinters',
         'getExtensionPrinters',
         'getPrivetPrinters',
+        'getPrinterCapabilities',
         'setupPrinter'
       ]);
 
@@ -46,6 +47,14 @@
     this.localDestinationInfos_ = [];
 
     /**
+     * @private {!Map<string,
+     *                !Promise<!print_preview.PrinterCapabilitiesResponse>}
+     *     A map from destination IDs to the responses to be sent when
+     *     |getPrinterCapabilities| is called for the ID.
+     */
+    this.localDestinationCapabilities_ = new Map();
+
+    /**
      * @private {!print_preview.PrinterSetupResponse} The response to be sent
      *     on a |setupPrinter| call.
      */
@@ -55,18 +64,6 @@
      * @private {boolean} Whether the printer setup request should be rejected.
      */
     this.shouldRejectPrinterSetup_ = false;
-
-    /**
-     * @private {string} The destination id to watch for counting calls to
-     *     |getLocalDestinationCapabilities|.
-     */
-    this.destinationToWatch_ = '';
-
-    /**
-     * @private {number} The number of calls to
-     *     |getLocalDestinationCapabilities| with id = |destinationToWatch_|.
-     */
-    this.getLocalDestinationCapabilitiesCallCount_ = 0;
   }
 
   NativeLayerStub.prototype = {
@@ -97,6 +94,12 @@
     },
 
     /** @override */
+    getPrinterCapabilities: function(printerId) {
+      this.methodCalled('getPrinterCapabilities', printerId);
+      return this.localDestinationCapabilities_.get(printerId);
+    },
+
+    /** @override */
     setupPrinter: function(printerId) {
       this.methodCalled('setupPrinter', printerId);
       return this.shouldRejectPrinterSetup_ ?
@@ -106,10 +109,7 @@
 
     /** Stubs for |print_preview.NativeLayer| methods that call C++ handlers. */
     previewReadyForTest: function() {},
-    startGetLocalDestinationCapabilities: function(destinationId) {
-      if (destinationId == this.destinationToWatch_)
-        this.getLocalDestinationCapabilitiesCallCount_++;
-    },
+
     startGetPreview: function(destination, printTicketStore, documentInfo,
                               generateDraft, requestId) {
       this.generateDraft_ = generateDraft;
@@ -125,15 +125,6 @@
       this.eventTarget_ = eventTarget;
     },
 
-    /**
-     * @return {boolean} Whether capabilities have been requested exactly once
-     *     for |destinationToWatch_|.
-     */
-    didGetCapabilitiesOnce: function(destinationId) {
-      return (destinationId == this.destinationToWatch_ &&
-              this.getLocalDestinationCapabilitiesCallCount_ == 1);
-    },
-
     /** @return {boolean} Whether a new draft was requested for preview. */
     generateDraft: function() { return this.generateDraft_; },
 
@@ -141,16 +132,6 @@
     isPrintStarted: function() { return this.printStarted_; },
 
     /**
-     * @param {string} destinationId The destination ID to watch for
-     *     |getLocalDestinationCapabilities| calls.
-     * Resets |getLocalDestinationCapabilitiesCallCount_|.
-     */
-    setDestinationToWatch: function(destinationId) {
-      this.destinationToWatch_ = destinationId;
-      this.getLocalDestinationCapabilitiesCallCount_ = 0;
-    },
-
-    /**
      * @param {!print_preview.NativeInitialSettings} settings The settings
      *     to return as a response to |getInitialSettings|.
      */
@@ -167,6 +148,18 @@
     },
 
     /**
+     * @param {!print_preview.PrinterCapabilitiesResponse} response The
+     *     response to send for the destination whose ID is in the response.
+     * @param {boolean?} opt_reject Whether to reject the callback for this
+     *     destination. Defaults to false (will resolve callback) if not
+     *     provided.
+     */
+    setLocalDestinationCapabilities: function(response, opt_reject) {
+      this.localDestinationCapabilities_.set(response.printerId,
+          opt_reject ? Promise.reject() : Promise.resolve(response));
+    },
+
+    /**
      * @param {boolean} reject Whether printSetup requests should be rejected.
      * @param {!print_preview.PrinterSetupResponse} The response to send when
      *     |setupPrinter| is called.
diff --git a/chrome/test/data/webui/print_preview/print_preview_destination_search_test.js b/chrome/test/data/webui/print_preview/print_preview_destination_search_test.js
index eb665ca..e5ea6e4 100644
--- a/chrome/test/data/webui/print_preview/print_preview_destination_search_test.js
+++ b/chrome/test/data/webui/print_preview/print_preview_destination_search_test.js
@@ -102,31 +102,6 @@
       });
     };
 
-    function mockSetupCall(destId, nativeLayerMock) {
-      assert (!cr.isChromeOS);
-      nativeLayerMock.setDestinationToWatch(destId);
-      var resolver = new PromiseResolver();
-
-      resolver.promise.then(
-          function(result) {
-            // Simulate the native layer dispatching capabilities.
-            var capsSetEvent =
-                new Event(print_preview.NativeLayer.EventType.CAPABILITIES_SET);
-            capsSetEvent.settingsInfo = result;
-            destinationStore_.onLocalDestinationCapabilitiesSet_(capsSetEvent);
-            expectTrue(nativeLayerMock.didGetCapabilitiesOnce(destId));
-          }.bind(this),
-          function() {
-            var failEvent = new Event(
-                print_preview.NativeLayer.EventType.GET_CAPABILITIES_FAIL);
-            failEvent.destinationId = destId;
-            destinationStore_.onGetCapabilitiesFail_(failEvent);
-            expectTrue(nativeLayerMock.didGetCapabilitiesOnce(destId));
-          }.bind(this));
-
-      return resolver;
-    };
-
     function requestSetup(destId, destinationSearch) {
       var origin = cr.isChromeOS ? print_preview.DestinationOrigin.CROS :
                                    print_preview.DestinationOrigin.LOCAL;
@@ -176,16 +151,17 @@
       if (cr.isChromeOS) {
         nativeLayer_.setSetupPrinterResponse(true, { printerId: destId,
                                                      success: false,});
-        requestSetup(destId, destinationSearch_);
-        return nativeLayer_.whenCalled('setupPrinter').then(
-            function(actualDestId) {
-              assertEquals(destId, actualDestId);
-            });
       } else {
-        var resolver = mockSetupCall(destId, nativeLayer_);
-        requestSetup(destId, destinationSearch_);
-        resolver.reject(destId);
+        nativeLayer_.setLocalDestinationCapabilities({printerId: destId,
+                                                      capabilities: getCaps()},
+                                                     true);
       }
+      requestSetup(destId, destinationSearch_);
+      var callback = cr.isChromeOS ? 'setupPrinter' : 'getPrinterCapabilities';
+      return nativeLayer_.whenCalled(callback).then(
+          function(actualDestId) {
+            assertEquals(destId, actualDestId);
+          });
     });
 
     test('ReceiveSuccessfulSetup', function() {
@@ -197,32 +173,22 @@
       };
       if (cr.isChromeOS)
         nativeLayer_.setSetupPrinterResponse(false, response);
+      else
+        nativeLayer_.setLocalDestinationCapabilities({printerId: destId,
+                                                      capabilities: getCaps()});
 
       var waiter = waitForEvent(
           destinationStore_,
           print_preview.DestinationStore.EventType.DESTINATION_SELECT);
-      if (cr.isChromeOS) {
-        requestSetup(destId, destinationSearch_);
-        return Promise.all([
-            nativeLayer_.whenCalled('setupPrinter'), waiter
-        ]).then(function(results) {
-          assertEquals(destId, results[0]);
-
-          // after setup succeeds and event arrives, the destination should
-          // be selected.
-          assertNotEquals(null, destinationStore_.selectedDestination);
-          assertEquals(destId, destinationStore_.selectedDestination.id);
-        });
-      } else { //!cr.isChromeOS
-        var resolver = mockSetupCall(destId, nativeLayer_);
-        requestSetup(destId, destinationSearch_);
-        resolver.resolve(response);
-        return waiter.then(function() {
-          // after setup succeeds, the destination should be selected.
-          assertNotEquals(null, destinationStore_.selectedDestination);
-          assertEquals(destId, destinationStore_.selectedDestination.id);
-        });
-      }
+      requestSetup(destId, destinationSearch_);
+      var callback = cr.isChromeOS ? 'setupPrinter' : 'getPrinterCapabilities';
+      return Promise.all([nativeLayer_.whenCalled(callback), waiter]).then(
+          function(results) {
+            assertEquals(destId, results[0]);
+            // after setup succeeds, the destination should be selected.
+            assertNotEquals(null, destinationStore_.selectedDestination);
+            assertEquals(destId, destinationStore_.selectedDestination.id);
+          });
     });
 
     if (cr.isChromeOS) {
diff --git a/chrome/test/data/webui/print_preview/print_preview_tests.js b/chrome/test/data/webui/print_preview/print_preview_tests.js
index e70b7c3..33ddef3 100644
--- a/chrome/test/data/webui/print_preview/print_preview_tests.js
+++ b/chrome/test/data/webui/print_preview/print_preview_tests.js
@@ -40,44 +40,39 @@
   }
 
   /**
-   * Start loading the local destinations using the destination infos currently
-   * stored in |localDestinationInfos|.
+   * Sets settings and destinations and local destination that is the system
+   * default.
+   * @param {print_preview.PrinterCapabilitiesResponse=} opt_device The
+   *     response to use for printer capabilities when the printer represented
+   *     by |opt_device| is loaded. To avoid crashing when initialize() is
+   *     called, |opt_device| should represent the printer that will be selected
+   *     when print preview is first opened, i.e. the system default
+   *     destination, or the most recently used destination or destination
+   *     selected by the rules string if these parameters are defined in
+   *     initialSettings.serializedAppStateStr_.
+   *     If |opt_device| is not provided, a default device with ID 'FooDevice'
+   *     will be used.
+   * @return {!Promise<print_preview.PrinterCapabilitiesResponse>} a
+   *     promise that will resolve when getPrinterCapabilities has been
+   *     called for the device (either default or provided).
    */
-  function setLocalDestinations() {
+  function setupSettingsAndDestinationsWithCapabilities(opt_device) {
+    nativeLayer.setInitialSettings(initialSettings);
     nativeLayer.setLocalDestinations(localDestinationInfos);
-    printPreview.destinationStore_.startLoadLocalDestinations();
-  }
+    opt_device = opt_device || getCddTemplate('FooDevice', 'FooName');
+    nativeLayer.setLocalDestinationCapabilities(opt_device);
 
-  /**
-   * Initializes print preview with the initial settings currently stored in
-   * |initialSettings|, waits for the getInitialSettings promise to resolve,
-   * and loads local destinations using destination infos currently stored in
-   * |localDestinationInfos|.
-   * @return {!Promise<!Array<!print_preview.LocalDestinationInfo>>} a
-   *     promise that will resolve when getPrinters has been resolved by
-   *     the native layer stub.
-   */
-  function setupSettingsAndDestinations() {
-    setInitialSettings();
+    printPreview.initialize();
     return nativeLayer.whenCalled('getInitialSettings').then(function() {
-      setLocalDestinations();
-      return nativeLayer.whenCalled('getPrinters');
+      printPreview.destinationStore_.startLoadLocalDestinations();
+      return Promise.all([
+        nativeLayer.whenCalled('getPrinters'),
+        nativeLayer.whenCalled('getPrinterCapabilities')
+      ]);
     });
   }
 
   /**
-   * Dispatch the CAPABILITIES_SET event. This call is NOT async and will
-   * happen in the same thread.
-   * @param {!Object} device The device whose capabilities should be dispatched.
-   */
-  function setCapabilities(device) {
-    var capsSetEvent =
-        new Event(print_preview.NativeLayer.EventType.CAPABILITIES_SET);
-    capsSetEvent.settingsInfo = device;
-    nativeLayer.getEventTarget().dispatchEvent(capsSetEvent);
-  }
-
-  /**
    * Verify that |section| visibility matches |visible|.
    * @param {HTMLDivElement} section The section to check.
    * @param {boolean} visible The expected state of visibility.
@@ -102,11 +97,13 @@
 
   /**
    * @param {string} printerId
-   * @return {!Object}
+   * @param {string=} opt_printerName Defaults to an empty string.
+   * @return {!print_preview.PrinterCapabilitiesResponse}
    */
-  function getCddTemplate(printerId) {
+  function getCddTemplate(printerId, opt_printerName) {
     return {
       printerId: printerId,
+      printerName: opt_printerName || '',
       capabilities: {
         version: '1.0',
         printer: {
@@ -215,7 +212,6 @@
    * and verifies it is displayed.
    */
   function startAdvancedSettingsTest(device) {
-    setCapabilities(device);
     expandMoreSettings();
 
     // Check that the advanced options settings section is visible.
@@ -285,7 +281,7 @@
 
     // Test some basic assumptions about the print preview WebUI.
     test('PrinterList', function() {
-      return setupSettingsAndDestinations().then(function() {
+      return setupSettingsAndDestinationsWithCapabilities().then(function() {
         var recentList =
             $('destination-search').querySelector('.recent-list ul');
         var localList =
@@ -315,7 +311,7 @@
     // Test that the printer list is structured correctly after calling
     // addCloudPrinters with an empty list.
     test('PrinterListCloudEmpty', function() {
-      return setupSettingsAndDestinations().then(function() {
+      return setupSettingsAndDestinationsWithCapabilities().then(function() {
         var cloudPrintEnableEvent = new Event(
             print_preview.NativeLayer.EventType.CLOUD_PRINT_ENABLE);
         cloudPrintEnableEvent.baseCloudPrintUrl = 'cloudprint url';
@@ -381,7 +377,7 @@
           },
         ],
       });
-
+      nativeLayer.setLocalDestinationCapabilities(getCddTemplate('ID'));
       setInitialSettings();
       return nativeLayer.whenCalled('getInitialSettings');
     });
@@ -397,7 +393,7 @@
             origin: origin,
             account: '',
             capabilities: 0,
-            name: '',
+            name: 'One',
             extensionId: '',
             extensionName: '',
           }, {
@@ -405,7 +401,7 @@
             origin: origin,
             account: '',
             capabilities: 0,
-            name: '',
+            name: 'Two',
             extensionId: '',
             extensionName: '',
           }, {
@@ -413,24 +409,28 @@
             origin: origin,
             account: '',
             capabilities: 0,
-            name: '',
+            name: 'Three',
             extensionId: '',
             extensionName: '',
           },
         ],
       });
+      // Set all three of these destinations in the local destination infos
+      // (represents currently available printers), plus an extra destination.
+      localDestinationInfos = [
+        { printerName: 'One', deviceName: 'ID1' },
+        { printerName: 'Two', deviceName: 'ID2' },
+        { printerName: 'Three', deviceName: 'ID3' },
+        { printerName: 'Four', deviceName: 'ID4' }
+      ];
 
-      setInitialSettings();
+      // Set up capabilities for ID1. This should be the device that should hav
+      // its capabilities fetched, since it is the most recent. If another
+      // device is selected the native layer will reject the callback.
+      var device = getCddTemplate('ID1', 'One');
 
-      return nativeLayer.whenCalled('getInitialSettings').then(
+      return setupSettingsAndDestinationsWithCapabilities(device).then(
           function() {
-            // Set capabilities for the three recently used destinations + 1
-            // more.
-            setCapabilities(getCddTemplate('ID1'));
-            setCapabilities(getCddTemplate('ID2'));
-            setCapabilities(getCddTemplate('ID3'));
-            setCapabilities(getCddTemplate('ID4'));
-
             // The most recently used destination should be the currently
             // selected one. This is ID1.
             assertEquals(
@@ -461,22 +461,17 @@
       // It also makes sure these rules do override system default destination.
       initialSettings.serializedDefaultDestinationSelectionRulesStr_ =
           JSON.stringify({namePattern: '.*Bar.*'});
-      // Set this early as the app state selection string will trigger a load
-      // of local destinations on initialization.
-      nativeLayer.setLocalDestinations(localDestinationInfos);
-      setInitialSettings();
-      return nativeLayer.whenCalled('getInitialSettings').then(function() {
-        return nativeLayer.whenCalled('getPrinters').then(function() {
-          assertEquals('BarDevice',
-                       printPreview.destinationStore_.selectedDestination.id);
-        });
-      });
+      return setupSettingsAndDestinationsWithCapabilities(
+          getCddTemplate('BarDevice', 'BarName')).then(function() {
+            assertEquals('BarDevice',
+                         printPreview.destinationStore_.selectedDestination.id);
+          });
     });
 
     test('SystemDialogLinkIsHiddenInAppKioskMode', function() {
       if (!cr.isChromeOS)
         initialSettings.isInAppKioskMode_ = true;
-
+      nativeLayer.setLocalDestinationCapabilities(getCddTemplate('FooDevice'));
       setInitialSettings();
       return nativeLayer.whenCalled('getInitialSettings').then(
           function() {
@@ -491,62 +486,65 @@
       checkSectionVisible($('layout-settings'), false);
       checkSectionVisible($('color-settings'), false);
       checkSectionVisible($('copies-settings'), false);
+      var device = getCddTemplate('FooDevice');
+      device.capabilities.printer.color = {
+        option: [{is_default: true, type: 'STANDARD_COLOR'}]
+      };
+      delete device.capabilities.printer.copies;
 
-      return setupSettingsAndDestinations().then(function() {
-        var device = getCddTemplate('FooDevice');
-        device.capabilities.printer.color = {
-          option: [{is_default: true, type: 'STANDARD_COLOR'}]
-        };
-        delete device.capabilities.printer.copies;
-        setCapabilities(device);
+      return setupSettingsAndDestinationsWithCapabilities(device)
+        .then(function() {
+          checkSectionVisible($('layout-settings'), true);
+          checkSectionVisible($('color-settings'), false);
+          checkSectionVisible($('copies-settings'), false);
 
-        checkSectionVisible($('layout-settings'), true);
-        checkSectionVisible($('color-settings'), false);
-        checkSectionVisible($('copies-settings'), false);
-
-        return whenAnimationDone('other-options-collapsible');
-      });
+          return whenAnimationDone('other-options-collapsible');
+        });
     });
 
     // When the source is 'PDF' and 'Save as PDF' option is selected, we hide
     // the fit to page option.
     test('PrintToPDFSelectedCapabilities', function() {
-      // Add PDF printer.
+      // Setup initial settings
       initialSettings.isDocumentModifiable_ = false;
       initialSettings.systemDefaultDestinationId_ = 'Save as PDF';
-      setInitialSettings();
 
-      return nativeLayer.whenCalled('getInitialSettings').then(function() {
-        var device = {
-          printerId: 'Save as PDF',
-          capabilities: {
-            version: '1.0',
-            printer: {
-              page_orientation: {
-                option: [
-                  {type: 'AUTO', is_default: true},
-                  {type: 'PORTRAIT'},
-                  {type: 'LANDSCAPE'}
-                ]
-              },
-              color: {
-                option: [
-                  {type: 'STANDARD_COLOR', is_default: true}
-                ]
-              },
-              media_size: {
-                option: [
-                  { name: 'NA_LETTER',
-                    width_microns: 0,
-                    height_microns: 0,
-                    is_default: true
-                  }
-                ]
-              }
+      // Set PDF printer
+      var device = {
+        printerId: 'Save as PDF',
+        capabilities: {
+          version: '1.0',
+          printer: {
+            page_orientation: {
+              option: [
+                {type: 'AUTO', is_default: true},
+                {type: 'PORTRAIT'},
+                {type: 'LANDSCAPE'}
+              ]
+            },
+            color: {
+              option: [
+                {type: 'STANDARD_COLOR', is_default: true}
+              ]
+            },
+            media_size: {
+              option: [
+                { name: 'NA_LETTER',
+                  width_microns: 0,
+                  height_microns: 0,
+                  is_default: true
+                }
+              ]
             }
           }
-        };
-        setCapabilities(device);
+        }
+      };
+      nativeLayer.setLocalDestinationCapabilities(device);
+
+      setInitialSettings();
+      return nativeLayer.whenCalled('getInitialSettings').then(function() {
+        return nativeLayer.whenCalled('getPrinterCapabilities');
+      }).then(function() {
         var otherOptions = $('other-options-settings');
         // If rasterization is an option, other options should be visible.
         // If not, there should be no available other options.
@@ -565,9 +563,7 @@
     // When the source is 'HTML', we always hide the fit to page option and show
     // media size option.
     test('SourceIsHTMLCapabilities', function() {
-      return setupSettingsAndDestinations().then(function() {
-        setCapabilities(getCddTemplate('FooDevice'));
-
+      return setupSettingsAndDestinationsWithCapabilities().then(function() {
         var otherOptions = $('other-options-settings');
         var fitToPage = otherOptions.querySelector('#fit-to-page-container');
         var rasterize;
@@ -601,9 +597,7 @@
     // we show/hide the fit to page option and hide media size selection.
     test('SourceIsPDFCapabilities', function() {
       initialSettings.isDocumentModifiable_ = false;
-      return setupSettingsAndDestinations().then(function() {
-        setCapabilities(getCddTemplate('FooDevice'));
-
+      return setupSettingsAndDestinationsWithCapabilities().then(function() {
         var otherOptions = $('other-options-settings');
         var scalingSettings = $('scaling-settings');
         var fitToPageContainer =
@@ -637,9 +631,7 @@
     // we show/hide the fit to page option and hide media size selection.
     test('ScalingUnchecksFitToPage', function() {
       initialSettings.isDocumentModifiable_ = false;
-      return setupSettingsAndDestinations().then(function() {
-        setCapabilities(getCddTemplate('FooDevice'));
-
+      return setupSettingsAndDestinationsWithCapabilities().then(function() {
         var otherOptions = $('other-options-settings');
         var scalingSettings = $('scaling-settings');
 
@@ -675,9 +667,7 @@
     // the copies value if capability is supported by printer.
     test('CheckNumCopiesPrintPreset', function() {
       initialSettings.isDocumentModifiable_ = false;
-      return setupSettingsAndDestinations().then(function() {
-        setCapabilities(getCddTemplate('FooDevice'));
-
+      return setupSettingsAndDestinationsWithCapabilities().then(function() {
         // Indicate that the number of copies print preset is set for source
         // PDF.
         var printPresetOptions = {
@@ -704,9 +694,7 @@
     // duplex setting if capability is supported by printer.
     test('CheckDuplexPrintPreset', function() {
       initialSettings.isDocumentModifiable_ = false;
-      return setupSettingsAndDestinations().then(function() {
-        setCapabilities(getCddTemplate('FooDevice'));
-
+      return setupSettingsAndDestinationsWithCapabilities().then(function() {
         // Indicate that the duplex print preset is set to 'long edge' for
         // source PDF.
         var printPresetOptions = {
@@ -731,9 +719,7 @@
 
     // Make sure that custom margins controls are properly set up.
     test('CustomMarginsControlsCheck', function() {
-      return setupSettingsAndDestinations().then(function() {
-        setCapabilities(getCddTemplate('FooDevice'));
-
+      return setupSettingsAndDestinationsWithCapabilities().then(function() {
         printPreview.printTicketStore_.marginsType.updateValue(
             print_preview.ticket_items.MarginsTypeValue.CUSTOM);
 
@@ -751,9 +737,7 @@
 
     // Page layout has zero margins. Hide header and footer option.
     test('PageLayoutHasNoMarginsHideHeaderFooter', function() {
-      return setupSettingsAndDestinations().then(function() {
-        setCapabilities(getCddTemplate('FooDevice'));
-
+      return setupSettingsAndDestinationsWithCapabilities().then(function() {
         var otherOptions = $('other-options-settings');
         var headerFooter =
             otherOptions.querySelector('#header-footer-container');
@@ -780,9 +764,7 @@
 
     // Page layout has half-inch margins. Show header and footer option.
     test('PageLayoutHasMarginsShowHeaderFooter', function() {
-      return setupSettingsAndDestinations().then(function() {
-        setCapabilities(getCddTemplate('FooDevice'));
-
+      return setupSettingsAndDestinationsWithCapabilities().then(function() {
         var otherOptions = $('other-options-settings');
         var headerFooter =
             otherOptions.querySelector('#header-footer-container');
@@ -810,9 +792,7 @@
     // Page layout has zero top and bottom margins. Hide header and footer
     // option.
     test('ZeroTopAndBottomMarginsHideHeaderFooter', function() {
-      return setupSettingsAndDestinations().then(function() {
-        setCapabilities(getCddTemplate('FooDevice'));
-
+      return setupSettingsAndDestinationsWithCapabilities().then(function() {
         var otherOptions = $('other-options-settings');
         var headerFooter =
             otherOptions.querySelector('#header-footer-container');
@@ -840,9 +820,7 @@
     // Page layout has zero top and half-inch bottom margin. Show header and
     // footer option.
     test('ZeroTopAndNonZeroBottomMarginShowHeaderFooter', function() {
-      return setupSettingsAndDestinations().then(function() {
-        setCapabilities(getCddTemplate('FooDevice'));
-
+      return setupSettingsAndDestinationsWithCapabilities().then(function() {
         var otherOptions = $('other-options-settings');
         var headerFooter =
             otherOptions.querySelector('#header-footer-container');
@@ -869,18 +847,17 @@
 
     // Check header footer availability with small (label) page size.
     test('SmallPaperSizeHeaderFooter', function() {
-      return setupSettingsAndDestinations().then(function() {
-        var device = getCddTemplate('FooDevice');
-        device.capabilities.printer.media_size = {
-          'option': [
-            {'name': 'SmallLabel', 'width_microns': 38100,
-              'height_microns': 12700, 'is_default': false},
-            {'name': 'BigLabel', 'width_microns': 50800,
-              'height_microns': 76200, 'is_default': true}
-          ]
-        };
-        setCapabilities(device);
-
+      var device = getCddTemplate('FooDevice');
+      device.capabilities.printer.media_size = {
+        'option': [
+          {'name': 'SmallLabel', 'width_microns': 38100,
+            'height_microns': 12700, 'is_default': false},
+          {'name': 'BigLabel', 'width_microns': 50800,
+            'height_microns': 76200, 'is_default': true}
+        ]
+      };
+      return setupSettingsAndDestinationsWithCapabilities(device)
+          .then(function() {
         var otherOptions = $('other-options-settings');
         var headerFooter =
             otherOptions.querySelector('#header-footer-container');
@@ -911,16 +888,16 @@
 
     // Test that the color settings, one option, standard monochrome.
     test('ColorSettingsMonochrome', function() {
-      return setupSettingsAndDestinations().then(function() {
-        // Only one option, standard monochrome.
-        var device = getCddTemplate('FooDevice');
-        device.capabilities.printer.color = {
-          'option': [
-            {'is_default': true, 'type': 'STANDARD_MONOCHROME'}
-          ]
-        };
-        setCapabilities(device);
+      // Only one option, standard monochrome.
+      var device = getCddTemplate('FooDevice');
+      device.capabilities.printer.color = {
+        'option': [
+          {'is_default': true, 'type': 'STANDARD_MONOCHROME'}
+        ]
+      };
 
+      return setupSettingsAndDestinationsWithCapabilities(device)
+          .then(function() {
         checkSectionVisible($('color-settings'), false);
 
         return whenAnimationDone('more-settings');
@@ -929,17 +906,17 @@
 
     // Test that the color settings, one option, custom monochrome.
     test('ColorSettingsCustomMonochrome', function() {
-      return setupSettingsAndDestinations().then(function() {
-        // Only one option, standard monochrome.
-        var device = getCddTemplate('FooDevice');
-        device.capabilities.printer.color = {
-          'option': [
-            {'is_default': true, 'type': 'CUSTOM_MONOCHROME',
-             'vendor_id': '42'}
-          ]
-        };
-        setCapabilities(device);
+      // Only one option, standard monochrome.
+      var device = getCddTemplate('FooDevice');
+      device.capabilities.printer.color = {
+        'option': [
+          {'is_default': true, 'type': 'CUSTOM_MONOCHROME',
+           'vendor_id': '42'}
+        ]
+      };
 
+      return setupSettingsAndDestinationsWithCapabilities(device)
+          .then(function() {
         checkSectionVisible($('color-settings'), false);
 
         return whenAnimationDone('more-settings');
@@ -948,15 +925,15 @@
 
     // Test that the color settings, one option, standard color.
     test('ColorSettingsColor', function() {
-      return setupSettingsAndDestinations().then(function() {
-        var device = getCddTemplate('FooDevice');
-        device.capabilities.printer.color = {
-          'option': [
-            {'is_default': true, 'type': 'STANDARD_COLOR'}
-          ]
-        };
-        setCapabilities(device);
+      var device = getCddTemplate('FooDevice');
+      device.capabilities.printer.color = {
+        'option': [
+          {'is_default': true, 'type': 'STANDARD_COLOR'}
+        ]
+      };
 
+      return setupSettingsAndDestinationsWithCapabilities(device)
+          .then(function() {
         checkSectionVisible($('color-settings'), false);
 
         return whenAnimationDone('more-settings');
@@ -965,15 +942,14 @@
 
     // Test that the color settings, one option, custom color.
     test('ColorSettingsCustomColor', function() {
-      return setupSettingsAndDestinations().then(function() {
-        var device = getCddTemplate('FooDevice');
-        device.capabilities.printer.color = {
-          'option': [
-            {'is_default': true, 'type': 'CUSTOM_COLOR', 'vendor_id': '42'}
-          ]
-        };
-        setCapabilities(device);
-
+      var device = getCddTemplate('FooDevice');
+      device.capabilities.printer.color = {
+        'option': [
+          {'is_default': true, 'type': 'CUSTOM_COLOR', 'vendor_id': '42'}
+        ]
+      };
+      return setupSettingsAndDestinationsWithCapabilities(device)
+          .then(function() {
         checkSectionVisible($('color-settings'), false);
 
         return whenAnimationDone('more-settings');
@@ -983,16 +959,15 @@
     // Test that the color settings, two options, both standard, defaults to
     // color.
     test('ColorSettingsBothStandardDefaultColor', function() {
-      return setupSettingsAndDestinations().then(function() {
-        var device = getCddTemplate('FooDevice');
-        device.capabilities.printer.color = {
-          'option': [
-            {'type': 'STANDARD_MONOCHROME'},
-            {'is_default': true, 'type': 'STANDARD_COLOR'}
-          ]
-        };
-        setCapabilities(device);
-
+      var device = getCddTemplate('FooDevice');
+      device.capabilities.printer.color = {
+        'option': [
+          {'type': 'STANDARD_MONOCHROME'},
+          {'is_default': true, 'type': 'STANDARD_COLOR'}
+        ]
+      };
+      return setupSettingsAndDestinationsWithCapabilities(device)
+          .then(function() {
         checkSectionVisible($('color-settings'), true);
         expectEquals(
             'color',
@@ -1006,16 +981,15 @@
     // Test that the color settings, two options, both standard, defaults to
     // monochrome.
     test('ColorSettingsBothStandardDefaultMonochrome', function() {
-      return setupSettingsAndDestinations().then(function() {
-        var device = getCddTemplate('FooDevice');
-        device.capabilities.printer.color = {
-          'option': [
-            {'is_default': true, 'type': 'STANDARD_MONOCHROME'},
-            {'type': 'STANDARD_COLOR'}
-          ]
-        };
-        setCapabilities(device);
-
+      var device = getCddTemplate('FooDevice');
+      device.capabilities.printer.color = {
+        'option': [
+          {'is_default': true, 'type': 'STANDARD_MONOCHROME'},
+          {'type': 'STANDARD_COLOR'}
+        ]
+      };
+      return setupSettingsAndDestinationsWithCapabilities(device)
+          .then(function() {
         checkSectionVisible($('color-settings'), true);
         expectEquals(
             'bw',
@@ -1029,16 +1003,15 @@
     // Test that the color settings, two options, both custom, defaults to
     // color.
     test('ColorSettingsBothCustomDefaultColor', function() {
-      return setupSettingsAndDestinations().then(function() {
-        var device = getCddTemplate('FooDevice');
-        device.capabilities.printer.color = {
-          'option': [
-            {'type': 'CUSTOM_MONOCHROME', 'vendor_id': '42'},
-            {'is_default': true, 'type': 'CUSTOM_COLOR', 'vendor_id': '43'}
-          ]
-        };
-        setCapabilities(device);
-
+      var device = getCddTemplate('FooDevice');
+      device.capabilities.printer.color = {
+        'option': [
+          {'type': 'CUSTOM_MONOCHROME', 'vendor_id': '42'},
+          {'is_default': true, 'type': 'CUSTOM_COLOR', 'vendor_id': '43'}
+        ]
+      };
+      return setupSettingsAndDestinationsWithCapabilities(device)
+          .then(function() {
         checkSectionVisible($('color-settings'), true);
         expectEquals(
             'color',
@@ -1052,9 +1025,7 @@
     // Test to verify that duplex settings are set according to the printer
     // capabilities.
     test('DuplexSettingsTrue', function() {
-      return setupSettingsAndDestinations().then(function() {
-        setCapabilities(getCddTemplate('FooDevice'));
-
+      return setupSettingsAndDestinationsWithCapabilities().then(function() {
         var otherOptions = $('other-options-settings');
         checkSectionVisible(otherOptions, true);
         duplexContainer = otherOptions.querySelector('#duplex-container');
@@ -1068,11 +1039,10 @@
     // Test to verify that duplex settings are set according to the printer
     // capabilities.
     test('DuplexSettingsFalse', function() {
-      return setupSettingsAndDestinations().then(function() {
-        var device = getCddTemplate('FooDevice');
-        delete device.capabilities.printer.duplex;
-        setCapabilities(device);
-
+      var device = getCddTemplate('FooDevice');
+      delete device.capabilities.printer.duplex;
+      return setupSettingsAndDestinationsWithCapabilities(device)
+          .then(function() {
         // Check that it is collapsed.
         var otherOptions = $('other-options-settings');
         checkSectionVisible(otherOptions, false);
@@ -1089,9 +1059,7 @@
 
     // Test that changing the selected printer updates the preview.
     test('PrinterChangeUpdatesPreview', function() {
-      return setupSettingsAndDestinations().then(function() {
-        setCapabilities(getCddTemplate('FooDevice'));
-
+      return setupSettingsAndDestinationsWithCapabilities().then(function() {
         var previewGenerator = mock(print_preview.PreviewGenerator);
         previewArea.previewGenerator_ = previewGenerator.proxy();
 
@@ -1099,28 +1067,31 @@
         // destination that will therefore dispatch ticket item change events.
         previewGenerator.expects(exactly(9)).requestPreview();
 
-        var barDestination =
-            printPreview.destinationStore_.destinations().find(
-                function(d) {
-                  return d.id == 'BarDevice';
-                });
-
-        printPreview.destinationStore_.selectDestination(barDestination);
-
+        // Setup capabilities for BarDevice.
         var device = getCddTemplate('BarDevice');
         device.capabilities.printer.color = {
           'option': [
             {'is_default': true, 'type': 'STANDARD_MONOCHROME'}
           ]
         };
-        setCapabilities(device);
+        nativeLayer.setLocalDestinationCapabilities(device);
 
+        // Select BarDevice
+        var barDestination =
+            printPreview.destinationStore_.destinations().find(
+                function(d) {
+                  return d.id == 'BarDevice';
+                });
+        printPreview.destinationStore_.selectDestination(barDestination);
+        return nativeLayer.whenCalled('getPrinterCapabilities', 'BarDevice');
+      }).then(function(){
         return whenAnimationDone('more-settings');
       });
     });
 
     // Test that error message is displayed when plugin doesn't exist.
     test('NoPDFPluginErrorMessage', function() {
+      nativeLayer.setLocalDestinationCapabilities(getCddTemplate('FooDevice'));
       setInitialSettings();
       return nativeLayer.whenCalled('getInitialSettings').then(function() {
         var previewAreaEl = $('preview-area');
@@ -1148,33 +1119,32 @@
 
     // Test custom localized paper names.
     test('CustomPaperNames', function() {
-      return setupSettingsAndDestinations().then(function() {
-        var customLocalizedMediaName = 'Vendor defined localized media name';
-        var customMediaName = 'Vendor defined media name';
+      var customLocalizedMediaName = 'Vendor defined localized media name';
+      var customMediaName = 'Vendor defined media name';
 
-        var device = getCddTemplate('FooDevice');
-        device.capabilities.printer.media_size = {
-          option: [
-            { name: 'CUSTOM',
-              width_microns: 15900,
-              height_microns: 79400,
-              is_default: true,
-              custom_display_name_localized: [
-                { locale: navigator.language,
-                  value: customLocalizedMediaName
-                }
-              ]
-            },
-            { name: 'CUSTOM',
-              width_microns: 15900,
-              height_microns: 79400,
-              custom_display_name: customMediaName
-            }
-          ]
-        };
+      var device = getCddTemplate('FooDevice');
+      device.capabilities.printer.media_size = {
+        option: [
+          { name: 'CUSTOM',
+            width_microns: 15900,
+            height_microns: 79400,
+            is_default: true,
+            custom_display_name_localized: [
+              { locale: navigator.language,
+                value: customLocalizedMediaName
+              }
+            ]
+          },
+          { name: 'CUSTOM',
+            width_microns: 15900,
+            height_microns: 79400,
+            custom_display_name: customMediaName
+          }
+        ]
+      };
 
-        setCapabilities(device);
-
+      return setupSettingsAndDestinationsWithCapabilities(device)
+          .then(function() {
         expandMoreSettings();
 
         checkSectionVisible($('media-size-settings'), true);
@@ -1197,7 +1167,8 @@
     // search box).
     test('AdvancedSettings1Option', function() {
       var device = getCddTemplateWithAdvancedSettings('FooDevice');
-      return setupSettingsAndDestinations().then(function() {
+      return setupSettingsAndDestinationsWithCapabilities(device)
+          .then(function() {
         startAdvancedSettingsTest(device);
         checkElementDisplayed($('advanced-settings').
             querySelector('.search-box-area'), false);
@@ -1224,7 +1195,8 @@
               ]
           }
       });
-      return setupSettingsAndDestinations().then(function() {
+      return setupSettingsAndDestinationsWithCapabilities(device)
+          .then(function() {
         startAdvancedSettingsTest(device);
 
         checkElementDisplayed($('advanced-settings').
@@ -1249,9 +1221,11 @@
           };
         }),
       });
-      setCapabilities(getCddTemplate('ID1'));
-      setCapabilities(getCddTemplate('ID2'));
-      setCapabilities(getCddTemplate('ID3'));
+
+      // Ensure all capabilities are available for fetch.
+      nativeLayer.setLocalDestinationCapabilities(getCddTemplate('ID1'));
+      nativeLayer.setLocalDestinationCapabilities(getCddTemplate('ID2'))
+      nativeLayer.setLocalDestinationCapabilities(getCddTemplate('ID3'));
 
       // Use a real preview generator.
       previewArea.previewGenerator_ =
@@ -1266,6 +1240,8 @@
       expectEquals(-1, previewArea.previewGenerator_.inFlightRequestId_);
       setInitialSettings();
       return nativeLayer.whenCalled('getInitialSettings').then(function() {
+        return nativeLayer.whenCalled('getPrinterCapabilities', 'ID1');
+      }).then(function() {
         expectEquals(0, previewArea.previewGenerator_.inFlightRequestId_);
       });
     });
@@ -1274,9 +1250,7 @@
     // an error and that the preview dialog can be recovered by selecting a
     // new destination.
     test('InvalidSettingsError', function() {
-      return setupSettingsAndDestinations().then(function() {
-        setCapabilities(getCddTemplate('FooDevice'));
-
+      return setupSettingsAndDestinationsWithCapabilities().then(function() {
         // Manually enable the print header. This is needed since there is no
         // plugin during test, so it will be set as disabled normally.
         printPreview.printHeader_.isEnabled = true;
@@ -1319,21 +1293,24 @@
                   return d.id == 'BarDevice';
                 });
 
+        nativeLayer.setLocalDestinationCapabilities(
+            getCddTemplate('BarDevice'));
         printPreview.destinationStore_.selectDestination(barDestination);
 
-        // Dispatch events indicating capabilities were fetched and new
-        // preview has loaded.
-        setCapabilities(getCddTemplate('BarDevice'));
-        var previewDoneEvent = new Event(
-            print_preview.PreviewArea.EventType.PREVIEW_GENERATION_DONE);
-        previewArea.dispatchEvent(previewDoneEvent);
+        return nativeLayer.whenCalled('getPrinterCapabilities', 'BarDevice')
+            .then(function() {
+              // Dispatch event indicating new preview has loaded.
+              var previewDoneEvent = new Event(
+                  print_preview.PreviewArea.EventType.PREVIEW_GENERATION_DONE);
+              previewArea.dispatchEvent(previewDoneEvent);
 
-        // Has active print button and successfully 'prints', indicating
-        // recovery from error state.
-        expectFalse(printButton.disabled);
-        expectFalse(nativeLayer.isPrintStarted());
-        printButton.click();
-        expectTrue(nativeLayer.isPrintStarted());
+              // Has active print button and successfully 'prints', indicating
+              // recovery from error state.
+              expectFalse(printButton.disabled);
+              expectFalse(nativeLayer.isPrintStarted());
+              printButton.click();
+              expectTrue(nativeLayer.isPrintStarted());
+            });
       });
     });
 
@@ -1345,9 +1322,7 @@
           new print_preview.PreviewGenerator(printPreview.destinationStore_,
               printPreview.printTicketStore_, nativeLayer,
               printPreview.documentInfo_);
-      return setupSettingsAndDestinations().then(function() {
-        setCapabilities(getCddTemplate('FooDevice'));
-
+      return setupSettingsAndDestinationsWithCapabilities().then(function() {
         // The first request should generate draft because there was no
         // previous print preview draft.
         expectTrue(nativeLayer.generateDraft());
diff --git a/chrome/test/data/webui/settings/cr_settings_interactive_ui_tests.js b/chrome/test/data/webui/settings/cr_settings_interactive_ui_tests.js
index 9f4b7e3..fac6e14 100644
--- a/chrome/test/data/webui/settings/cr_settings_interactive_ui_tests.js
+++ b/chrome/test/data/webui/settings/cr_settings_interactive_ui_tests.js
@@ -67,6 +67,30 @@
 
 
 /**
+ * Test fixture for Sync Page.
+ * @constructor
+ * @extends {CrSettingsInteractiveUITest}
+ */
+function CrSettingsSyncPageTest() {}
+
+CrSettingsSyncPageTest.prototype = {
+  __proto__: CrSettingsInteractiveUITest.prototype,
+
+  /** @override */
+  browsePreload: 'chrome://md-settings/people_page/sync_page.html',
+
+  /** @override */
+  extraLibraries: CrSettingsInteractiveUITest.prototype.extraLibraries.concat([
+    'people_page_sync_page_interactive_test.js',
+  ]),
+};
+
+TEST_F('CrSettingsSyncPageTest', 'All', function() {
+  mocha.run();
+});
+
+
+/**
  * @constructor
  * @extends {CrSettingsInteractiveUITest}
  */
diff --git a/chrome/test/data/webui/settings/people_page_sync_page_interactive_test.js b/chrome/test/data/webui/settings/people_page_sync_page_interactive_test.js
new file mode 100644
index 0000000..d2c42083
--- /dev/null
+++ b/chrome/test/data/webui/settings/people_page_sync_page_interactive_test.js
@@ -0,0 +1,31 @@
+// 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.
+
+suite('sync-page-test', function() {
+  /** @type {SyncPageElement} */ var testElement;
+
+  setup(function() {
+    PolymerTest.clearBody();
+
+    testElement = document.createElement('settings-sync-page');
+    document.body.appendChild(testElement);
+  });
+
+  test('autofocus correctly after container is shown', function() {
+    cr.webUIListenerCallback('sync-prefs-changed', {passphraseRequired: true});
+    Polymer.dom.flush();
+
+    var input = testElement.$$('#existingPassphraseInput');
+
+    var focused = false;
+    input.addEventListener('focus', function() {
+      focused = true;
+    });
+
+    // Simulate event normally fired by main_page_behavior after subpage
+    // animation ends.
+    testElement.fire('show-container');
+    assertTrue(focused);
+  });
+});
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index e921e08..5912703 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-9641.0.0
\ No newline at end of file
+9649.0.0
\ No newline at end of file
diff --git a/chromeos/dbus/services/proxy_resolution_service_provider.cc b/chromeos/dbus/services/proxy_resolution_service_provider.cc
index a5f6a78..54807c3 100644
--- a/chromeos/dbus/services/proxy_resolution_service_provider.cc
+++ b/chromeos/dbus/services/proxy_resolution_service_provider.cc
@@ -26,7 +26,6 @@
 
 struct ProxyResolutionServiceProvider::Request {
  public:
-  // Constructor for returning proxy info via an asynchronous D-Bus response.
   Request(const std::string& source_url,
           std::unique_ptr<dbus::Response> response,
           const dbus::ExportedObject::ResponseSender& response_sender,
@@ -38,38 +37,15 @@
     DCHECK(this->response);
     DCHECK(!response_sender.is_null());
   }
-
-  // Constructor for returning proxy info via a D-Bus signal.
-  Request(const std::string& source_url,
-          const std::string& signal_interface,
-          const std::string& signal_name,
-          scoped_refptr<net::URLRequestContextGetter> context_getter)
-      : source_url(source_url),
-        signal_interface(signal_interface),
-        signal_name(signal_name),
-        context_getter(context_getter) {
-    DCHECK(!signal_interface.empty());
-    DCHECK(!signal_name.empty());
-  }
-
   ~Request() = default;
 
   // URL being resolved.
   const std::string source_url;
 
   // D-Bus response and callback for returning data on resolution completion.
-  // Either these two members or |signal_interface|/|signal_name| must be
-  // supplied, but not both.
   std::unique_ptr<dbus::Response> response;
   const dbus::ExportedObject::ResponseSender response_sender;
 
-  // D-Bus interface and name for emitting result signal after resolution is
-  // complete.
-  // TODO(derat): Remove these and associated code after all callers use async
-  // responses instead of signals: http://crbug.com/446115
-  const std::string signal_interface;
-  const std::string signal_name;
-
   // Used to get the network context associated with the profile used to run
   // this request.
   const scoped_refptr<net::URLRequestContextGetter> context_getter;
@@ -85,12 +61,8 @@
 };
 
 ProxyResolutionServiceProvider::ProxyResolutionServiceProvider(
-    const std::string& dbus_interface,
-    const std::string& dbus_method_name,
     std::unique_ptr<Delegate> delegate)
-    : dbus_interface_(dbus_interface),
-      dbus_method_name_(dbus_method_name),
-      delegate_(std::move(delegate)),
+    : delegate_(std::move(delegate)),
       origin_thread_(base::ThreadTaskRunnerHandle::Get()),
       weak_ptr_factory_(this) {}
 
@@ -104,7 +76,7 @@
   exported_object_ = exported_object;
   VLOG(1) << "ProxyResolutionServiceProvider started";
   exported_object_->ExportMethod(
-      dbus_interface_, dbus_method_name_,
+      kNetworkProxyServiceInterface, kNetworkProxyServiceResolveProxyMethod,
       base::Bind(&ProxyResolutionServiceProvider::ResolveProxy,
                  weak_ptr_factory_.GetWeakPtr()),
       base::Bind(&ProxyResolutionServiceProvider::OnExported,
@@ -140,33 +112,13 @@
     return;
   }
 
-  // The signal interface and name arguments are optional but must be supplied
-  // together.
-  std::string signal_interface, signal_name;
-  if (reader.HasMoreData() &&
-      (!reader.PopString(&signal_interface) || signal_interface.empty() ||
-       !reader.PopString(&signal_name) || signal_name.empty())) {
-    LOG(ERROR) << "Method call has invalid interface/name args: "
-               << method_call->ToString();
-    response_sender.Run(dbus::ErrorResponse::FromMethodCall(
-        method_call, DBUS_ERROR_INVALID_ARGS, "Invalid interface/name args"));
-    return;
-  }
-
   std::unique_ptr<dbus::Response> response =
       dbus::Response::FromMethodCall(method_call);
   scoped_refptr<net::URLRequestContextGetter> context_getter =
       delegate_->GetRequestContext();
 
-  // If signal information was supplied, emit a signal instead of including
-  // proxy information in the response.
-  std::unique_ptr<Request> request =
-      !signal_interface.empty()
-          ? base::MakeUnique<Request>(source_url, signal_interface, signal_name,
-                                      context_getter)
-          : base::MakeUnique<Request>(source_url, std::move(response),
-                                      response_sender, context_getter);
-
+  std::unique_ptr<Request> request = base::MakeUnique<Request>(
+      source_url, std::move(response), response_sender, context_getter);
   NotifyCallback notify_callback =
       base::Bind(&ProxyResolutionServiceProvider::NotifyProxyResolved,
                  weak_ptr_factory_.GetWeakPtr());
@@ -180,11 +132,6 @@
       base::Bind(&ProxyResolutionServiceProvider::ResolveProxyOnNetworkThread,
                  base::Passed(std::move(request)), origin_thread_,
                  notify_callback));
-
-  // If we didn't already pass the response to the Request object because we're
-  // returning data via a signal, send an empty response immediately.
-  if (response)
-    response_sender.Run(std::move(response));
 }
 
 // static
@@ -240,22 +187,11 @@
     std::unique_ptr<Request> request) {
   DCHECK(OnOriginThread());
 
-  if (request->response) {
-    // Reply to the original D-Bus method call.
-    dbus::MessageWriter writer(request->response.get());
-    writer.AppendString(request->proxy_info.ToPacString());
-    writer.AppendString(request->error);
-    request->response_sender.Run(std::move(request->response));
-  } else {
-    // Send a signal to the client.
-    dbus::Signal signal(request->signal_interface, request->signal_name);
-    dbus::MessageWriter writer(&signal);
-    writer.AppendString(request->source_url);
-    writer.AppendString(request->proxy_info.ToPacString());
-    writer.AppendString(request->error);
-    exported_object_->SendSignal(&signal);
-    VLOG(1) << "Sending signal: " << signal.ToString();
-  }
+  // Reply to the original D-Bus method call.
+  dbus::MessageWriter writer(request->response.get());
+  writer.AppendString(request->proxy_info.ToPacString());
+  writer.AppendString(request->error);
+  request->response_sender.Run(std::move(request->response));
 }
 
 }  // namespace chromeos
diff --git a/chromeos/dbus/services/proxy_resolution_service_provider.h b/chromeos/dbus/services/proxy_resolution_service_provider.h
index 9c750d77..0e3ebdc 100644
--- a/chromeos/dbus/services/proxy_resolution_service_provider.h
+++ b/chromeos/dbus/services/proxy_resolution_service_provider.h
@@ -30,30 +30,18 @@
 
 namespace chromeos {
 
-// This class provides proxy resolution service for CrosDBusService.
-// It processes proxy resolution requests for ChromeOS clients.
+// This class processes proxy resolution requests for Chrome OS clients.
 //
 // The following method is exported:
 //
-// Interface: org.chromium.LibCrosServiceInterface (kLibCrosServiceInterface)
-// Method: ResolveNetworkProxy (kResolveNetworkProxy)
+// Interface: org.chromium.NetworkProxyServiceInterface
+//            (kNetworkProxyServiceInterface)
+// Method: ResolveProxy (kNetworkProxyServiceResolveProxyMethod)
 // Parameters: string:source_url
-//             string:signal_interface (optional)
-//             string:signal_name (optional)
 //
-//   Resolves the proxy for |source_url|. If |signal_interface| and
-//   |signal_name| are supplied, returns an empty reply immediately and
-//   asynchronously emits a D-Bus signal to the requested destination.
-//   Otherwise, returns proxy information an asynchronous response without
-//   emitting a signal.
+//   Resolves the proxy for |source_url| and returns proxy information via an
+//   asynchronous response containing two values:
 //
-//   The signal (if requested) will contain three values:
-//   - string:source_url - requested source URL.
-//   - string:proxy_info - proxy info for the source URL in PAC format
-//                         like "PROXY cache.example.com:12345"
-//   - string:error_message - error message. Empty if successful.
-//
-//   The method call response (if requested) will contain just two values:
 //   - string:proxy_info - proxy info for the source URL in PAC format
 //                         like "PROXY cache.example.com:12345"
 //   - string:error_message - error message. Empty if successful.
@@ -61,9 +49,9 @@
 // This service can be manually tested using dbus-send:
 //
 //   % dbus-send --system --type=method_call --print-reply
-//       --dest=org.chromium.LibCrosService
-//       /org/chromium/LibCrosService
-//       org.chromium.LibCrosServiceInterface.ResolveNetworkProxy
+//       --dest=org.chromium.NetworkProxyService
+//       /org/chromium/NetworkProxyService
+//       org.chromium.NetworkProxyServiceInterface.ResolveProxy
 //       string:https://www.google.com/
 //
 class CHROMEOS_EXPORT ProxyResolutionServiceProvider
@@ -80,9 +68,7 @@
     virtual scoped_refptr<net::URLRequestContextGetter> GetRequestContext() = 0;
   };
 
-  ProxyResolutionServiceProvider(const std::string& dbus_interface,
-                                 const std::string& dbus_method_name,
-                                 std::unique_ptr<Delegate> delegate);
+  explicit ProxyResolutionServiceProvider(std::unique_ptr<Delegate> delegate);
   ~ProxyResolutionServiceProvider() override;
 
   // CrosDBusService::ServiceProviderInterface:
@@ -131,8 +117,6 @@
   // information to the client over D-Bus.
   void NotifyProxyResolved(std::unique_ptr<Request> request);
 
-  const std::string dbus_interface_;
-  const std::string dbus_method_name_;
   std::unique_ptr<Delegate> delegate_;
   scoped_refptr<dbus::ExportedObject> exported_object_;
   scoped_refptr<base::SingleThreadTaskRunner> origin_thread_;
diff --git a/chromeos/dbus/services/proxy_resolution_service_provider_unittest.cc b/chromeos/dbus/services/proxy_resolution_service_provider_unittest.cc
index fbb4345..b14be97e 100644
--- a/chromeos/dbus/services/proxy_resolution_service_provider_unittest.cc
+++ b/chromeos/dbus/services/proxy_resolution_service_provider_unittest.cc
@@ -31,14 +31,6 @@
 
 namespace {
 
-// ProxyResolutionServiceProvider will return the proxy info as a D-Bus
-// signal, to the following signal interface and the signal name.
-const char kReturnSignalInterface[] = "org.chromium.TestInterface";
-const char kReturnSignalName[] = "TestSignal";
-
-// Maximum time to wait for D-Bus signals to be received, in seconds.
-int kSignalTimeoutSec = 60;
-
 // Runs pending, non-delayed tasks on |task_runner|. Note that delayed tasks or
 // additional tasks posted by pending tests will not be run.
 void RunPendingTasks(scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
@@ -196,7 +188,6 @@
     proxy_resolver_ =
         base::MakeUnique<TestProxyResolver>(network_thread_.task_runner());
     service_provider_ = base::MakeUnique<ProxyResolutionServiceProvider>(
-        kNetworkProxyServiceInterface, kNetworkProxyServiceResolveProxyMethod,
         base::MakeUnique<TestDelegate>(network_thread_.task_runner(),
                                        proxy_resolver_.get()));
     test_helper_.SetUp(
@@ -217,94 +208,19 @@
   }
 
  protected:
-  // Arguments extracted from a D-Bus signal.
-  struct SignalInfo {
-    std::string source_url;
-    std::string proxy_info;
-    std::string error_message;
-  };
-
-  // Called when a signal is received.
-  void OnSignalReceived(dbus::Signal* signal) {
-    EXPECT_EQ(kReturnSignalInterface, signal->GetInterface());
-    EXPECT_EQ(kReturnSignalName, signal->GetMember());
-
-    ASSERT_FALSE(signal_);
-    signal_ = base::MakeUnique<SignalInfo>();
-
-    // The signal should contain three strings.
-    dbus::MessageReader reader(signal);
-    EXPECT_TRUE(reader.PopString(&signal_->source_url));
-    EXPECT_TRUE(reader.PopString(&signal_->proxy_info));
-    EXPECT_TRUE(reader.PopString(&signal_->error_message));
-
-    // Stop the message loop.
-    ASSERT_FALSE(quit_closure_.is_null()) << "Unexpected D-Bus signal";
-    quit_closure_.Run();
-    quit_closure_.Reset();
-  }
-
-  // Called when connected to a signal.
-  void OnConnectedToSignal(const std::string& signal_interface,
-                           const std::string& signal_name,
-                           bool success){
-    EXPECT_EQ(kReturnSignalInterface, signal_interface);
-    EXPECT_EQ(kReturnSignalName, signal_name);
-    EXPECT_TRUE(success);
-  }
-
-  // Makes a D-Bus call to |service_provider_|'s ResolveProxy method. If
-  // |request_signal| is true, requests that the proxy information be returned
-  // via a signal; otherwise it should be included in the response.
-  // |response_out| is updated to hold the response, and |signal_out| is updated
-  // to hold information about the emitted signal, if any.
-  void CallMethod(const std::string& source_url,
-                  bool request_signal,
-                  std::unique_ptr<dbus::Response>* response_out,
-                  std::unique_ptr<SignalInfo>* signal_out) {
+  // Makes a D-Bus call to |service_provider_|'s ResolveProxy method and returns
+  // the response.
+  std::unique_ptr<dbus::Response> CallMethod(const std::string& source_url) {
     dbus::MethodCall method_call(kNetworkProxyServiceInterface,
                                  kNetworkProxyServiceResolveProxyMethod);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(source_url);
-    if (request_signal) {
-      writer.AppendString(kReturnSignalInterface);
-      writer.AppendString(kReturnSignalName);
-
-      // Connect to the signal that will be sent to kReturnSignalInterface and
-      // kReturnSignalName.
-      test_helper_.SetUpReturnSignal(
-          kReturnSignalInterface, kReturnSignalName,
-          base::Bind(&ProxyResolutionServiceProviderTest::OnSignalReceived,
-                     base::Unretained(this)),
-          base::Bind(&ProxyResolutionServiceProviderTest::OnConnectedToSignal,
-                     base::Unretained(this)));
-    }
-
-    *response_out = test_helper_.CallMethod(&method_call);
-
-    // If a signal is being emitted, run the main message loop until it's
-    // received or we get tired of waiting.
-    if (request_signal) {
-      base::RunLoop run_loop;
-      quit_closure_ = run_loop.QuitClosure();
-      base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-          FROM_HERE, quit_closure_,
-          base::TimeDelta::FromSeconds(kSignalTimeoutSec));
-      run_loop.Run();
-    }
-
-    *signal_out = std::move(signal_);
+    return test_helper_.CallMethod(&method_call);
   }
 
   // Thread used to perform network operations.
   base::Thread network_thread_;
 
-  // Information about the last D-Bus signal received by OnSignalReceived().
-  std::unique_ptr<SignalInfo> signal_;
-
-  // Closure used to stop the message loop after receiving a D-Bus signal.
-  base::Closure quit_closure_;
-
   std::unique_ptr<TestProxyResolver> proxy_resolver_;
   std::unique_ptr<ProxyResolutionServiceProvider> service_provider_;
   ServiceProviderTestHelper test_helper_;
@@ -312,52 +228,9 @@
   DISALLOW_COPY_AND_ASSIGN(ProxyResolutionServiceProviderTest);
 };
 
-// Tests that synchronously-resolved proxy information is returned via a signal.
-TEST_F(ProxyResolutionServiceProviderTest, SignalSync) {
+TEST_F(ProxyResolutionServiceProviderTest, Sync) {
   const char kSourceURL[] = "http://www.gmail.com/";
-
-  std::unique_ptr<dbus::Response> response;
-  std::unique_ptr<SignalInfo> signal;
-  CallMethod(kSourceURL, true /* request_signal */, &response, &signal);
-
-  // An empty response should be returned.
-  ASSERT_TRUE(response);
-  EXPECT_FALSE(dbus::MessageReader(response.get()).HasMoreData());
-
-  // Confirm that the signal is received successfully.
-  ASSERT_TRUE(signal);
-  EXPECT_EQ(kSourceURL, signal->source_url);
-  EXPECT_EQ(proxy_resolver_->proxy_info().ToPacString(), signal->proxy_info);
-  EXPECT_EQ("", signal->error_message);
-}
-
-// Tests that asynchronously-resolved proxy information is returned via a
-// signal.
-TEST_F(ProxyResolutionServiceProviderTest, SignalAsync) {
-  const char kSourceURL[] = "http://www.gmail.com/";
-  proxy_resolver_->set_async(true);
-  proxy_resolver_->mutable_proxy_info()->UseNamedProxy("http://localhost:8080");
-
-  std::unique_ptr<dbus::Response> response;
-  std::unique_ptr<SignalInfo> signal;
-  CallMethod(kSourceURL, true /* request_signal */, &response, &signal);
-
-  // An empty response should be returned.
-  ASSERT_TRUE(response);
-  EXPECT_FALSE(dbus::MessageReader(response.get()).HasMoreData());
-
-  // Confirm that the signal is received successfully.
-  ASSERT_TRUE(signal);
-  EXPECT_EQ(kSourceURL, signal->source_url);
-  EXPECT_EQ(proxy_resolver_->proxy_info().ToPacString(), signal->proxy_info);
-  EXPECT_EQ("", signal->error_message);
-}
-
-TEST_F(ProxyResolutionServiceProviderTest, ResponseSync) {
-  const char kSourceURL[] = "http://www.gmail.com/";
-  std::unique_ptr<dbus::Response> response;
-  std::unique_ptr<SignalInfo> signal;
-  CallMethod(kSourceURL, false /* request_signal */, &response, &signal);
+  std::unique_ptr<dbus::Response> response = CallMethod(kSourceURL);
 
   // The response should contain the proxy info and an empty error.
   ASSERT_TRUE(response);
@@ -367,18 +240,13 @@
   EXPECT_TRUE(reader.PopString(&error));
   EXPECT_EQ(proxy_resolver_->proxy_info().ToPacString(), proxy_info);
   EXPECT_EQ("", error);
-
-  // No signal should've been emitted.
-  EXPECT_FALSE(signal);
 }
 
-TEST_F(ProxyResolutionServiceProviderTest, ResponseAsync) {
+TEST_F(ProxyResolutionServiceProviderTest, Async) {
   const char kSourceURL[] = "http://www.gmail.com/";
   proxy_resolver_->set_async(true);
   proxy_resolver_->mutable_proxy_info()->UseNamedProxy("http://localhost:8080");
-  std::unique_ptr<dbus::Response> response;
-  std::unique_ptr<SignalInfo> signal;
-  CallMethod(kSourceURL, false /* request_signal */, &response, &signal);
+  std::unique_ptr<dbus::Response> response = CallMethod(kSourceURL);
 
   // The response should contain the proxy info and an empty error.
   ASSERT_TRUE(response);
@@ -388,17 +256,12 @@
   EXPECT_TRUE(reader.PopString(&error));
   EXPECT_EQ(proxy_resolver_->proxy_info().ToPacString(), proxy_info);
   EXPECT_EQ("", error);
-
-  // No signal should've been emitted.
-  EXPECT_FALSE(signal);
 }
 
-TEST_F(ProxyResolutionServiceProviderTest, ResponseError) {
+TEST_F(ProxyResolutionServiceProviderTest, Error) {
   const char kSourceURL[] = "http://www.gmail.com/";
   proxy_resolver_->set_result(net::ERR_FAILED);
-  std::unique_ptr<dbus::Response> response;
-  std::unique_ptr<SignalInfo> signal;
-  CallMethod(kSourceURL, false /* request_signal */, &response, &signal);
+  std::unique_ptr<dbus::Response> response = CallMethod(kSourceURL);
 
   // The response should contain empty proxy info and a "mandatory proxy config
   // failed" error (which the error from the resolver will be mapped to).
@@ -410,9 +273,6 @@
   EXPECT_EQ("DIRECT", proxy_info);
   EXPECT_EQ(net::ErrorToString(net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED),
             error);
-
-  // No signal should've been emitted.
-  EXPECT_FALSE(signal);
 }
 
 }  // namespace chromeos
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc
index d74d535..82d560c 100644
--- a/components/browser_sync/profile_sync_service.cc
+++ b/components/browser_sync/profile_sync_service.cc
@@ -788,7 +788,7 @@
 
 bool ProfileSyncService::IsSyncConfirmationNeeded() const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return !IsFirstSetupInProgress() && !IsFirstSetupComplete() &&
+  return IsSignedIn() && !IsFirstSetupInProgress() && !IsFirstSetupComplete() &&
          IsSyncRequested();
 }
 
diff --git a/components/browser_sync/profile_sync_service.h b/components/browser_sync/profile_sync_service.h
index ddc7d6b0..a47fe59 100644
--- a/components/browser_sync/profile_sync_service.h
+++ b/components/browser_sync/profile_sync_service.h
@@ -437,7 +437,7 @@
 
   // Whether sync is currently blocked from starting because the sync
   // confirmation dialog hasn't been confirmed.
-  bool IsSyncConfirmationNeeded() const;
+  virtual bool IsSyncConfirmationNeeded() const;
 
   // Returns whether sync is managed, i.e. controlled by configuration
   // management. If so, the user is not allowed to configure sync.
diff --git a/components/browser_watcher/stability_debugging.cc b/components/browser_watcher/stability_debugging.cc
index 3c8cc649..aee484c8 100644
--- a/components/browser_watcher/stability_debugging.cc
+++ b/components/browser_watcher/stability_debugging.cc
@@ -4,10 +4,48 @@
 
 #include "components/browser_watcher/stability_debugging.h"
 
+#include <windows.h>
+
+#include <memory>
+
 #include "base/debug/activity_tracker.h"
+#include "build/build_config.h"
 
 namespace browser_watcher {
 
+namespace {
+
+struct VehUnregisterer {
+  void operator()(void* handle) const {
+    ::RemoveVectoredExceptionHandler(handle);
+  }
+};
+
+using VehHandle = std::unique_ptr<void, VehUnregisterer>;
+
+uintptr_t GetProgramCounter(const CONTEXT& context) {
+#if defined(ARCH_CPU_X86)
+  return context.Eip;
+#elif defined(ARCH_CPU_X86_64)
+  return context.Rip;
+#endif
+}
+
+LONG CALLBACK VectoredExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
+  base::debug::GlobalActivityTracker* tracker =
+      base::debug::GlobalActivityTracker::Get();
+  if (tracker) {
+    EXCEPTION_RECORD* record = exception_pointers->ExceptionRecord;
+    uintptr_t pc = GetProgramCounter(*exception_pointers->ContextRecord);
+    tracker->RecordException(reinterpret_cast<void*>(pc),
+                             record->ExceptionAddress, record->ExceptionCode);
+  }
+
+  return EXCEPTION_CONTINUE_SEARCH;  // Continue to the next handler.
+}
+
+}  // namespace
+
 void SetStabilityDataBool(base::StringPiece name, bool value) {
   base::debug::GlobalActivityTracker* global_tracker =
       base::debug::GlobalActivityTracker::Get();
@@ -26,4 +64,16 @@
   global_tracker->process_data().SetInt(name, value);
 }
 
+void RegisterStabilityVEH() {
+  // Register a vectored exception handler and request it be first. Note that
+  // subsequent registrations may also request to be first, in which case this
+  // one will be bumped.
+  // TODO(manzagop): Depending on observations, it may be necessary to
+  // consider refreshing the registration, either periodically or at opportune
+  // (e.g. risky) times.
+  static VehHandle veh_handler(
+      ::AddVectoredExceptionHandler(1, &VectoredExceptionHandler));
+  DCHECK(veh_handler);
+}
+
 }  // namespace browser_watcher
diff --git a/components/browser_watcher/stability_debugging.h b/components/browser_watcher/stability_debugging.h
index cc23f69f..145979d 100644
--- a/components/browser_watcher/stability_debugging.h
+++ b/components/browser_watcher/stability_debugging.h
@@ -15,6 +15,10 @@
 void SetStabilityDataBool(base::StringPiece name, bool value);
 void SetStabilityDataInt(base::StringPiece name, int64_t value);
 
+// Registers a vectored exception handler that stores exception details to the
+// stability file.
+void RegisterStabilityVEH();
+
 }  // namespace browser_watcher
 
 #endif  // COMPONENTS_BROWSER_WATCHER_STABILITY_DEBUGGING_H_
diff --git a/components/browser_watcher/stability_debugging_win_unittest.cc b/components/browser_watcher/stability_debugging_win_unittest.cc
index 785ab22be..d4bd107 100644
--- a/components/browser_watcher/stability_debugging_win_unittest.cc
+++ b/components/browser_watcher/stability_debugging_win_unittest.cc
@@ -4,84 +4,88 @@
 
 #include "components/browser_watcher/stability_debugging.h"
 
-#include "base/files/file_enumerator.h"
+#include <windows.h>
+
+#include "base/command_line.h"
+#include "base/debug/activity_tracker.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/process/process.h"
 #include "base/test/multiprocess_test.h"
-#include "components/browser_watcher/stability_paths.h"
+#include "base/test/test_timeouts.h"
+#include "components/browser_watcher/stability_report.pb.h"
+#include "components/browser_watcher/stability_report_extractor.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/multiprocess_func_list.h"
 
 namespace browser_watcher {
 
-class StabilityDebuggingWinMultiProcTest : public base::MultiProcessTest {};
+using base::debug::GlobalActivityTracker;
 
-MULTIPROCESS_TEST_MAIN(DummyProcess) {
-  return 0;
-}
+const int kMemorySize = 1 << 20;  // 1MiB
+const uint32_t exception_code = 42U;
+const uint32_t exception_flag_continuable = 0U;
 
-TEST_F(StabilityDebuggingWinMultiProcTest, GetStabilityFileForProcessTest) {
-  const base::FilePath empty_path;
-
-  // Get the path for the current process.
-  base::FilePath stability_path;
-  ASSERT_TRUE(GetStabilityFileForProcess(base::Process::Current(), empty_path,
-                                         &stability_path));
-
-  // Ensure requesting a second time produces the same.
-  base::FilePath stability_path_two;
-  ASSERT_TRUE(GetStabilityFileForProcess(base::Process::Current(), empty_path,
-                                         &stability_path_two));
-  EXPECT_EQ(stability_path, stability_path_two);
-
-  // Ensure a different process has a different stability path.
-  base::SpawnChildResult spawn_result = SpawnChild("DummyProcess");
-  base::FilePath stability_path_other;
-  ASSERT_TRUE(GetStabilityFileForProcess(spawn_result.process, empty_path,
-                                         &stability_path_other));
-  EXPECT_NE(stability_path, stability_path_other);
-}
-
-TEST(StabilityDebuggingWinTest,
-     GetStabilityFilePatternMatchesGetStabilityFileForProcessResult) {
-  // GetStabilityFileForProcess file names must match GetStabilityFilePattern
-  // according to
-  // FileEnumerator's algorithm. We test this by writing out some files and
-  // validating what is matched.
-
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-  base::FilePath user_data_dir = temp_dir.GetPath();
-
-  // Create the stability directory.
-  base::FilePath stability_dir = GetStabilityDir(user_data_dir);
-  ASSERT_TRUE(base::CreateDirectory(stability_dir));
-
-  // Write a stability file.
-  base::FilePath stability_file;
-  ASSERT_TRUE(GetStabilityFileForProcess(base::Process::Current(),
-                                         user_data_dir, &stability_file));
-  {
-    base::ScopedFILE file(base::OpenFile(stability_file, "w"));
-    ASSERT_TRUE(file.get());
+class StabilityDebuggingTest : public testing::Test {
+ public:
+  StabilityDebuggingTest() {}
+  ~StabilityDebuggingTest() override {
+    GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get();
+    if (global_tracker) {
+      global_tracker->ReleaseTrackerForCurrentThreadForTesting();
+      delete global_tracker;
+    }
   }
 
-  // Write a file that shouldn't match.
-  base::FilePath non_matching_file =
-      stability_dir.AppendASCII("non_matching.foo");
-  {
-    base::ScopedFILE file(base::OpenFile(non_matching_file, "w"));
-    ASSERT_TRUE(file.get());
+  void SetUp() override {
+    testing::Test::SetUp();
+
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    debug_path_ = temp_dir_.GetPath().AppendASCII("debug.pma");
+
+    GlobalActivityTracker::CreateWithFile(debug_path_, kMemorySize, 0ULL, "",
+                                          3);
   }
 
-  // Validate only the stability file matches.
-  base::FileEnumerator enumerator(stability_dir, false /* recursive */,
-                                  base::FileEnumerator::FILES,
-                                  GetStabilityFilePattern());
-  ASSERT_EQ(stability_file, enumerator.Next());
-  ASSERT_TRUE(enumerator.Next().empty());
+  const base::FilePath& debug_path() { return debug_path_; }
+
+ private:
+  base::ScopedTempDir temp_dir_;
+  base::FilePath debug_path_;
+};
+
+TEST_F(StabilityDebuggingTest, CrashingTest) {
+  RegisterStabilityVEH();
+
+  // Raise an exception, then continue.
+  __try {
+    ::RaiseException(exception_code, exception_flag_continuable, 0U, nullptr);
+  } __except (EXCEPTION_CONTINUE_EXECUTION) {
+  }
+
+  // Collect the report.
+  StabilityReport report;
+  ASSERT_EQ(SUCCESS, Extract(debug_path(), &report));
+
+  // Validate expectations.
+  ASSERT_EQ(1, report.process_states_size());
+  const ProcessState& process_state = report.process_states(0);
+  ASSERT_EQ(1, process_state.threads_size());
+
+  bool thread_found = false;
+  for (const ThreadState& thread : process_state.threads()) {
+    if (thread.thread_id() == ::GetCurrentThreadId()) {
+      thread_found = true;
+      ASSERT_TRUE(thread.has_exception());
+      const Exception& exception = thread.exception();
+      EXPECT_EQ(exception_code, exception.code());
+      EXPECT_NE(0ULL, exception.program_counter());
+      EXPECT_NE(0ULL, exception.exception_address());
+      EXPECT_NE(0LL, exception.time());
+    }
+  }
+  ASSERT_TRUE(thread_found);
 }
 
 }  // namespace browser_watcher
diff --git a/components/browser_watcher/stability_paths_unittest.cc b/components/browser_watcher/stability_paths_unittest.cc
index 4b9eea0..7a8f6ce 100644
--- a/components/browser_watcher/stability_paths_unittest.cc
+++ b/components/browser_watcher/stability_paths_unittest.cc
@@ -4,14 +4,87 @@
 
 #include "components/browser_watcher/stability_paths.h"
 
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_file.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/process/process.h"
+#include "base/test/multiprocess_test.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
 
 namespace browser_watcher {
 
+class StabilityPathsMultiProcTest : public base::MultiProcessTest {};
+
+MULTIPROCESS_TEST_MAIN(DummyProcess) {
+  return 0;
+}
+
+TEST_F(StabilityPathsMultiProcTest, GetStabilityFileForProcessTest) {
+  const base::FilePath empty_path;
+
+  // Get the path for the current process.
+  base::FilePath stability_path;
+  ASSERT_TRUE(GetStabilityFileForProcess(base::Process::Current(), empty_path,
+                                         &stability_path));
+
+  // Ensure requesting a second time produces the same.
+  base::FilePath stability_path_two;
+  ASSERT_TRUE(GetStabilityFileForProcess(base::Process::Current(), empty_path,
+                                         &stability_path_two));
+  EXPECT_EQ(stability_path, stability_path_two);
+
+  // Ensure a different process has a different stability path.
+  base::SpawnChildResult spawn_result = SpawnChild("DummyProcess");
+  base::FilePath stability_path_other;
+  ASSERT_TRUE(GetStabilityFileForProcess(spawn_result.process, empty_path,
+                                         &stability_path_other));
+  EXPECT_NE(stability_path, stability_path_other);
+}
+
+TEST(StabilityPathsTest,
+     GetStabilityFilePatternMatchesGetStabilityFileForProcessResult) {
+  // GetStabilityFileForProcess file names must match GetStabilityFilePattern
+  // according to
+  // FileEnumerator's algorithm. We test this by writing out some files and
+  // validating what is matched.
+
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  base::FilePath user_data_dir = temp_dir.GetPath();
+
+  // Create the stability directory.
+  base::FilePath stability_dir = GetStabilityDir(user_data_dir);
+  ASSERT_TRUE(base::CreateDirectory(stability_dir));
+
+  // Write a stability file.
+  base::FilePath stability_file;
+  ASSERT_TRUE(GetStabilityFileForProcess(base::Process::Current(),
+                                         user_data_dir, &stability_file));
+  {
+    base::ScopedFILE file(base::OpenFile(stability_file, "w"));
+    ASSERT_TRUE(file.get());
+  }
+
+  // Write a file that shouldn't match.
+  base::FilePath non_matching_file =
+      stability_dir.AppendASCII("non_matching.foo");
+  {
+    base::ScopedFILE file(base::OpenFile(non_matching_file, "w"));
+    ASSERT_TRUE(file.get());
+  }
+
+  // Validate only the stability file matches.
+  base::FileEnumerator enumerator(stability_dir, false /* recursive */,
+                                  base::FileEnumerator::FILES,
+                                  GetStabilityFilePattern());
+  ASSERT_EQ(stability_file, enumerator.Next());
+  ASSERT_TRUE(enumerator.Next().empty());
+}
+
 TEST(StabilityPathsTest, GetStabilityFiles) {
   base::ScopedTempDir temp_dir;
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
diff --git a/components/browser_watcher/stability_report.proto b/components/browser_watcher/stability_report.proto
index b1ca52e7..af0dae3c 100644
--- a/components/browser_watcher/stability_report.proto
+++ b/components/browser_watcher/stability_report.proto
@@ -142,8 +142,17 @@
   map<string, TypedValue> user_data = 9;
 }
 
-// The state of a thread.
+// Details about an exception.
 // Next id: 5
+message Exception {
+  optional uint32 code = 1;
+  optional uint64 program_counter = 2;
+  optional uint64 exception_address = 3;
+  optional int64 time = 4;
+}
+
+// The state of a thread.
+// Next id: 6
 message ThreadState {
   // The name of the thread, up to a maxiumum length.
   optional string thread_name = 1;
@@ -155,6 +164,10 @@
   // stack and |activities| holds the base of the stack (up to a maximum size).
   optional int32 activity_count = 3;
   repeated Activity activities = 4;
+
+  // The last exception to be successfully captured. Note this exception may
+  // have been recovered from.
+  optional Exception exception = 5;
 }
 
 // The state of a process.
diff --git a/components/browser_watcher/stability_report_extractor.cc b/components/browser_watcher/stability_report_extractor.cc
index fd5c185e..a9f71ba 100644
--- a/components/browser_watcher/stability_report_extractor.cc
+++ b/components/browser_watcher/stability_report_extractor.cc
@@ -181,6 +181,15 @@
   }
 }
 
+void CollectException(const base::debug::Activity& recorded,
+                      Exception* collected) {
+  DCHECK(collected);
+  collected->set_code(recorded.data.exception.code);
+  collected->set_program_counter(recorded.calling_address);
+  collected->set_exception_address(recorded.origin_address);
+  collected->set_time(recorded.time_internal);
+}
+
 void CollectThread(
     const base::debug::ThreadActivityAnalyzer::Snapshot& snapshot,
     ThreadState* thread_state) {
@@ -190,6 +199,12 @@
   thread_state->set_thread_id(snapshot.thread_id);
   thread_state->set_activity_count(snapshot.activity_stack_depth);
 
+  if (snapshot.last_exception.activity_type ==
+      base::debug::Activity::ACT_EXCEPTION) {
+    CollectException(snapshot.last_exception,
+                     thread_state->mutable_exception());
+  }
+
   for (size_t i = 0; i < snapshot.activity_stack.size(); ++i) {
     Activity* collected = thread_state->add_activities();
 
diff --git a/components/browser_watcher/stability_report_extractor_unittest.cc b/components/browser_watcher/stability_report_extractor_unittest.cc
index 63dd24d..d5f50b6 100644
--- a/components/browser_watcher/stability_report_extractor_unittest.cc
+++ b/components/browser_watcher/stability_report_extractor_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/persistent_memory_allocator.h"
 #include "base/stl_util.h"
+#include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/crashpad/crashpad/client/crash_report_database.h"
 
@@ -119,6 +120,22 @@
     return WrapUnique(new ThreadActivityTracker(mem_base, tracker_mem_size));
   }
 
+  void PerformBasicReportValidation(const StabilityReport& report) {
+    // One report with one thread that has the expected name and id.
+    ASSERT_EQ(1, report.process_states_size());
+    const ProcessState& process_state = report.process_states(0);
+    EXPECT_EQ(base::GetCurrentProcId(), process_state.process_id());
+    ASSERT_EQ(1, process_state.threads_size());
+    const ThreadState& thread_state = process_state.threads(0);
+    EXPECT_EQ(base::PlatformThread::GetName(), thread_state.thread_name());
+#if defined(OS_WIN)
+    EXPECT_EQ(base::PlatformThread::CurrentId(), thread_state.thread_id());
+#elif defined(OS_POSIX)
+    EXPECT_EQ(base::PlatformThread::CurrentHandle().platform_handle(),
+              thread_state.thread_id());
+#endif
+  }
+
   const FilePath& debug_file_path() const { return debug_file_path_; }
 
  protected:
@@ -163,19 +180,8 @@
   ASSERT_EQ(SUCCESS, Extract(debug_file_path(), &report));
 
   // Validate the report.
-  ASSERT_EQ(1, report.process_states_size());
-  const ProcessState& process_state = report.process_states(0);
-  EXPECT_EQ(base::GetCurrentProcId(), process_state.process_id());
-  ASSERT_EQ(1, process_state.threads_size());
-
-  const ThreadState& thread_state = process_state.threads(0);
-  EXPECT_EQ(base::PlatformThread::GetName(), thread_state.thread_name());
-#if defined(OS_WIN)
-  EXPECT_EQ(base::PlatformThread::CurrentId(), thread_state.thread_id());
-#elif defined(OS_POSIX)
-  EXPECT_EQ(base::PlatformThread::CurrentHandle().platform_handle(),
-            thread_state.thread_id());
-#endif
+  ASSERT_NO_FATAL_FAILURE(PerformBasicReportValidation(report));
+  const ThreadState& thread_state = report.process_states(0).threads(0);
 
   EXPECT_EQ(7, thread_state.activity_count());
   ASSERT_EQ(6, thread_state.activities_size());
@@ -223,6 +229,48 @@
   }
 }
 
+TEST_F(StabilityReportExtractorThreadTrackerTest, CollectException) {
+  const void* expected_pc = reinterpret_cast<void*>(0xCAFE);
+  const void* expected_address = nullptr;
+  const uint32_t expected_code = 42U;
+
+  // Record an exception.
+  const int64_t timestamp = base::Time::Now().ToInternalValue();
+  tracker_->RecordExceptionActivity(expected_pc, expected_address,
+                                    base::debug::Activity::ACT_EXCEPTION,
+                                    ActivityData::ForException(expected_code));
+
+  // Collect report and validate.
+  StabilityReport report;
+  ASSERT_EQ(SUCCESS, Extract(debug_file_path(), &report));
+
+  // Validate the presence of the exception.
+  ASSERT_NO_FATAL_FAILURE(PerformBasicReportValidation(report));
+  const ThreadState& thread_state = report.process_states(0).threads(0);
+  ASSERT_TRUE(thread_state.has_exception());
+  const Exception& exception = thread_state.exception();
+  EXPECT_EQ(expected_code, exception.code());
+  EXPECT_EQ(expected_pc, reinterpret_cast<void*>(exception.program_counter()));
+  EXPECT_EQ(expected_address,
+            reinterpret_cast<void*>(exception.exception_address()));
+  const int64_t tolerance_us = 1000ULL;
+  EXPECT_LE(std::abs(timestamp - exception.time()), tolerance_us);
+}
+
+TEST_F(StabilityReportExtractorThreadTrackerTest, CollectNoException) {
+  // Record something.
+  tracker_->PushActivity(reinterpret_cast<void*>(kTaskOrigin),
+                         base::debug::Activity::ACT_TASK_RUN,
+                         ActivityData::ForTask(kTaskSequenceNum));
+
+  // Collect report and validate there is no exception.
+  StabilityReport report;
+  ASSERT_EQ(SUCCESS, Extract(debug_file_path(), &report));
+  ASSERT_NO_FATAL_FAILURE(PerformBasicReportValidation(report));
+  const ThreadState& thread_state = report.process_states(0).threads(0);
+  ASSERT_FALSE(thread_state.has_exception());
+}
+
 // Tests stability report extraction.
 class StabilityReportExtractorTest : public testing::Test {
  public:
diff --git a/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom b/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom
index 07013fe0..e935de9d57 100644
--- a/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom
+++ b/components/chrome_cleaner/public/interfaces/chrome_prompt.mojom
@@ -4,7 +4,14 @@
 
 module chrome_cleaner.mojom;
 
-import "mojo/common/file_path.mojom";
+// IMPORTANT NOTE: Avoid adding dependencies to typemapped .mojom files.
+// Enabling typemaps currently (as of July 2017) requires indirectly depending
+// on all existing typemap definitions. The Chrome Cleaner is built
+// independently from Chromium and would like to avoid these dependencies.
+
+// Once it's possible to specify a limited subset of typemaps to use, be
+// careful not to add dependencies to [Native] mojo structures. The wire format
+// for [Native] structs is not guaranteed to be consistent between versions.
 
 [Extensible]
 enum PromptAcceptance {
@@ -21,6 +28,10 @@
   IGNORED = 4,
 };
 
+struct FilePath {
+  array<uint16> value;
+};
+
 // Service provided by Chrome to prompt the user to start a cleanup if the
 // Chrome Cleanup Tool detects unwanted software on the system.
 interface ChromePrompt {
@@ -29,6 +40,6 @@
   //                         will be deleted by the Chrome Cleanup Tool.
   // Returns:
   //  - prompt_acceptance: indicates if the user accepted the prompt.
-  PromptUser(array<mojo.common.mojom.FilePath> files_to_delete)
+  PromptUser(array<FilePath> files_to_delete)
       => (PromptAcceptance prompt_acceptance);
 };
diff --git a/components/chrome_cleaner/public/typemaps/DEPS b/components/chrome_cleaner/public/typemaps/DEPS
new file mode 100644
index 0000000..e9aaaf9
--- /dev/null
+++ b/components/chrome_cleaner/public/typemaps/DEPS
@@ -0,0 +1,4 @@
+# Allow the typemaps to access their dependencies.
+include_rules = [
+  '+base/files/file_path.h',
+]
diff --git a/components/chrome_cleaner/public/typemaps/OWNERS b/components/chrome_cleaner/public/typemaps/OWNERS
new file mode 100644
index 0000000..adcd752e
--- /dev/null
+++ b/components/chrome_cleaner/public/typemaps/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+file://ipc/SECURITY_OWNERS
+
+per-file *_struct_traits*.*=set noparent
+per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/components/chrome_cleaner/public/typemaps/chrome_prompt.typemap b/components/chrome_cleaner/public/typemaps/chrome_prompt.typemap
new file mode 100644
index 0000000..ec34d5e
--- /dev/null
+++ b/components/chrome_cleaner/public/typemaps/chrome_prompt.typemap
@@ -0,0 +1,12 @@
+# 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.
+
+mojom = "//components/chrome_cleaner/public/interfaces/chrome_prompt.mojom"
+public_headers = [ "//base/files/file_path.h" ]
+traits_headers = [ "//components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h" ]
+sources = [
+  "//components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.cc",
+]
+
+type_mappings = [ "chrome_cleaner.mojom.FilePath=base::FilePath" ]
diff --git a/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.cc b/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.cc
new file mode 100644
index 0000000..d15c172
--- /dev/null
+++ b/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.cc
@@ -0,0 +1,41 @@
+// 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 "components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h"
+
+namespace mojo {
+
+// static
+ConstCArray<uint16_t>
+StructTraits<chrome_cleaner::mojom::FilePathDataView, base::FilePath>::value(
+    const base::FilePath& file_path) {
+#if defined(OS_WIN)
+  return ConstCArray<uint16_t>(
+      file_path.value().size(),
+      reinterpret_cast<const uint16_t*>(file_path.value().data()));
+#else
+  NOTREACHED();
+  return ConstCArray<uint16_t>();
+#endif
+}
+
+// static
+bool StructTraits<chrome_cleaner::mojom::FilePathDataView,
+                  base::FilePath>::Read(chrome_cleaner::mojom::FilePathDataView
+                                            path_view,
+                                        base::FilePath* out) {
+#if defined(OS_WIN)
+  ArrayDataView<uint16_t> view;
+  path_view.GetValueDataView(&view);
+  base::FilePath path = base::FilePath(base::string16(
+      reinterpret_cast<const base::char16*>(view.data()), view.size()));
+  *out = std::move(path);
+  return true;
+#else
+  NOTREACHED();
+  return false;
+#endif
+}
+
+}  // namespace mojo
diff --git a/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h b/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h
new file mode 100644
index 0000000..76d123f
--- /dev/null
+++ b/components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h
@@ -0,0 +1,22 @@
+// 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 COMPONENTS_CHROME_CLEANER_PUBLIC_TYPEMAPS_CHROME_CLEANER_STRUCT_TRAITS_H_
+#define COMPONENTS_CHROME_CLEANER_PUBLIC_TYPEMAPS_CHROME_CLEANER_STRUCT_TRAITS_H_
+
+#include "base/files/file_path.h"
+#include "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<chrome_cleaner::mojom::FilePathDataView, base::FilePath> {
+  static ConstCArray<uint16_t> value(const base::FilePath& file_path);
+  static bool Read(chrome_cleaner::mojom::FilePathDataView path_view,
+                   base::FilePath* out);
+};
+
+}  // namespace mojo
+
+#endif  // COMPONENTS_CHROME_CLEANER_PUBLIC_TYPEMAPS_CHROME_CLEANER_STRUCT_TRAITS_H_
diff --git a/components/download/content/factory/download_service_factory.cc b/components/download/content/factory/download_service_factory.cc
index bb2d7b97..b99a024 100644
--- a/components/download/content/factory/download_service_factory.cc
+++ b/components/download/content/factory/download_service_factory.cc
@@ -12,6 +12,7 @@
 #include "components/download/internal/download_store.h"
 #include "components/download/internal/model_impl.h"
 #include "components/download/internal/proto/entry.pb.h"
+#include "components/download/internal/scheduler/scheduler_impl.h"
 #include "components/leveldb_proto/proto_database_impl.h"
 
 namespace download {
@@ -42,10 +43,11 @@
                                                std::move(entry_db));
   auto model = base::MakeUnique<ModelImpl>(std::move(store));
   auto device_status_listener = base::MakeUnique<DeviceStatusListener>();
+  auto scheduler = base::MakeUnique<SchedulerImpl>(nullptr, client_set.get());
 
   auto controller = base::MakeUnique<ControllerImpl>(
       std::move(client_set), std::move(config), std::move(driver),
-      std::move(model), std::move(device_status_listener),
+      std::move(model), std::move(device_status_listener), std::move(scheduler),
       std::move(task_scheduler));
   return new DownloadServiceImpl(std::move(controller));
 }
diff --git a/components/download/content/internal/download_driver_impl.cc b/components/download/content/internal/download_driver_impl.cc
index 99dfd42..0300deb 100644
--- a/components/download/content/internal/download_driver_impl.cc
+++ b/components/download/content/internal/download_driver_impl.cc
@@ -47,6 +47,7 @@
   entry.bytes_downloaded = item->GetReceivedBytes();
   entry.expected_total_size = item->GetTotalBytes();
   entry.response_headers = item->GetResponseHeaders();
+  entry.url_chain = item->GetUrlChain();
   return entry;
 }
 
@@ -78,40 +79,44 @@
 }
 
 bool DownloadDriverImpl::IsReady() const {
-  return client_ && download_manager_;
+  return client_ && download_manager_ &&
+         download_manager_->IsManagerInitialized();
 }
 
 void DownloadDriverImpl::Start(
-    const DownloadParams& params,
+    const RequestParams& request_params,
+    const std::string& guid,
     const net::NetworkTrafficAnnotationTag& traffic_annotation) {
-  DCHECK(!params.request_params.url.is_empty());
-  DCHECK(!params.guid.empty());
+  DCHECK(!request_params.url.is_empty());
+  DCHECK(!guid.empty());
   if (!download_manager_)
     return;
 
   content::StoragePartition* storage_partition =
       content::BrowserContext::GetStoragePartitionForSite(
-          download_manager_->GetBrowserContext(), params.request_params.url);
+          download_manager_->GetBrowserContext(), request_params.url);
   DCHECK(storage_partition);
 
   std::unique_ptr<content::DownloadUrlParameters> download_url_params(
       new content::DownloadUrlParameters(
-          params.request_params.url, storage_partition->GetURLRequestContext(),
+          request_params.url, storage_partition->GetURLRequestContext(),
           traffic_annotation));
 
-  // TODO(xingliu): Handle the request headers from |params|, need to tweak
-  // download network stack.
-  // Make content::DownloadManager handle potential guid collision and return
-  // an error to fail the download cleanly.
-  download_url_params->set_guid(params.guid);
+  // TODO(xingliu): Make content::DownloadManager handle potential guid
+  // collision and return an error to fail the download cleanly.
+  for (net::HttpRequestHeaders::Iterator it(request_params.request_headers);
+       it.GetNext();) {
+    download_url_params->add_request_header(it.name(), it.value());
+  }
+  download_url_params->set_guid(guid);
   download_url_params->set_transient(true);
-  download_url_params->set_method(params.request_params.method);
-  download_url_params->set_file_path(file_dir_.AppendASCII(params.guid));
+  download_url_params->set_method(request_params.method);
+  download_url_params->set_file_path(file_dir_.AppendASCII(guid));
 
   download_manager_->DownloadUrl(std::move(download_url_params));
 }
 
-void DownloadDriverImpl::Cancel(const std::string& guid) {
+void DownloadDriverImpl::Remove(const std::string& guid) {
   if (!download_manager_)
     return;
   content::DownloadItem* item = download_manager_->GetDownloadByGuid(guid);
@@ -173,7 +178,11 @@
   item->AddObserver(this);
   DCHECK(client_);
   DriverEntry entry = CreateDriverEntry(item);
-  client_->OnDownloadCreated(entry);
+
+  // Only notifies the client about new downloads. Exsting download data will be
+  // loaded before the driver is ready.
+  if (IsReady())
+    client_->OnDownloadCreated(entry);
 }
 
 void DownloadDriverImpl::OnManagerInitialized() {
diff --git a/components/download/content/internal/download_driver_impl.h b/components/download/content/internal/download_driver_impl.h
index 61bc64f..5330733 100644
--- a/components/download/content/internal/download_driver_impl.h
+++ b/components/download/content/internal/download_driver_impl.h
@@ -36,9 +36,10 @@
   void Initialize(DownloadDriver::Client* client) override;
   bool IsReady() const override;
   void Start(
-      const DownloadParams& params,
+      const RequestParams& request_params,
+      const std::string& guid,
       const net::NetworkTrafficAnnotationTag& traffic_annotation) override;
-  void Cancel(const std::string& guid) override;
+  void Remove(const std::string& guid) override;
   void Pause(const std::string& guid) override;
   void Resume(const std::string& guid) override;
   base::Optional<DriverEntry> Find(const std::string& guid) override;
diff --git a/components/download/internal/controller.h b/components/download/internal/controller.h
index 26011a0b..7ed4804 100644
--- a/components/download/internal/controller.h
+++ b/components/download/internal/controller.h
@@ -18,6 +18,23 @@
 struct SchedulingParams;
 struct StartupStatus;
 
+// The type of completion when the download entry transits to complete state.
+// TODO(xingliu): Implement timeout and unknown failure types.
+enum class CompletionType {
+  // The download is successfully finished.
+  SUCCEED = 0,
+  // The download is interrupted and failed.
+  FAIL = 1,
+  // The download is aborted by the client.
+  ABORT = 2,
+  // The download is timed out and the connection is closed.
+  TIMEOUT = 3,
+  // The download is failed for unknown reasons.
+  UNKNOWN = 4,
+  // The download is cancelled.
+  CANCEL = 5,
+};
+
 // The core Controller responsible for gluing various DownloadService components
 // together to manage the active downloads.
 class Controller {
diff --git a/components/download/internal/controller_impl.cc b/components/download/internal/controller_impl.cc
index c5e6782..75870bae 100644
--- a/components/download/internal/controller_impl.cc
+++ b/components/download/internal/controller_impl.cc
@@ -9,14 +9,31 @@
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/memory/ptr_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/download/internal/client_set.h"
 #include "components/download/internal/config.h"
 #include "components/download/internal/entry.h"
 #include "components/download/internal/entry_utils.h"
 #include "components/download/internal/model.h"
+#include "components/download/internal/scheduler/scheduler.h"
+#include "components/download/internal/stats.h"
+#include "components/download/public/client.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
 
 namespace download {
+namespace {
+
+// Helper function to transit the state of |entry| to |new_state|.
+void TransitTo(Entry* entry, Entry::State new_state, Model* model) {
+  DCHECK(entry);
+  if (entry->state == new_state)
+    return;
+  entry->state = new_state;
+  model->Update(*entry);
+}
+
+}  // namespace
 
 ControllerImpl::ControllerImpl(
     std::unique_ptr<ClientSet> clients,
@@ -24,17 +41,17 @@
     std::unique_ptr<DownloadDriver> driver,
     std::unique_ptr<Model> model,
     std::unique_ptr<DeviceStatusListener> device_status_listener,
+    std::unique_ptr<Scheduler> scheduler,
     std::unique_ptr<TaskScheduler> task_scheduler)
     : clients_(std::move(clients)),
       config_(std::move(config)),
       driver_(std::move(driver)),
       model_(std::move(model)),
       device_status_listener_(std::move(device_status_listener)),
+      scheduler_(std::move(scheduler)),
       task_scheduler_(std::move(task_scheduler)) {}
 
-ControllerImpl::~ControllerImpl() {
-  device_status_listener_->Stop();
-}
+ControllerImpl::~ControllerImpl() = default;
 
 void ControllerImpl::Initialize() {
   DCHECK(!startup_status_.Complete());
@@ -83,15 +100,19 @@
   DCHECK(startup_status_.Ok());
 
   auto* entry = model_->Get(guid);
-  DCHECK(entry);
 
-  if (entry->state == Entry::State::PAUSED ||
+  if (!entry || entry->state == Entry::State::PAUSED ||
       entry->state == Entry::State::COMPLETE ||
       entry->state == Entry::State::WATCHDOG) {
     return;
   }
 
-  // TODO(dtrainor): Pause the download.
+  TransitTo(entry, Entry::State::PAUSED, model_.get());
+  UpdateDriverState(*entry);
+
+  // Pausing a download may yield a concurrent slot to start a new download, and
+  // may change the scheduling criteria.
+  ActivateMoreDownloads();
 }
 
 void ControllerImpl::ResumeDownload(const std::string& guid) {
@@ -103,23 +124,28 @@
   if (entry->state != Entry::State::PAUSED)
     return;
 
-  // TODO(dtrainor): Resume the download.
+  TransitTo(entry, Entry::State::ACTIVE, model_.get());
+  UpdateDriverState(*entry);
+
+  ActivateMoreDownloads();
 }
 
 void ControllerImpl::CancelDownload(const std::string& guid) {
   DCHECK(startup_status_.Ok());
 
   auto* entry = model_->Get(guid);
-  DCHECK(entry);
+  if (!entry)
+    return;
 
   if (entry->state == Entry::State::NEW) {
     // Check if we're currently trying to add the download.
     DCHECK(start_callbacks_.find(entry->guid) != start_callbacks_.end());
     HandleStartDownloadResponse(entry->client, entry->guid,
                                 DownloadParams::StartResult::CLIENT_CANCELLED);
+    return;
   }
 
-  // TODO(dtrainor): Cancel the download.
+  HandleCompleteDownload(CompletionType::CANCEL, guid);
 }
 
 void ControllerImpl::ChangeDownloadCriteria(const std::string& guid,
@@ -127,9 +153,18 @@
   DCHECK(startup_status_.Ok());
 
   auto* entry = model_->Get(guid);
-  DCHECK(entry);
+  if (!entry || entry->scheduling_params == params) {
+    DVLOG(1) << "Try to update the same scheduling parameters.";
+    return;
+  }
 
-  // TODO(dtrainor): Change the criteria of the download.
+  UpdateDriverState(*entry);
+
+  // Update the scheduling parameters.
+  entry->scheduling_params = params;
+  model_->Update(*entry);
+
+  ActivateMoreDownloads();
 }
 
 DownloadClient ControllerImpl::GetOwnerOfDownload(const std::string& guid) {
@@ -194,15 +229,51 @@
   AttemptToFinalizeSetup();
 }
 
-void ControllerImpl::OnDownloadCreated(const DriverEntry& download) {}
+void ControllerImpl::OnDownloadCreated(const DriverEntry& download) {
+  Entry* entry = model_->Get(download.guid);
 
+  if (!entry) {
+    // TODO(xingliu): Log non download service initiated downloads.
+    return;
+  }
+
+  download::Client* client = clients_->GetClient(entry->client);
+  DCHECK(client);
+  using ShouldDownload = download::Client::ShouldDownload;
+  ShouldDownload should_download = client->OnDownloadStarted(
+      download.guid, download.url_chain, download.response_headers);
+  if (should_download == ShouldDownload::ABORT) {
+    HandleCompleteDownload(CompletionType::ABORT, entry->guid);
+  }
+}
 void ControllerImpl::OnDownloadFailed(const DriverEntry& download, int reason) {
+  Entry* entry = model_->Get(download.guid);
+  if (!entry)
+    return;
+
+  HandleCompleteDownload(CompletionType::FAIL, download.guid);
 }
 
 void ControllerImpl::OnDownloadSucceeded(const DriverEntry& download,
-                                         const base::FilePath& path) {}
+                                         const base::FilePath& path) {
+  Entry* entry = model_->Get(download.guid);
+  if (!entry)
+    return;
 
-void ControllerImpl::OnDownloadUpdated(const DriverEntry& download) {}
+  HandleCompleteDownload(CompletionType::SUCCEED, download.guid);
+}
+
+void ControllerImpl::OnDownloadUpdated(const DriverEntry& download) {
+  Entry* entry = model_->Get(download.guid);
+  if (!entry)
+    return;
+
+  DCHECK_EQ(download.state, DriverEntry::State::IN_PROGRESS);
+
+  download::Client* client = clients_->GetClient(entry->client);
+  if (client)
+    client->OnDownloadUpdated(download.guid, download.bytes_downloaded);
+}
 
 void ControllerImpl::OnModelReady(bool success) {
   DCHECK(!startup_status_.model_ok.has_value());
@@ -227,9 +298,12 @@
   HandleStartDownloadResponse(client, guid,
                               DownloadParams::StartResult::ACCEPTED);
 
-  auto* entry = model_->Get(guid);
+  Entry* entry = model_->Get(guid);
   DCHECK(entry);
   DCHECK_EQ(Entry::State::NEW, entry->state);
+  TransitTo(entry, Entry::State::AVAILABLE, model_.get());
+
+  ActivateMoreDownloads();
 
   // TODO(dtrainor): Make sure we're running all of the downloads we care about.
 }
@@ -237,7 +311,12 @@
 void ControllerImpl::OnItemUpdated(bool success,
                                    DownloadClient client,
                                    const std::string& guid) {
-  // TODO(dtrainor): Fail and clean up the download if necessary.
+  Entry* entry = model_->Get(guid);
+  DCHECK(entry);
+  if (entry->state == Entry::State::COMPLETE ||
+      entry->state == Entry::State::WATCHDOG) {
+    driver_->Remove(guid);
+  }
 }
 
 void ControllerImpl::OnItemRemoved(bool success,
@@ -247,7 +326,8 @@
 }
 
 void ControllerImpl::OnDeviceStatusChanged(const DeviceStatus& device_status) {
-  NOTIMPLEMENTED();
+  UpdateDriverStates();
+  ActivateMoreDownloads();
 }
 
 void ControllerImpl::AttemptToFinalizeSetup() {
@@ -265,12 +345,16 @@
   device_status_listener_->Start(this);
   CancelOrphanedRequests();
   ResolveInitialRequestStates();
+  UpdateDriverStates();
   PullCurrentRequestStatus();
 
   // TODO(dtrainor): Post this so that the initialization step is finalized
   // before Clients can take action.
   NotifyClientsOfStartup();
   ProcessScheduledTasks();
+
+  // Pull the initial straw if active downloads haven't reach maximum.
+  ActivateMoreDownloads();
 }
 
 void ControllerImpl::CancelOrphanedRequests() {
@@ -282,8 +366,10 @@
       guids_to_remove.push_back(entry->guid);
   }
 
-  for (auto guid : guids_to_remove) {
-    // TODO(dtrainor): Remove the download.
+  for (const auto& guid : guids_to_remove) {
+    model_->Remove(guid);
+    // TODO(xingliu): Use file monitor to delete the files.
+    driver_->Remove(guid);
   }
 }
 
@@ -291,6 +377,53 @@
   // TODO(dtrainor): Implement.
 }
 
+void ControllerImpl::UpdateDriverStates() {
+  DCHECK(startup_status_.Complete());
+
+  for (auto* const entry : model_->PeekEntries())
+    UpdateDriverState(*entry);
+}
+
+void ControllerImpl::UpdateDriverState(const Entry& entry) {
+  base::Optional<DriverEntry> driver_entry = driver_->Find(entry.guid);
+
+  bool meets_device_criteria = device_status_listener_->CurrentDeviceStatus()
+                                   .MeetsCondition(entry.scheduling_params)
+                                   .MeetsRequirements();
+  switch (entry.state) {
+    case Entry::State::ACTIVE:
+      if (!meets_device_criteria) {
+        driver_->Pause(entry.guid);
+        break;
+      }
+      // Start or resume the download if it should be running.
+      if (!driver_entry.has_value()) {
+        driver_->Start(entry.request_params, entry.guid,
+                       NO_TRAFFIC_ANNOTATION_YET);
+        break;
+      }
+      if (driver_entry->state != DriverEntry::State::IN_PROGRESS) {
+        driver_->Resume(entry.guid);
+      }
+      break;
+    case Entry::State::PAUSED:
+      // Pause the in progress downloads that should not be running.
+      if (driver_entry.has_value() &&
+          driver_entry->state == DriverEntry::State::IN_PROGRESS) {
+        driver_->Pause(entry.guid);
+      }
+      break;
+    // Fall through.
+    case Entry::State::AVAILABLE:
+    case Entry::State::NEW:
+    case Entry::State::COMPLETE:
+    case Entry::State::WATCHDOG:
+      break;
+    default:
+      NOTREACHED();
+  }
+}
+
 void ControllerImpl::PullCurrentRequestStatus() {
   // TODO(dtrainor): Implement.
 }
@@ -329,4 +462,36 @@
       FROM_HERE, base::Bind(callback, guid, result));
 }
 
+void ControllerImpl::HandleCompleteDownload(CompletionType type,
+                                            const std::string& guid) {
+  Entry* entry = model_->Get(guid);
+  DCHECK(entry);
+  stats::LogDownloadCompletion(type);
+
+  if (entry->state == Entry::State::COMPLETE ||
+      entry->state == Entry::State::WATCHDOG) {
+    DVLOG(1) << "Download is already completed.";
+    return;
+  }
+
+  // TODO(xingliu): Notify the client based on completion type.
+  TransitTo(entry, Entry::State::COMPLETE, model_.get());
+  ActivateMoreDownloads();
+}
+
+void ControllerImpl::ActivateMoreDownloads() {
+  // TODO(xingliu): Check the configuration to throttle downloads.
+  Entry* next = scheduler_->Next(
+      model_->PeekEntries(), device_status_listener_->CurrentDeviceStatus());
+
+  while (next) {
+    DCHECK_EQ(Entry::State::AVAILABLE, next->state);
+    TransitTo(next, Entry::State::ACTIVE, model_.get());
+    UpdateDriverState(*next);
+    next = scheduler_->Next(model_->PeekEntries(),
+                            device_status_listener_->CurrentDeviceStatus());
+  }
+  scheduler_->Reschedule(model_->PeekEntries());
+}
+
 }  // namespace download
diff --git a/components/download/internal/controller_impl.h b/components/download/internal/controller_impl.h
index 3e7a852..fa151fe 100644
--- a/components/download/internal/controller_impl.h
+++ b/components/download/internal/controller_impl.h
@@ -12,6 +12,7 @@
 #include "base/optional.h"
 #include "components/download/internal/controller.h"
 #include "components/download/internal/download_driver.h"
+#include "components/download/internal/entry.h"
 #include "components/download/internal/model.h"
 #include "components/download/internal/scheduler/device_status_listener.h"
 #include "components/download/internal/startup_status.h"
@@ -24,6 +25,7 @@
 class ClientSet;
 class DownloadDriver;
 class Model;
+class Scheduler;
 
 struct Configuration;
 struct SchedulingParams;
@@ -41,6 +43,7 @@
                  std::unique_ptr<DownloadDriver> driver,
                  std::unique_ptr<Model> model,
                  std::unique_ptr<DeviceStatusListener> device_status_listener,
+                 std::unique_ptr<Scheduler> scheduler,
                  std::unique_ptr<TaskScheduler> task_scheduler);
   ~ControllerImpl() override;
 
@@ -94,6 +97,14 @@
   // resolve state issues during startup.
   void ResolveInitialRequestStates();
 
+  // Updates the driver states based on the states of entries in download
+  // service.
+  void UpdateDriverStates();
+
+  // Processes the download based on the state of |entry|. May start, pause
+  // or resume a download accordingly.
+  void UpdateDriverState(const Entry& entry);
+
   // Notifies all Client in |clients_| that this controller is initialized and
   // lets them know which download requests we are aware of for their
   // DownloadClient.
@@ -120,6 +131,12 @@
                           bool needs_reschedule,
                           stats::ScheduledTaskStatus status);
 
+  void HandleCompleteDownload(CompletionType type, const std::string& guid);
+
+  // Find more available entries to download, until the number of active entries
+  // reached maximum.
+  void ActivateMoreDownloads();
+
   std::unique_ptr<ClientSet> clients_;
   std::unique_ptr<Configuration> config_;
 
@@ -127,6 +144,7 @@
   std::unique_ptr<DownloadDriver> driver_;
   std::unique_ptr<Model> model_;
   std::unique_ptr<DeviceStatusListener> device_status_listener_;
+  std::unique_ptr<Scheduler> scheduler_;
   std::unique_ptr<TaskScheduler> task_scheduler_;
 
   // Internal state.
diff --git a/components/download/internal/controller_impl_unittest.cc b/components/download/internal/controller_impl_unittest.cc
index 6cecf73..7503be05 100644
--- a/components/download/internal/controller_impl_unittest.cc
+++ b/components/download/internal/controller_impl_unittest.cc
@@ -17,6 +17,7 @@
 #include "components/download/internal/config.h"
 #include "components/download/internal/entry.h"
 #include "components/download/internal/model_impl.h"
+#include "components/download/internal/scheduler/scheduler.h"
 #include "components/download/internal/test/entry_utils.h"
 #include "components/download/internal/test/mock_client.h"
 #include "components/download/internal/test/test_device_status_listener.h"
@@ -41,14 +42,27 @@
   MOCK_METHOD1(CancelTask, void(DownloadTaskType));
 };
 
+class MockScheduler : public Scheduler {
+ public:
+  MockScheduler() = default;
+  ~MockScheduler() override = default;
+
+  MOCK_METHOD1(Reschedule, void(const Model::EntryList&));
+  MOCK_METHOD2(Next, Entry*(const Model::EntryList&, const DeviceStatus&));
+};
+
 class DownloadServiceControllerImplTest : public testing::Test {
  public:
   DownloadServiceControllerImplTest()
       : task_runner_(new base::TestSimpleTaskRunner),
         handle_(task_runner_),
+        controller_(nullptr),
         client_(nullptr),
         driver_(nullptr),
-        store_(nullptr) {
+        store_(nullptr),
+        model_(nullptr),
+        device_status_listener_(nullptr),
+        scheduler_(nullptr) {
     start_callback_ =
         base::Bind(&DownloadServiceControllerImplTest::StartCallback,
                    base::Unretained(this));
@@ -71,13 +85,19 @@
     clients->insert(std::make_pair(DownloadClient::TEST, std::move(client)));
     auto client_set = base::MakeUnique<ClientSet>(std::move(clients));
     auto model = base::MakeUnique<ModelImpl>(std::move(store));
-    auto device_status_listener = base::MakeUnique<TestDeviceStatusListener>();
+    auto device_status_listener =
+        base::MakeUnique<test::TestDeviceStatusListener>();
+    auto scheduler = base::MakeUnique<MockScheduler>();
     auto task_scheduler = base::MakeUnique<MockTaskScheduler>();
 
+    model_ = model.get();
+    device_status_listener_ = device_status_listener.get();
+    scheduler_ = scheduler.get();
+
     controller_ = base::MakeUnique<ControllerImpl>(
         std::move(client_set), std::move(config), std::move(driver),
         std::move(model), std::move(device_status_listener),
-        std::move(task_scheduler));
+        std::move(scheduler), std::move(task_scheduler));
   }
 
  protected:
@@ -100,6 +120,9 @@
   test::MockClient* client_;
   test::TestDownloadDriver* driver_;
   test::TestStore* store_;
+  ModelImpl* model_;
+  test::TestDeviceStatusListener* device_status_listener_;
+  MockScheduler* scheduler_;
 
   DownloadParams::StartCallback start_callback_;
 
@@ -122,6 +145,8 @@
   EXPECT_TRUE(controller_->GetStartupStatus()->model_ok.value());
 
   EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
 
   driver_->MakeReady();
   EXPECT_TRUE(controller_->GetStartupStatus()->Complete());
@@ -144,6 +169,8 @@
   EXPECT_TRUE(controller_->GetStartupStatus()->driver_ok.value());
 
   EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
 
   store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>());
   EXPECT_TRUE(controller_->GetStartupStatus()->Complete());
@@ -165,6 +192,8 @@
   EXPECT_CALL(
       *client_,
       OnServiceInitialized(testing::UnorderedElementsAreArray(expected_guids)));
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
 
   controller_->Initialize();
   driver_->MakeReady();
@@ -188,6 +217,8 @@
   std::vector<Entry> entries = {entry};
 
   EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
 
   controller_->Initialize();
   driver_->MakeReady();
@@ -202,6 +233,8 @@
 
 TEST_F(DownloadServiceControllerImplTest, AddDownloadAccepted) {
   EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
 
   // Set up the Controller.
   controller_->Initialize();
@@ -214,6 +247,8 @@
   EXPECT_CALL(*this,
               StartCallback(params.guid, DownloadParams::StartResult::ACCEPTED))
       .Times(1);
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
   controller_->StartDownload(params);
 
   // TODO(dtrainor): Compare the full DownloadParams with the full Entry.
@@ -224,6 +259,8 @@
 
 TEST_F(DownloadServiceControllerImplTest, AddDownloadFailsWithBackoff) {
   EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
 
   Entry entry = test::BuildBasicEntry();
   std::vector<Entry> entries = {entry};
@@ -252,6 +289,8 @@
 TEST_F(DownloadServiceControllerImplTest,
        AddDownloadFailsWithDuplicateGuidInModel) {
   EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
 
   Entry entry = test::BuildBasicEntry();
   std::vector<Entry> entries = {entry};
@@ -280,6 +319,10 @@
   testing::InSequence sequence;
 
   EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
 
   // Set up the Controller.
   controller_->Initialize();
@@ -305,6 +348,8 @@
 
 TEST_F(DownloadServiceControllerImplTest, AddDownloadFailsWithBadClient) {
   EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
 
   // Set up the Controller.
   controller_->Initialize();
@@ -326,6 +371,8 @@
 
 TEST_F(DownloadServiceControllerImplTest, AddDownloadFailsWithClientCancel) {
   EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
 
   // Set up the Controller.
   controller_->Initialize();
@@ -349,6 +396,8 @@
 
 TEST_F(DownloadServiceControllerImplTest, AddDownloadFailsWithInternalError) {
   EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
 
   // Set up the Controller.
   controller_->Initialize();
@@ -368,4 +417,182 @@
   task_runner_->RunUntilIdle();
 }
 
+TEST_F(DownloadServiceControllerImplTest, Pause) {
+  // Setup download service test data.
+  Entry entry1 = test::BuildBasicEntry();
+  Entry entry2 = test::BuildBasicEntry();
+  Entry entry3 = test::BuildBasicEntry();
+  entry1.state = Entry::State::AVAILABLE;
+  entry2.state = Entry::State::ACTIVE;
+  entry3.state = Entry::State::COMPLETE;
+  std::vector<Entry> entries = {entry1, entry2, entry3};
+
+  EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(3);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(3);
+
+  // Set the network status to disconnected so no entries will be polled from
+  // the scheduler.
+  controller_->Initialize();
+  store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+  driver_->MakeReady();
+
+  // Setup download driver test data.
+  DriverEntry driver_entry1, driver_entry2, driver_entry3;
+  driver_entry1.guid = entry1.guid;
+  driver_entry1.state = DriverEntry::State::IN_PROGRESS;
+  driver_entry2.guid = entry2.guid;
+  driver_entry2.state = DriverEntry::State::IN_PROGRESS;
+  driver_entry3.guid = entry3.guid;
+  driver_->AddTestData(
+      std::vector<DriverEntry>{driver_entry1, driver_entry2, driver_entry3});
+
+  // Pause in progress available entry.
+  EXPECT_EQ(Entry::State::AVAILABLE, model_->Get(entry1.guid)->state);
+  controller_->PauseDownload(entry1.guid);
+  EXPECT_TRUE(driver_->Find(entry1.guid)->paused);
+  EXPECT_EQ(Entry::State::PAUSED, model_->Get(entry1.guid)->state);
+
+  // Pause in progress active entry.
+  controller_->PauseDownload(entry2.guid);
+  EXPECT_TRUE(driver_->Find(entry2.guid)->paused);
+  EXPECT_EQ(Entry::State::PAUSED, model_->Get(entry2.guid)->state);
+
+  // Entries in complete states can't be paused.
+  controller_->PauseDownload(entry3.guid);
+  EXPECT_FALSE(driver_->Find(entry3.guid)->paused);
+  EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entry3.guid)->state);
+
+  task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest, Resume) {
+  // Setupd download service test data.
+  Entry entry1 = test::BuildBasicEntry();
+  Entry entry2 = test::BuildBasicEntry();
+  entry1.state = Entry::State::PAUSED;
+  entry2.state = Entry::State::ACTIVE;
+  std::vector<Entry> entries = {entry1, entry2};
+
+  EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(2);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(2);
+
+  controller_->Initialize();
+  store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+  driver_->MakeReady();
+
+  // Setup download driver test data.
+  DriverEntry driver_entry1, driver_entry2;
+  driver_entry1.guid = entry1.guid;
+  driver_entry1.paused = true;
+  driver_entry2.guid = entry2.guid;
+  driver_entry2.paused = false;
+  driver_->AddTestData(std::vector<DriverEntry>{driver_entry1, driver_entry2});
+
+  // Resume the paused download.
+  device_status_listener_->SetDeviceStatus(
+      DeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(Entry::State::PAUSED, model_->Get(entry1.guid)->state);
+  controller_->ResumeDownload(entry1.guid);
+  EXPECT_FALSE(driver_->Find(entry1.guid)->paused);
+  EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry1.guid)->state);
+
+  // Entries in paused state can't be resumed.
+  controller_->ResumeDownload(entry2.guid);
+  EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry2.guid)->state);
+  EXPECT_FALSE(driver_->Find(entry2.guid)->paused);
+
+  task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest, Cancel) {
+  Entry entry = test::BuildBasicEntry();
+  entry.state = Entry::State::ACTIVE;
+  std::vector<Entry> entries = {entry};
+
+  EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(2);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(2);
+
+  controller_->Initialize();
+  store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+  driver_->MakeReady();
+
+  DriverEntry driver_entry;
+  driver_entry.guid = entry.guid;
+  driver_->AddTestData(std::vector<DriverEntry>{driver_entry});
+
+  controller_->CancelDownload(entry.guid);
+  EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entry.guid)->state);
+
+  task_runner_->RunUntilIdle();
+}
+
+TEST_F(DownloadServiceControllerImplTest, OnDownloadFailed) {
+  Entry entry = test::BuildBasicEntry();
+  entry.state = Entry::State::ACTIVE;
+  std::vector<Entry> entries = {entry};
+
+  EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(2);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(2);
+
+  controller_->Initialize();
+  store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+  driver_->MakeReady();
+
+  DriverEntry driver_entry;
+  driver_entry.guid = entry.guid;
+
+  driver_->NotifyDownloadFailed(driver_entry, 1);
+  EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entry.guid)->state);
+}
+
+TEST_F(DownloadServiceControllerImplTest, OnDownloadSucceeded) {
+  Entry entry = test::BuildBasicEntry();
+  entry.state = Entry::State::ACTIVE;
+  std::vector<Entry> entries = {entry};
+
+  EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(2);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(2);
+
+  controller_->Initialize();
+  store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+  driver_->MakeReady();
+
+  DriverEntry driver_entry;
+  driver_entry.guid = entry.guid;
+  driver_entry.bytes_downloaded = 1024;
+  base::FilePath path = base::FilePath::FromUTF8Unsafe("123");
+
+  driver_->NotifyDownloadSucceeded(driver_entry, path);
+  EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entry.guid)->state);
+}
+
+TEST_F(DownloadServiceControllerImplTest, OnDownloadUpdated) {
+  Entry entry = test::BuildBasicEntry();
+  entry.state = Entry::State::ACTIVE;
+  std::vector<Entry> entries = {entry};
+
+  EXPECT_CALL(*client_, OnServiceInitialized(_)).Times(1);
+  EXPECT_CALL(*scheduler_, Next(_, _)).Times(1);
+  EXPECT_CALL(*scheduler_, Reschedule(_)).Times(1);
+
+  controller_->Initialize();
+  store_->TriggerInit(true, base::MakeUnique<std::vector<Entry>>(entries));
+  driver_->MakeReady();
+
+  DriverEntry driver_entry;
+  driver_entry.state = DriverEntry::State::IN_PROGRESS;
+  driver_entry.guid = entry.guid;
+  driver_entry.bytes_downloaded = 1024;
+
+  EXPECT_CALL(*client_,
+              OnDownloadUpdated(entry.guid, driver_entry.bytes_downloaded));
+  driver_->NotifyDownloadUpdate(driver_entry);
+  EXPECT_EQ(Entry::State::ACTIVE, model_->Get(entry.guid)->state);
+}
+
 }  // namespace download
diff --git a/components/download/internal/download_driver.h b/components/download/internal/download_driver.h
index 704233ec..149e4160 100644
--- a/components/download/internal/download_driver.h
+++ b/components/download/internal/download_driver.h
@@ -17,7 +17,7 @@
 
 namespace download {
 
-struct DownloadParams;
+struct RequestParams;
 
 // The interface that includes all the operations to interact with low level
 // download library functionalities.
@@ -52,19 +52,20 @@
   // Initialize the driver to receive download updates.
   virtual void Initialize(Client* client) = 0;
 
-  // Returns if the driver is ready. Returns false when the driver is not
-  // initialized by the client, or low level download library has been shut
-  // down.
+  // Returns if the driver is ready after the low level library has loaded all
+  // the data. Returns false when the driver is not initialized by the client,
+  // or low level download library has been shut down.
   virtual bool IsReady() const = 0;
 
   // Starts a new download.
   virtual void Start(
-      const DownloadParams& params,
+      const RequestParams& request_params,
+      const std::string& guid,
       const net::NetworkTrafficAnnotationTag& traffic_annotation) = 0;
 
   // Cancels an existing download, all data associated with this download should
   // be removed.
-  virtual void Cancel(const std::string& guid) = 0;
+  virtual void Remove(const std::string& guid) = 0;
 
   // Pauses the download.
   virtual void Pause(const std::string& guid) = 0;
@@ -72,7 +73,7 @@
   // Resumes the download
   virtual void Resume(const std::string& guid) = 0;
 
-  // Find a download record from low level download library.
+  // Finds a download record from low level download library.
   virtual base::Optional<DriverEntry> Find(const std::string& guid) = 0;
 };
 
diff --git a/components/download/internal/driver_entry.h b/components/download/internal/driver_entry.h
index a21a431..52f421b 100644
--- a/components/download/internal/driver_entry.h
+++ b/components/download/internal/driver_entry.h
@@ -6,8 +6,10 @@
 #define COMPONENTS_DOWNLOAD_INTERNAL_DRIVER_ENTRY_H_
 
 #include <string>
+#include <vector>
 
 #include "base/memory/ref_counted.h"
+#include "url/gurl.h"
 
 namespace net {
 class HttpResponseHeaders;
@@ -54,6 +56,10 @@
 
   // The response headers for the most recent download request.
   scoped_refptr<const net::HttpResponseHeaders> response_headers;
+
+  // The url chain of the download. Download may encounter redirects, and
+  // fetches the content from the last url in the chain.
+  std::vector<GURL> url_chain;
 };
 
 }  // namespace download
diff --git a/components/download/internal/entry_utils.cc b/components/download/internal/entry_utils.cc
index b02ec79..211f8ae2 100644
--- a/components/download/internal/entry_utils.cc
+++ b/components/download/internal/entry_utils.cc
@@ -52,5 +52,12 @@
   return criteria;
 }
 
+bool EntryBetterThan(const Entry& lhs, const Entry& rhs) {
+  return lhs.scheduling_params.priority > rhs.scheduling_params.priority ||
+         (lhs.scheduling_params.priority == rhs.scheduling_params.priority &&
+          lhs.scheduling_params.cancel_time <
+              rhs.scheduling_params.cancel_time);
+}
+
 }  // namespace util
 }  // namespace download
diff --git a/components/download/internal/entry_utils.h b/components/download/internal/entry_utils.h
index 7e46cdba..a50244b 100644
--- a/components/download/internal/entry_utils.h
+++ b/components/download/internal/entry_utils.h
@@ -33,10 +33,14 @@
     const std::set<DownloadClient>& clients,
     const std::vector<Entry*>& entries);
 
-// Get the least strict scheduling criteria from |entries|, the criteria is used
-// to schedule platform background tasks.
+// Gets the least strict scheduling criteria from |entries|, the criteria is
+// used to schedule platform background tasks.
 Criteria GetSchedulingCriteria(const Model::EntryList& entries);
 
+// Returns if |lhs| entry is a better candidate to be the next download than
+// |rhs| based on their priority and cancel time.
+bool EntryBetterThan(const Entry& lhs, const Entry& rhs);
+
 }  // namespace util
 }  // namespace download
 
diff --git a/components/download/internal/scheduler/device_status.cc b/components/download/internal/scheduler/device_status.cc
index 54fc152..b206ddf 100644
--- a/components/download/internal/scheduler/device_status.cc
+++ b/components/download/internal/scheduler/device_status.cc
@@ -17,6 +17,9 @@
     : battery_status(BatteryStatus::NOT_CHARGING),
       network_status(NetworkStatus::DISCONNECTED) {}
 
+DeviceStatus::DeviceStatus(BatteryStatus battery, NetworkStatus network)
+    : battery_status(battery), network_status(network) {}
+
 bool DeviceStatus::operator==(const DeviceStatus& rhs) const {
   return network_status == rhs.network_status &&
          battery_status == rhs.battery_status;
diff --git a/components/download/internal/scheduler/device_status.h b/components/download/internal/scheduler/device_status.h
index c1870313..43c3b22 100644
--- a/components/download/internal/scheduler/device_status.h
+++ b/components/download/internal/scheduler/device_status.h
@@ -27,6 +27,8 @@
 // Contains battery and network status.
 struct DeviceStatus {
   DeviceStatus();
+  DeviceStatus(BatteryStatus battery, NetworkStatus network);
+
   struct Result {
     Result();
     bool MeetsRequirements() const;
diff --git a/components/download/internal/scheduler/device_status_listener.h b/components/download/internal/scheduler/device_status_listener.h
index b02e169..4a94608 100644
--- a/components/download/internal/scheduler/device_status_listener.h
+++ b/components/download/internal/scheduler/device_status_listener.h
@@ -40,6 +40,9 @@
   // The observer that listens to device status change events.
   Observer* observer_;
 
+  // If we are actively listening to network and battery change events.
+  bool listening_;
+
  private:
   // net::NetworkChangeNotifier implementation.
   void OnConnectionTypeChanged(
@@ -51,9 +54,6 @@
   // Notifies |observers_| about device status change.
   void NotifyStatusChange();
 
-  // If we are actively listening to network and battery change events.
-  bool listening_;
-
   DISALLOW_COPY_AND_ASSIGN(DeviceStatusListener);
 };
 
diff --git a/components/download/internal/scheduler/scheduler.h b/components/download/internal/scheduler/scheduler.h
index 3cb0034..36e0d49 100644
--- a/components/download/internal/scheduler/scheduler.h
+++ b/components/download/internal/scheduler/scheduler.h
@@ -26,6 +26,8 @@
   // The sequence of polling on entries with exactly same states is undefined.
   virtual Entry* Next(const Model::EntryList& entries,
                       const DeviceStatus& device_status) = 0;
+
+  virtual ~Scheduler() {}
 };
 
 // Interface to schedule platform dependent background tasks that can run after
@@ -34,6 +36,7 @@
  public:
   virtual void ScheduleDownloadTask(const Criteria& criteria) = 0;
   virtual void CancelDownloadTask() = 0;
+  virtual ~PlatformTaskScheduler() {}
 };
 
 }  // namespace download
diff --git a/components/download/internal/scheduler/scheduler_impl.cc b/components/download/internal/scheduler/scheduler_impl.cc
index 1233dda..3a638b6 100644
--- a/components/download/internal/scheduler/scheduler_impl.cc
+++ b/components/download/internal/scheduler/scheduler_impl.cc
@@ -4,12 +4,32 @@
 
 #include "components/download/internal/scheduler/scheduler_impl.h"
 
+#include "components/download/internal/client_set.h"
 #include "components/download/internal/entry_utils.h"
 #include "components/download/internal/scheduler/device_status.h"
 #include "components/download/public/download_params.h"
 
 namespace download {
 
+namespace {
+
+// Returns a vector of elements contained in the |set|.
+template <typename T>
+std::vector<T> ToList(const std::set<T>& set) {
+  std::vector<T> list;
+  for (const auto& element : set) {
+    list.push_back(element);
+  }
+  return list;
+}
+
+}  // namespace
+
+SchedulerImpl::SchedulerImpl(PlatformTaskScheduler* platform_scheduler,
+                             const ClientSet* clients)
+    : SchedulerImpl(platform_scheduler,
+                    ToList<DownloadClient>(clients->GetRegisteredClients())) {}
+
 SchedulerImpl::SchedulerImpl(PlatformTaskScheduler* platform_scheduler,
                              const std::vector<DownloadClient>& clients)
     : platform_scheduler_(platform_scheduler),
@@ -61,7 +81,6 @@
     // are UI priority entries for other clients.
     if (!entry || ui_priority) {
       entry = candidate;
-      DCHECK(entry);
 
       // Load balancing between clients.
       current_client_index_ = (index + i + 1) % download_clients_.size();
@@ -94,11 +113,7 @@
 
     // Find the most appropriate download based on priority and cancel time.
     Entry* candidate = candidates[entry->client];
-    if (!candidate ||
-        (current_params.priority > candidate->scheduling_params.priority ||
-         (current_params.priority == candidate->scheduling_params.priority &&
-          entry->scheduling_params.cancel_time <
-              candidate->scheduling_params.cancel_time))) {
+    if (!candidate || util::EntryBetterThan(*entry, *candidate)) {
       candidates[entry->client] = entry;
     }
   }
diff --git a/components/download/internal/scheduler/scheduler_impl.h b/components/download/internal/scheduler/scheduler_impl.h
index 30c2969..cfac7cd 100644
--- a/components/download/internal/scheduler/scheduler_impl.h
+++ b/components/download/internal/scheduler/scheduler_impl.h
@@ -15,6 +15,8 @@
 
 namespace download {
 
+class ClientSet;
+
 // Scheduler implementation that
 // 1. Creates platform background task based on the states of download entries.
 // 2. Polls the next entry to be processed by the service mainly according to
@@ -24,8 +26,10 @@
 class SchedulerImpl : public Scheduler {
  public:
   SchedulerImpl(PlatformTaskScheduler* platform_scheduler,
+                const ClientSet* clients);
+  SchedulerImpl(PlatformTaskScheduler* platform_scheduler,
                 const std::vector<DownloadClient>& clients);
-  ~SchedulerImpl();
+  ~SchedulerImpl() override;
 
   // Scheduler implementation.
   void Reschedule(const Model::EntryList& entries) override;
diff --git a/components/download/internal/stats.cc b/components/download/internal/stats.cc
index 762204d..b532f879 100644
--- a/components/download/internal/stats.cc
+++ b/components/download/internal/stats.cc
@@ -34,5 +34,9 @@
   // TODO(shaktisahu): Log |task_type| and |status|.
 }
 
+void LogDownloadCompletion(CompletionType type) {
+  // TODO(xingliu): Log completion.
+}
+
 }  // namespace stats
 }  // namespace download
diff --git a/components/download/internal/stats.h b/components/download/internal/stats.h
index 4ccae0c..7a9cecb 100644
--- a/components/download/internal/stats.h
+++ b/components/download/internal/stats.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_DOWNLOAD_INTERNAL_STATS_H_
 #define COMPONENTS_DOWNLOAD_INTERNAL_STATS_H_
 
+#include "components/download/internal/controller.h"
 #include "components/download/public/clients.h"
 #include "components/download/public/download_params.h"
 #include "components/download/public/download_task_types.h"
@@ -92,6 +93,9 @@
 void LogScheduledTaskStatus(DownloadTaskType task_type,
                             ScheduledTaskStatus status);
 
+// Logs download completion event.
+void LogDownloadCompletion(CompletionType type);
+
 }  // namespace stats
 }  // namespace download
 
diff --git a/components/download/internal/test/test_device_status_listener.cc b/components/download/internal/test/test_device_status_listener.cc
index 7f68952..d7dc52b 100644
--- a/components/download/internal/test/test_device_status_listener.cc
+++ b/components/download/internal/test/test_device_status_listener.cc
@@ -5,10 +5,15 @@
 #include "components/download/internal/test/test_device_status_listener.h"
 
 namespace download {
+namespace test {
 
 TestDeviceStatusListener::TestDeviceStatusListener() = default;
 
-TestDeviceStatusListener::~TestDeviceStatusListener() = default;
+TestDeviceStatusListener::~TestDeviceStatusListener() {
+  // Mark |listening_| to false to bypass the remove observer calls in the base
+  // class.
+  Stop();
+}
 
 void TestDeviceStatusListener::NotifyObserver(
     const DeviceStatus& device_status) {
@@ -17,13 +22,20 @@
   observer_->OnDeviceStatusChanged(status_);
 }
 
+void TestDeviceStatusListener::SetDeviceStatus(const DeviceStatus& status) {
+  status_ = status;
+}
+
 void TestDeviceStatusListener::Start(DeviceStatusListener::Observer* observer) {
+  listening_ = true;
   observer_ = observer;
 }
 
 void TestDeviceStatusListener::Stop() {
   status_ = DeviceStatus();
   observer_ = nullptr;
+  listening_ = false;
 }
 
+}  // namespace test
 }  // namespace download
diff --git a/components/download/internal/test/test_device_status_listener.h b/components/download/internal/test/test_device_status_listener.h
index 72472eca..1f8bb22 100644
--- a/components/download/internal/test/test_device_status_listener.h
+++ b/components/download/internal/test/test_device_status_listener.h
@@ -8,6 +8,7 @@
 #include "components/download/internal/scheduler/device_status_listener.h"
 
 namespace download {
+namespace test {
 
 // Test device status listener can directly notify the observer about battery
 // and network changes, without calling external class methods.
@@ -19,6 +20,9 @@
   // Notifies observer with current device status.
   void NotifyObserver(const DeviceStatus& device_status);
 
+  // Sets the device status without notifying the observer.
+  void SetDeviceStatus(const DeviceStatus& status);
+
   // DeviceStatusListener implementation.
   void Start(DeviceStatusListener::Observer* observer) override;
   void Stop() override;
@@ -27,6 +31,7 @@
   DISALLOW_COPY_AND_ASSIGN(TestDeviceStatusListener);
 };
 
+}  // namespace test
 }  // namespace download
 
 #endif  // COMPONENTS_DOWNLOAD_INTERNAL_TEST_TEST_DEVICE_STATUS_LISTENER_H_
diff --git a/components/download/internal/test/test_download_driver.cc b/components/download/internal/test/test_download_driver.cc
index beffa6a69d..c9f11a4 100644
--- a/components/download/internal/test/test_download_driver.cc
+++ b/components/download/internal/test/test_download_driver.cc
@@ -22,6 +22,13 @@
     client_->OnDriverReady(is_ready_);
 }
 
+void TestDownloadDriver::AddTestData(const std::vector<DriverEntry>& entries) {
+  for (const auto& entry : entries) {
+    DCHECK(entries_.find(entry.guid) == entries_.end()) << "Existing guid.";
+    entries_.emplace(entry.guid, entry);
+  }
+}
+
 void TestDownloadDriver::NotifyDownloadUpdate(const DriverEntry& entry) {
   if (client_) {
     entries_[entry.guid] = entry;
@@ -55,27 +62,40 @@
 }
 
 void TestDownloadDriver::Start(
-    const DownloadParams& params,
+    const RequestParams& params,
+    const std::string& guid,
     const net::NetworkTrafficAnnotationTag& traffic_annotation) {
   DriverEntry entry;
-  entry.guid = params.guid;
+  entry.guid = guid;
   entry.state = DriverEntry::State::IN_PROGRESS;
   entry.paused = false;
   entry.bytes_downloaded = 0;
   entry.expected_total_size = 0;
   entry.response_headers =
       base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200");
-  entries_[params.guid] = entry;
+  entries_[guid] = entry;
 
   if (client_)
     client_->OnDownloadCreated(entry);
 }
 
-void TestDownloadDriver::Cancel(const std::string& guid) {}
+void TestDownloadDriver::Remove(const std::string& guid) {
+  entries_.erase(guid);
+}
 
-void TestDownloadDriver::Pause(const std::string& guid) {}
+void TestDownloadDriver::Pause(const std::string& guid) {
+  auto it = entries_.find(guid);
+  if (it == entries_.end())
+    return;
+  it->second.paused = true;
+}
 
-void TestDownloadDriver::Resume(const std::string& guid) {}
+void TestDownloadDriver::Resume(const std::string& guid) {
+  auto it = entries_.find(guid);
+  if (it == entries_.end())
+    return;
+  it->second.paused = false;
+}
 
 base::Optional<DriverEntry> TestDownloadDriver::Find(const std::string& guid) {
   auto it = entries_.find(guid);
diff --git a/components/download/internal/test/test_download_driver.h b/components/download/internal/test/test_download_driver.h
index 5e540f65..05fd808 100644
--- a/components/download/internal/test/test_download_driver.h
+++ b/components/download/internal/test/test_download_driver.h
@@ -25,6 +25,9 @@
   // data initialization.
   void MakeReady();
 
+  // Adds driver entries data that will be returned
+  void AddTestData(const std::vector<DriverEntry>& entries);
+
   // Simulates download events from content layer.
   void NotifyDownloadUpdate(const DriverEntry& entry);
   void NotifyDownloadFailed(const DriverEntry& entry, int reason);
@@ -35,9 +38,10 @@
   void Initialize(DownloadDriver::Client* client) override;
   bool IsReady() const override;
   void Start(
-      const DownloadParams& params,
+      const RequestParams& params,
+      const std::string& guid,
       const net::NetworkTrafficAnnotationTag& traffic_annotation) override;
-  void Cancel(const std::string& guid) override;
+  void Remove(const std::string& guid) override;
   void Pause(const std::string& guid) override;
   void Resume(const std::string& guid) override;
   base::Optional<DriverEntry> Find(const std::string& guid) override;
diff --git a/components/download/public/client.h b/components/download/public/client.h
index 28264e7..43349b6 100644
--- a/components/download/public/client.h
+++ b/components/download/public/client.h
@@ -58,7 +58,7 @@
   // DownloadParams::cancel_after timeout.
   virtual void OnDownloadTimedOut(const std::string& guid) = 0;
 
-  // Called when the download has been aborted after reaching a treshold where
+  // Called when the download has been aborted after reaching a threshold where
   // we decide it is not worth attempting to start again.  This could be either
   // due to a specific number of failed retry attempts or a specific number of
   // wasted bytes due to the download restarting.
diff --git a/components/download/public/download_params.cc b/components/download/public/download_params.cc
index f24d467..8d94dcf 100644
--- a/components/download/public/download_params.cc
+++ b/components/download/public/download_params.cc
@@ -13,6 +13,12 @@
       network_requirements(NetworkRequirements::NONE),
       battery_requirements(BatteryRequirements::BATTERY_INSENSITIVE) {}
 
+bool SchedulingParams::operator==(const SchedulingParams& rhs) const {
+  return network_requirements == rhs.network_requirements &&
+         battery_requirements == rhs.battery_requirements &&
+         priority == rhs.priority && cancel_time == rhs.cancel_time;
+}
+
 RequestParams::RequestParams() : method("GET") {}
 
 DownloadParams::DownloadParams() : client(DownloadClient::INVALID) {}
diff --git a/components/download/public/download_params.h b/components/download/public/download_params.h
index e9885e9..abd18771 100644
--- a/components/download/public/download_params.h
+++ b/components/download/public/download_params.h
@@ -67,6 +67,8 @@
   SchedulingParams(const SchedulingParams& other) = default;
   ~SchedulingParams() = default;
 
+  bool operator==(const SchedulingParams& rhs) const;
+
   // Cancel the download after this time.  Will cancel in-progress downloads.
   base::Time cancel_time;
 
diff --git a/components/gcm_driver/BUILD.gn b/components/gcm_driver/BUILD.gn
index 2a549fa8..4b61384 100644
--- a/components/gcm_driver/BUILD.gn
+++ b/components/gcm_driver/BUILD.gn
@@ -2,12 +2,16 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/buildflag_header.gni")
+import("//components/gcm_driver/config.gni")
+
+buildflag_header("gcm_build_features") {
+  header = "gcm_build_features.h"
+  flags = [ "USE_GCM_FROM_PLATFORM=$use_gcm_from_platform" ]
+}
+
 static_library("gcm_driver") {
   sources = [
-    "android/component_jni_registrar.cc",
-    "android/component_jni_registrar.h",
-    "gcm_account_mapper.cc",
-    "gcm_account_mapper.h",
     "gcm_account_tracker.cc",
     "gcm_account_tracker.h",
     "gcm_activity.cc",
@@ -16,40 +20,22 @@
     "gcm_app_handler.h",
     "gcm_backoff_policy.cc",
     "gcm_backoff_policy.h",
-    "gcm_channel_status_request.cc",
-    "gcm_channel_status_request.h",
-    "gcm_channel_status_syncer.cc",
-    "gcm_channel_status_syncer.h",
     "gcm_client.cc",
     "gcm_client.h",
-    "gcm_client_factory.cc",
-    "gcm_client_factory.h",
-    "gcm_client_impl.cc",
-    "gcm_client_impl.h",
     "gcm_connection_observer.cc",
     "gcm_connection_observer.h",
     "gcm_delayed_task_controller.cc",
     "gcm_delayed_task_controller.h",
-    "gcm_desktop_utils.cc",
-    "gcm_desktop_utils.h",
     "gcm_driver.cc",
     "gcm_driver.h",
-    "gcm_driver_android.cc",
-    "gcm_driver_android.h",
     "gcm_driver_constants.cc",
     "gcm_driver_constants.h",
-    "gcm_driver_desktop.cc",
-    "gcm_driver_desktop.h",
     "gcm_internals_constants.cc",
     "gcm_internals_constants.h",
     "gcm_internals_helper.cc",
     "gcm_internals_helper.h",
     "gcm_profile_service.cc",
     "gcm_profile_service.h",
-    "gcm_stats_recorder_android.cc",
-    "gcm_stats_recorder_android.h",
-    "gcm_stats_recorder_impl.cc",
-    "gcm_stats_recorder_impl.h",
     "registration_info.cc",
     "registration_info.h",
     "system_encryptor.cc",
@@ -63,6 +49,7 @@
     "//components/gcm_driver/instance_id",
   ]
   deps = [
+    ":gcm_build_features",
     "//base:i18n",
     "//components/crx_file",
     "//components/data_use_measurement/core",
@@ -90,8 +77,17 @@
     deps += [ "//components/timers" ]
   }
 
-  if (is_android) {
-    sources -= [
+  if (use_gcm_from_platform) {
+    sources += [
+      "android/component_jni_registrar.cc",
+      "android/component_jni_registrar.h",
+      "gcm_driver_android.cc",
+      "gcm_driver_android.h",
+      "gcm_stats_recorder_android.cc",
+      "gcm_stats_recorder_android.h",
+    ]
+  } else {
+    sources += [
       "gcm_account_mapper.cc",
       "gcm_account_mapper.h",
       "gcm_channel_status_request.cc",
@@ -109,10 +105,13 @@
       "gcm_stats_recorder_impl.cc",
       "gcm_stats_recorder_impl.h",
     ]
-    deps -= [
+    deps += [
       "//components/crx_file",
       "//google_apis/gcm",
     ]
+  }
+
+  if (is_android) {
     deps += [ "android:jni_headers" ]
   }
 }
@@ -122,10 +121,6 @@
   sources = [
     "fake_gcm_app_handler.cc",
     "fake_gcm_app_handler.h",
-    "fake_gcm_client.cc",
-    "fake_gcm_client.h",
-    "fake_gcm_client_factory.cc",
-    "fake_gcm_client_factory.h",
     "fake_gcm_driver.cc",
     "fake_gcm_driver.h",
   ]
@@ -140,14 +135,14 @@
     "//testing/gtest",
   ]
 
-  if (is_android) {
-    sources -= [
+  if (!use_gcm_from_platform) {
+    sources += [
       "fake_gcm_client.cc",
       "fake_gcm_client.h",
       "fake_gcm_client_factory.cc",
       "fake_gcm_client_factory.h",
     ]
-    deps -= [ "//google_apis/gcm:test_support" ]
+    deps += [ "//google_apis/gcm:test_support" ]
   }
 }
 
@@ -155,14 +150,9 @@
   testonly = true
 
   sources = [
-    "gcm_account_mapper_unittest.cc",
     "gcm_account_tracker_unittest.cc",
-    "gcm_channel_status_request_unittest.cc",
-    "gcm_client_impl_unittest.cc",
     "gcm_delayed_task_controller_unittest.cc",
-    "gcm_driver_desktop_unittest.cc",
     "gcm_stats_recorder_android_unittest.cc",
-    "gcm_stats_recorder_impl_unittest.cc",
   ]
 
   deps = [
@@ -180,8 +170,8 @@
     "//third_party/protobuf:protobuf_lite",
   ]
 
-  if (is_android) {
-    sources -= [
+  if (!use_gcm_from_platform) {
+    sources += [
       "gcm_account_mapper_unittest.cc",
       "gcm_channel_status_request_unittest.cc",
       "gcm_client_impl_unittest.cc",
diff --git a/components/gcm_driver/config.gni b/components/gcm_driver/config.gni
new file mode 100644
index 0000000..9620f1a
--- /dev/null
+++ b/components/gcm_driver/config.gni
@@ -0,0 +1,9 @@
+# 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.
+
+declare_args() {
+  # Use native GCM driver for all non-Android builds. On Android, the platform
+  # includes GMS which provides the GCM client.
+  use_gcm_from_platform = is_android
+}
diff --git a/components/gcm_driver/gcm_profile_service.cc b/components/gcm_driver/gcm_profile_service.cc
index c113aa2..5a2c7f0 100644
--- a/components/gcm_driver/gcm_profile_service.cc
+++ b/components/gcm_driver/gcm_profile_service.cc
@@ -15,7 +15,7 @@
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(USE_GCM_FROM_PLATFORM)
 #include "base/sequenced_task_runner.h"
 #include "base/threading/sequenced_worker_pool.h"
 #include "components/gcm_driver/gcm_driver_android.h"
@@ -36,7 +36,7 @@
 
 namespace gcm {
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(USE_GCM_FROM_PLATFORM)
 // Identity observer only has actual work to do when the user is actually signed
 // in. It ensures that account tracker is taking
 class GCMProfileService::IdentityObserver : public IdentityProvider::Observer {
@@ -117,18 +117,18 @@
   gcm_account_tracker_->Start();
 }
 
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(USE_GCM_FROM_PLATFORM)
 
 // static
 bool GCMProfileService::IsGCMEnabled(PrefService* prefs) {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(USE_GCM_FROM_PLATFORM)
   return true;
 #else
   return prefs->GetBoolean(gcm::prefs::kGCMChannelStatus);
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(USE_GCM_FROM_PLATFORM)
 }
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(USE_GCM_FROM_PLATFORM)
 GCMProfileService::GCMProfileService(
     base::FilePath path,
     scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner) {
@@ -158,16 +158,16 @@
   identity_observer_.reset(new IdentityObserver(
       profile_identity_provider_.get(), request_context_, driver_.get()));
 }
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(USE_GCM_FROM_PLATFORM)
 
 GCMProfileService::GCMProfileService() {}
 
 GCMProfileService::~GCMProfileService() {}
 
 void GCMProfileService::Shutdown() {
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(USE_GCM_FROM_PLATFORM)
   identity_observer_.reset();
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(USE_GCM_FROM_PLATFORM)
   if (driver_) {
     driver_->Shutdown();
     driver_.reset();
@@ -176,12 +176,12 @@
 
 void GCMProfileService::SetDriverForTesting(GCMDriver* driver) {
   driver_.reset(driver);
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(USE_GCM_FROM_PLATFORM)
   if (identity_observer_) {
     identity_observer_.reset(new IdentityObserver(
         profile_identity_provider_.get(), request_context_, driver));
   }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(USE_GCM_FROM_PLATFORM)
 }
 
 }  // namespace gcm
diff --git a/components/gcm_driver/gcm_profile_service.h b/components/gcm_driver/gcm_profile_service.h
index b3305b6..4886b4e 100644
--- a/components/gcm_driver/gcm_profile_service.h
+++ b/components/gcm_driver/gcm_profile_service.h
@@ -14,6 +14,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "build/build_config.h"
+#include "components/gcm_driver/gcm_build_features.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/signin/core/browser/profile_identity_provider.h"
 #include "components/version_info/version_info.h"
@@ -36,7 +37,7 @@
 // Providing GCM service, via GCMDriver.
 class GCMProfileService : public KeyedService {
  public:
-#if defined(OS_ANDROID)
+#if BUILDFLAG(USE_GCM_FROM_PLATFORM)
   GCMProfileService(
       base::FilePath path,
       scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner);
@@ -74,7 +75,7 @@
   std::unique_ptr<ProfileIdentityProvider> profile_identity_provider_;
   std::unique_ptr<GCMDriver> driver_;
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(USE_GCM_FROM_PLATFORM)
   net::URLRequestContextGetter* request_context_ = nullptr;
 
   // Used for both account tracker and GCM.UserSignedIn UMA.
diff --git a/components/gcm_driver/instance_id/BUILD.gn b/components/gcm_driver/instance_id/BUILD.gn
index b77c034..ad09767 100644
--- a/components/gcm_driver/instance_id/BUILD.gn
+++ b/components/gcm_driver/instance_id/BUILD.gn
@@ -2,10 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//components/gcm_driver/config.gni")
+
 static_library("instance_id") {
   sources = [
-    "android/component_jni_registrar.cc",
-    "android/component_jni_registrar.h",
     "instance_id.cc",
     "instance_id.h",
     "instance_id_driver.cc",
@@ -19,12 +19,14 @@
     "//crypto",
   ]
 
-  if (is_android) {
+  if (use_gcm_from_platform) {
     sources -= [
       "instance_id_impl.cc",
       "instance_id_impl.h",
     ]
     sources += [
+      "android/component_jni_registrar.cc",
+      "android/component_jni_registrar.h",
       "instance_id_android.cc",
       "instance_id_android.h",
     ]
@@ -65,6 +67,7 @@
     ":instance_id",
     ":test_support",
     "//base",
+    "//components/gcm_driver:gcm_build_features",
     "//google_apis/gcm",
     "//net:test_support",
     "//testing/gtest",
diff --git a/components/gcm_driver/instance_id/instance_id_driver_unittest.cc b/components/gcm_driver/instance_id/instance_id_driver_unittest.cc
index b8c663fe..86f275e1 100644
--- a/components/gcm_driver/instance_id/instance_id_driver_unittest.cc
+++ b/components/gcm_driver/instance_id/instance_id_driver_unittest.cc
@@ -13,14 +13,15 @@
 #include "base/run_loop.h"
 #include "base/strings/string_util.h"
 #include "base/test/scoped_task_environment.h"
+#include "components/gcm_driver/gcm_build_features.h"
 #include "components/gcm_driver/instance_id/fake_gcm_driver_for_instance_id.h"
 #include "components/gcm_driver/instance_id/instance_id.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(USE_GCM_FROM_PLATFORM)
 #include "components/gcm_driver/instance_id/instance_id_android.h"
 #include "components/gcm_driver/instance_id/scoped_use_fake_instance_id_android.h"
-#endif  // OS_ANDROID
+#endif  // BUILDFLAG(USE_GCM_FROM_PLATFORM)
 
 namespace instance_id {
 
@@ -91,10 +92,10 @@
   std::unique_ptr<FakeGCMDriverForInstanceID> gcm_driver_;
   std::unique_ptr<InstanceIDDriver> driver_;
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(USE_GCM_FROM_PLATFORM)
   InstanceIDAndroid::ScopedBlockOnAsyncTasksForTesting block_async_;
   ScopedUseFakeInstanceIDAndroid use_fake_;
-#endif  // OS_ANDROID
+#endif  // BUILDFLAG(USE_GCM_FROM_PLATFORM)
 
   std::string id_;
   base::Time creation_time_;
diff --git a/components/metrics/file_metrics_provider_unittest.cc b/components/metrics/file_metrics_provider_unittest.cc
index 2b6fbdb..42e38bc7 100644
--- a/components/metrics/file_metrics_provider_unittest.cc
+++ b/components/metrics/file_metrics_provider_unittest.cc
@@ -496,7 +496,7 @@
         PersistentSystemProfile persistent_profile;
         persistent_profile.RegisterPersistentAllocator(
             allocator->memory_allocator());
-        persistent_profile.SetSystemProfile(profile_proto);
+        persistent_profile.SetSystemProfile(profile_proto, true);
       });
 
   // Register the file and allow the "checker" task to run.
@@ -567,7 +567,7 @@
         PersistentSystemProfile persistent_profile;
         persistent_profile.RegisterPersistentAllocator(
             allocator->memory_allocator());
-        persistent_profile.SetSystemProfile(profile_proto);
+        persistent_profile.SetSystemProfile(profile_proto, true);
       });
 
   // Register the file and allow the "checker" task to run.
diff --git a/components/metrics/metrics_log.cc b/components/metrics/metrics_log.cc
index 4d3123c..ba570a7 100644
--- a/components/metrics/metrics_log.cc
+++ b/components/metrics/metrics_log.cc
@@ -123,7 +123,12 @@
   if (product != uma_proto_.product())
     uma_proto_.set_product(product);
 
-  RecordCoreSystemProfile(client_, uma_proto_.mutable_system_profile());
+  SystemProfileProto* system_profile = uma_proto()->mutable_system_profile();
+  RecordCoreSystemProfile(client_, system_profile);
+  if (log_type_ == ONGOING_LOG) {
+    GlobalPersistentSystemProfile::GetInstance()->SetSystemProfile(
+        *system_profile, /*complete=*/false);
+  }
 }
 
 MetricsLog::~MetricsLog() {
@@ -338,8 +343,10 @@
   std::string serialized_proto =
       recorder.SerializeAndRecordEnvironmentToPrefs(*system_profile);
 
-  GlobalPersistentSystemProfile::GetInstance()->SetSystemProfile(
-      serialized_proto);
+  if (log_type_ == ONGOING_LOG) {
+    GlobalPersistentSystemProfile::GetInstance()->SetSystemProfile(
+        serialized_proto, /*complete=*/true);
+  }
 
   return serialized_proto;
 }
diff --git a/components/metrics/metrics_switches.cc b/components/metrics/metrics_switches.cc
index 1aef619..f380ff8e 100644
--- a/components/metrics/metrics_switches.cc
+++ b/components/metrics/metrics_switches.cc
@@ -7,6 +7,13 @@
 namespace metrics {
 namespace switches {
 
+// Enables the recording of metrics reports but disables reporting. In contrast
+// to kDisableMetrics, this executes all the code that a normal client would
+// use for reporting, except the report is dropped rather than sent to the
+// server. This is useful for finding issues in the metrics code during UI and
+// performance tests.
+const char kMetricsRecordingOnly[] = "metrics-recording-only";
+
 // Forces a reset of the one-time-randomized FieldTrials on this client, also
 // known as the Chrome Variations state.
 const char kResetVariationState[] = "reset-variation-state";
diff --git a/components/metrics/metrics_switches.h b/components/metrics/metrics_switches.h
index b3fe7fd9..bfdf414 100644
--- a/components/metrics/metrics_switches.h
+++ b/components/metrics/metrics_switches.h
@@ -10,6 +10,8 @@
 
 // Alphabetical list of switches specific to the metrics component. Document
 // each in the .cc file.
+
+extern const char kMetricsRecordingOnly[];
 extern const char kResetVariationState[];
 
 }  // namespace switches
diff --git a/components/metrics/persistent_system_profile.cc b/components/metrics/persistent_system_profile.cc
index dc4e7eb..88b2a76 100644
--- a/components/metrics/persistent_system_profile.cc
+++ b/components/metrics/persistent_system_profile.cc
@@ -46,6 +46,7 @@
     base::PersistentMemoryAllocator* memory_allocator,
     size_t min_size)
     : allocator_(memory_allocator),
+      has_complete_profile_(false),
       alloc_reference_(0),
       alloc_size_(0),
       end_offset_(0) {
@@ -75,6 +76,7 @@
   }
 
   // Reset member variables.
+  has_complete_profile_ = false;
   alloc_reference_ = 0;
   alloc_size_ = 0;
   end_offset_ = 0;
@@ -264,6 +266,7 @@
   // block is reserved now.
   RecordAllocator allocator(memory_allocator, 1);
   allocators_.push_back(std::move(allocator));
+  all_have_complete_profile_ = false;
 }
 
 void PersistentSystemProfile::DeregisterPersistentAllocator(
@@ -278,26 +281,42 @@
 }
 
 void PersistentSystemProfile::SetSystemProfile(
-    const std::string& serialized_profile) {
+    const std::string& serialized_profile,
+    bool complete) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   if (allocators_.empty() || serialized_profile.empty())
     return;
 
   for (auto& allocator : allocators_) {
+    // Don't overwrite a complete profile with an incomplete one.
+    if (!complete && allocator.has_complete_profile())
+      continue;
     // A full system profile always starts fresh.
     allocator.Reset();
     // Write out the serialized profile.
     allocator.Write(kSystemProfileProto, serialized_profile);
+    // Indicate if this is a complete profile.
+    if (complete)
+      allocator.set_complete_profile();
   }
+
+  if (complete)
+    all_have_complete_profile_ = true;
 }
 
 void PersistentSystemProfile::SetSystemProfile(
-    const SystemProfileProto& profile) {
+    const SystemProfileProto& profile,
+    bool complete) {
+  // Avoid serialization if passed profile is not complete and all allocators
+  // already have complete ones.
+  if (!complete && all_have_complete_profile_)
+    return;
+
   std::string serialized_profile;
   if (!profile.SerializeToString(&serialized_profile))
     return;
-  SetSystemProfile(serialized_profile);
+  SetSystemProfile(serialized_profile, complete);
 }
 
 // static
diff --git a/components/metrics/persistent_system_profile.h b/components/metrics/persistent_system_profile.h
index 1775dde..2f687811 100644
--- a/components/metrics/persistent_system_profile.h
+++ b/components/metrics/persistent_system_profile.h
@@ -31,9 +31,12 @@
       base::PersistentMemoryAllocator* memory_allocator);
 
   // Stores a complete system profile. Use the version taking the serialized
-  // version if available to avoid multiple serialization actions.
-  void SetSystemProfile(const std::string& serialized_profile);
-  void SetSystemProfile(const SystemProfileProto& profile);
+  // version if available to avoid multiple serialization actions. The
+  // |complete| flag indicates that this profile contains all known information
+  // and can replace whatever exists. If the flag is false, the profile will be
+  // stored only if there is nothing else already present.
+  void SetSystemProfile(const std::string& serialized_profile, bool complete);
+  void SetSystemProfile(const SystemProfileProto& profile, bool complete);
 
   // Tests if a persistent memory allocator contains an system profile.
   static bool HasSystemProfile(
@@ -76,6 +79,9 @@
 
     base::PersistentMemoryAllocator* allocator() { return allocator_; }
 
+    bool has_complete_profile() { return has_complete_profile_; }
+    void set_complete_profile() { has_complete_profile_ = true; }
+
    private:
     // Advance to the next record segment in the memory allocator.
     bool NextSegment() const;
@@ -97,6 +103,9 @@
     // This never changes but can't be "const" because vector calls operator=().
     base::PersistentMemoryAllocator* allocator_;  // Storage location.
 
+    // Indicates if a complete profile has been stored.
+    bool has_complete_profile_;
+
     // These change even though the underlying data may be "const".
     mutable uint32_t alloc_reference_;  // Last storage block.
     mutable size_t alloc_size_;         // Size of the block.
@@ -109,6 +118,9 @@
   // instances.
   std::vector<RecordAllocator> allocators_;
 
+  // Indicates if a complete profile has been stored to all allocators.
+  bool all_have_complete_profile_ = false;
+
   THREAD_CHECKER(thread_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(PersistentSystemProfile);
diff --git a/components/metrics/persistent_system_profile_unittest.cc b/components/metrics/persistent_system_profile_unittest.cc
index 5ca4e898..a2e038a4 100644
--- a/components/metrics/persistent_system_profile_unittest.cc
+++ b/components/metrics/persistent_system_profile_unittest.cc
@@ -91,7 +91,7 @@
   trial->set_name_id(123);
   trial->set_group_id(456);
 
-  persistent_profile()->SetSystemProfile(proto1);
+  persistent_profile()->SetSystemProfile(proto1, false);
 
   SystemProfileProto proto2;
   ASSERT_TRUE(PersistentSystemProfile::HasSystemProfile(*memory_allocator()));
@@ -107,7 +107,23 @@
   trial->set_name_id(78);
   trial->set_group_id(90);
 
-  persistent_profile()->SetSystemProfile(proto1);
+  persistent_profile()->SetSystemProfile(proto1, true);
+
+  ASSERT_TRUE(
+      PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &proto2));
+  ASSERT_EQ(2, proto2.field_trial_size());
+  EXPECT_EQ(123U, proto2.field_trial(0).name_id());
+  EXPECT_EQ(456U, proto2.field_trial(0).group_id());
+  EXPECT_EQ(78U, proto2.field_trial(1).name_id());
+  EXPECT_EQ(90U, proto2.field_trial(1).group_id());
+
+  // Check that the profile won't be overwritten by a new non-complete profile.
+
+  trial = proto1.add_field_trial();
+  trial->set_name_id(0xC0DE);
+  trial->set_group_id(0xFEED);
+
+  persistent_profile()->SetSystemProfile(proto1, false);
 
   ASSERT_TRUE(
       PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &proto2));
diff --git a/components/metrics_services_manager/metrics_services_manager.cc b/components/metrics_services_manager/metrics_services_manager.cc
index 15bfccec..ef4c993 100644
--- a/components/metrics_services_manager/metrics_services_manager.cc
+++ b/components/metrics_services_manager/metrics_services_manager.cc
@@ -6,10 +6,12 @@
 
 #include <utility>
 
+#include "base/command_line.h"
 #include "base/logging.h"
 #include "components/metrics/metrics_service.h"
 #include "components/metrics/metrics_service_client.h"
 #include "components/metrics/metrics_state_manager.h"
+#include "components/metrics/metrics_switches.h"
 #include "components/metrics_services_manager/metrics_services_manager_client.h"
 #include "components/rappor/rappor_service_impl.h"
 #include "components/ukm/ukm_service.h"
@@ -103,7 +105,8 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   metrics::MetricsService* metrics = GetMetricsService();
 
-  if (client_->OnlyDoMetricsRecording()) {
+  const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+  if (cmdline->HasSwitch(metrics::switches::kMetricsRecordingOnly)) {
     metrics->StartRecordingForTests();
     GetRapporServiceImpl()->Update(true, false);
     return;
diff --git a/components/metrics_services_manager/metrics_services_manager_client.h b/components/metrics_services_manager/metrics_services_manager_client.h
index afbd601..5d4e50dc 100644
--- a/components/metrics_services_manager/metrics_services_manager_client.h
+++ b/components/metrics_services_manager/metrics_services_manager_client.h
@@ -51,9 +51,6 @@
   // Returns whether metrics reporting is enabled.
   virtual bool IsMetricsReportingEnabled() = 0;
 
-  // Whether the metrics services should record but not report metrics.
-  virtual bool OnlyDoMetricsRecording() = 0;
-
   // Update the running state of metrics services managed by the embedder, for
   // example, crash reporting.
   virtual void UpdateRunningServices(bool may_record, bool may_upload) {}
diff --git a/components/offline_pages/core/prefetch/generate_page_bundle_request.cc b/components/offline_pages/core/prefetch/generate_page_bundle_request.cc
index 8fddcfba..94f9155 100644
--- a/components/offline_pages/core/prefetch/generate_page_bundle_request.cc
+++ b/components/offline_pages/core/prefetch/generate_page_bundle_request.cc
@@ -56,18 +56,19 @@
 void GeneratePageBundleRequest::OnCompleted(PrefetchRequestStatus status,
                                             const std::string& data) {
   if (status != PrefetchRequestStatus::SUCCESS) {
-    callback_.Run(status, std::vector<RenderPageInfo>());
+    callback_.Run(status, std::string(), std::vector<RenderPageInfo>());
     return;
   }
 
   std::vector<RenderPageInfo> pages;
-  if (!ParseOperationResponse(data, &pages)) {
+  std::string operation_name = ParseOperationResponse(data, &pages);
+  if (operation_name.empty()) {
     callback_.Run(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
-                  std::vector<RenderPageInfo>());
+                  std::string(), std::vector<RenderPageInfo>());
     return;
   }
 
-  callback_.Run(PrefetchRequestStatus::SUCCESS, pages);
+  callback_.Run(PrefetchRequestStatus::SUCCESS, operation_name, pages);
 }
 
 }  // offline_pages
diff --git a/components/offline_pages/core/prefetch/generate_page_bundle_request_unittest.cc b/components/offline_pages/core/prefetch/generate_page_bundle_request_unittest.cc
index 19a313b..17e478a 100644
--- a/components/offline_pages/core/prefetch/generate_page_bundle_request_unittest.cc
+++ b/components/offline_pages/core/prefetch/generate_page_bundle_request_unittest.cc
@@ -77,12 +77,15 @@
       CreateRequest(callback.Get()));
 
   PrefetchRequestStatus status;
+  std::string operation_name;
   std::vector<RenderPageInfo> pages;
-  EXPECT_CALL(callback, Run(_, _))
-      .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&pages)));
+  EXPECT_CALL(callback, Run(_, _, _))
+      .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&operation_name),
+                      SaveArg<2>(&pages)));
   RespondWithData("");
 
   EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF, status);
+  EXPECT_TRUE(operation_name.empty());
   EXPECT_TRUE(pages.empty());
 }
 
@@ -92,12 +95,15 @@
       CreateRequest(callback.Get()));
 
   PrefetchRequestStatus status;
+  std::string operation_name;
   std::vector<RenderPageInfo> pages;
-  EXPECT_CALL(callback, Run(_, _))
-      .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&pages)));
+  EXPECT_CALL(callback, Run(_, _, _))
+      .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&operation_name),
+                      SaveArg<2>(&pages)));
   RespondWithData("Some invalid data");
 
   EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF, status);
+  EXPECT_TRUE(operation_name.empty());
   EXPECT_TRUE(pages.empty());
 }
 
diff --git a/components/offline_pages/core/prefetch/get_operation_request.cc b/components/offline_pages/core/prefetch/get_operation_request.cc
index 59865ff..77d2cd2 100644
--- a/components/offline_pages/core/prefetch/get_operation_request.cc
+++ b/components/offline_pages/core/prefetch/get_operation_request.cc
@@ -15,7 +15,7 @@
 namespace offline_pages {
 
 namespace {
-const char kGetOperationURLPath[] = "v1/operations/";
+const char kGetOperationURLPath[] = "v1/";
 }  // namespace
 
 GetOperationRequest::GetOperationRequest(
@@ -38,18 +38,19 @@
 void GetOperationRequest::OnCompleted(PrefetchRequestStatus status,
                                       const std::string& data) {
   if (status != PrefetchRequestStatus::SUCCESS) {
-    callback_.Run(status, std::vector<RenderPageInfo>());
+    callback_.Run(status, std::string(), std::vector<RenderPageInfo>());
     return;
   }
 
   std::vector<RenderPageInfo> pages;
-  if (!ParseOperationResponse(data, &pages)) {
+  std::string operation_name = ParseOperationResponse(data, &pages);
+  if (operation_name.empty()) {
     callback_.Run(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
-                  std::vector<RenderPageInfo>());
+                  std::string(), std::vector<RenderPageInfo>());
     return;
   }
 
-  callback_.Run(PrefetchRequestStatus::SUCCESS, pages);
+  callback_.Run(PrefetchRequestStatus::SUCCESS, operation_name, pages);
 }
 
 }  // offline_pages
diff --git a/components/offline_pages/core/prefetch/get_operation_request_unittest.cc b/components/offline_pages/core/prefetch/get_operation_request_unittest.cc
index b104888..45f2717 100644
--- a/components/offline_pages/core/prefetch/get_operation_request_unittest.cc
+++ b/components/offline_pages/core/prefetch/get_operation_request_unittest.cc
@@ -63,12 +63,15 @@
   std::unique_ptr<GetOperationRequest> request(CreateRequest(callback.Get()));
 
   PrefetchRequestStatus status;
+  std::string operation_name;
   std::vector<RenderPageInfo> pages;
-  EXPECT_CALL(callback, Run(_, _))
-      .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&pages)));
+  EXPECT_CALL(callback, Run(_, _, _))
+      .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&operation_name),
+                      SaveArg<2>(&pages)));
   RespondWithData("");
 
   EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF, status);
+  EXPECT_TRUE(operation_name.empty());
   EXPECT_TRUE(pages.empty());
 }
 
@@ -77,12 +80,15 @@
   std::unique_ptr<GetOperationRequest> request(CreateRequest(callback.Get()));
 
   PrefetchRequestStatus status;
+  std::string operation_name;
   std::vector<RenderPageInfo> pages;
-  EXPECT_CALL(callback, Run(_, _))
-      .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&pages)));
+  EXPECT_CALL(callback, Run(_, _, _))
+      .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&operation_name),
+                      SaveArg<2>(&pages)));
   RespondWithData("Some invalid data");
 
   EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF, status);
+  EXPECT_TRUE(operation_name.empty());
   EXPECT_TRUE(pages.empty());
 }
 
diff --git a/components/offline_pages/core/prefetch/prefetch_proto_utils.cc b/components/offline_pages/core/prefetch/prefetch_proto_utils.cc
index 0fe5040..0fcdfb7 100644
--- a/components/offline_pages/core/prefetch/prefetch_proto_utils.cc
+++ b/components/offline_pages/core/prefetch/prefetch_proto_utils.cc
@@ -123,18 +123,19 @@
 
 }  // namespace
 
-bool ParseOperationResponse(const std::string& data,
-                            std::vector<RenderPageInfo>* pages) {
+std::string ParseOperationResponse(const std::string& data,
+                                   std::vector<RenderPageInfo>* pages) {
   proto::Operation operation;
   if (!operation.ParseFromString(data)) {
     DVLOG(1) << "Failed to parse operation";
-    return false;
+    return std::string();
   }
 
-  if (operation.done())
-    return ParseDoneOperationResponse(operation, pages);
-  else
-    return ParsePendingOperationResponse(operation, pages);
+  std::string name = operation.name();
+  bool success = operation.done()
+                     ? ParseDoneOperationResponse(operation, pages)
+                     : ParsePendingOperationResponse(operation, pages);
+  return success ? name : std::string();
 }
 
 }  // namespace offline_pages
diff --git a/components/offline_pages/core/prefetch/prefetch_proto_utils.h b/components/offline_pages/core/prefetch/prefetch_proto_utils.h
index cc77de1..92813af 100644
--- a/components/offline_pages/core/prefetch/prefetch_proto_utils.h
+++ b/components/offline_pages/core/prefetch/prefetch_proto_utils.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_PROTO_UTILS_H_
 #define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_PROTO_UTILS_H_
 
+#include <string>
 #include <vector>
 #include "components/offline_pages/core/prefetch/prefetch_types.h"
 
@@ -13,9 +14,10 @@
 // The fully qualified type name for PageBundle defined in proto.
 extern const char kPageBundleTypeURL[];
 
-// Used to parse the Operation serialized in binary proto |data|.
-bool ParseOperationResponse(const std::string& data,
-                            std::vector<RenderPageInfo>* pages);
+// Parse the Operation serialized in binary proto |data|. Returns the operation
+// name if parsing succeeded. Otherwise, empty string is returned.
+std::string ParseOperationResponse(const std::string& data,
+                                   std::vector<RenderPageInfo>* pages);
 
 }  // namespace offline_pages
 
diff --git a/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc b/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc
index cd59945..3df538c 100644
--- a/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc
+++ b/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc
@@ -24,6 +24,7 @@
 
 namespace {
 const version_info::Channel kTestChannel = version_info::Channel::UNKNOWN;
+const char kTestOperationName[] = "operation/test123";
 const char kTestURL[] = "http://example.com";
 const char kTestURL2[] = "http://example.com/2";
 const char kTestURL3[] = "http://example.com/3";
@@ -101,6 +102,7 @@
                              const std::string& any_type_url,
                              const std::string& any_value) {
     proto::Operation operation;
+    operation.set_name(kTestOperationName);
     operation.set_done(is_done);
     if (error_code != proto::OK) {
       operation.mutable_error()->set_code(error_code);
@@ -234,6 +236,7 @@
         builder_.BuildFromAny(kPageBundleTypeURL, bundle_data));
   }
 
+  const std::string& operation_name() const { return operation_name_; }
   const std::vector<RenderPageInfo>& pages() const { return pages_; }
 
  private:
@@ -242,14 +245,17 @@
     builder_.CreateRequest(request_context(), callback.Get());
 
     PrefetchRequestStatus status;
+    operation_name_.clear();
     pages_.clear();
-    EXPECT_CALL(callback, Run(_, _))
-        .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&pages_)));
+    EXPECT_CALL(callback, Run(_, _, _))
+        .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&operation_name_),
+                        SaveArg<2>(&pages_)));
     RespondWithData(response_data);
     return status;
   }
 
   T builder_;
+  std::string operation_name_;
   std::vector<RenderPageInfo> pages_;
 };
 
@@ -266,24 +272,28 @@
             // No error is set for OK. Thus this will cause the operation
             // being filled with only done flag.
             this->SendWithErrorResponse(proto::OK, ""));
+  EXPECT_TRUE(this->operation_name().empty());
   EXPECT_TRUE(this->pages().empty());
 }
 
 TYPED_TEST(PrefetchRequestOperationResponseTest, ErrorValue) {
   EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
             this->SendWithErrorResponse(proto::UNKNOWN, kErrorMessage));
+  EXPECT_TRUE(this->operation_name().empty());
   EXPECT_TRUE(this->pages().empty());
 }
 
 TYPED_TEST(PrefetchRequestOperationResponseTest, InvalidTypeUrl) {
   EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
             this->SendWithAnyResponse("foo", ""));
+  EXPECT_TRUE(this->operation_name().empty());
   EXPECT_TRUE(this->pages().empty());
 }
 
 TYPED_TEST(PrefetchRequestOperationResponseTest, InvalidValue) {
   EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
             this->SendWithAnyResponse(kPageBundleTypeURL, "foo"));
+  EXPECT_TRUE(this->operation_name().empty());
   EXPECT_TRUE(this->pages().empty());
 }
 
@@ -291,6 +301,7 @@
   proto::PageBundle bundle;
   EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
             this->SendWithPageBundleResponse(bundle));
+  EXPECT_TRUE(this->operation_name().empty());
   EXPECT_TRUE(this->pages().empty());
 }
 
@@ -299,6 +310,7 @@
   bundle.add_archives();
   EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
             this->SendWithPageBundleResponse(bundle));
+  EXPECT_TRUE(this->operation_name().empty());
   EXPECT_TRUE(this->pages().empty());
 }
 
@@ -309,6 +321,7 @@
   archive->set_body_length(kTestBodyLength);
   EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
             this->SendWithPageBundleResponse(bundle));
+  EXPECT_TRUE(this->operation_name().empty());
   EXPECT_TRUE(this->pages().empty());
 }
 
@@ -319,6 +332,7 @@
   page_info->set_redirect_url(kTestURL);
   EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
             this->SendWithPageBundleResponse(bundle));
+  EXPECT_TRUE(this->operation_name().empty());
   EXPECT_TRUE(this->pages().empty());
 }
 
@@ -338,6 +352,7 @@
                                               1000000);
   EXPECT_EQ(PrefetchRequestStatus::SUCCESS,
             this->SendWithPageBundleResponse(bundle));
+  EXPECT_EQ(kTestOperationName, this->operation_name());
   ASSERT_EQ(1u, this->pages().size());
   EXPECT_EQ(kTestURL, this->pages().back().url);
   EXPECT_EQ(kTestURL2, this->pages().back().redirect_url);
@@ -383,6 +398,7 @@
 
   EXPECT_EQ(PrefetchRequestStatus::SUCCESS,
             this->SendWithPageBundleResponse(bundle));
+  EXPECT_EQ(kTestOperationName, this->operation_name());
   ASSERT_EQ(4u, this->pages().size());
   EXPECT_EQ(kTestURL, this->pages().at(0).url);
   EXPECT_EQ(RenderStatus::PENDING, this->pages().at(0).status);
diff --git a/components/offline_pages/core/prefetch/prefetch_types.h b/components/offline_pages/core/prefetch/prefetch_types.h
index 80208f0a..8fb4bbe4 100644
--- a/components/offline_pages/core/prefetch/prefetch_types.h
+++ b/components/offline_pages/core/prefetch/prefetch_types.h
@@ -106,6 +106,7 @@
 // Callback invoked upon completion of a prefetch request.
 using PrefetchRequestFinishedCallback =
     base::Callback<void(PrefetchRequestStatus status,
+                        const std::string& operation_name,
                         const std::vector<RenderPageInfo>& pages)>;
 
 // Holds information about a new URL to be prefetched.
diff --git a/components/safe_browsing/common/safebrowsing_constants.cc b/components/safe_browsing/common/safebrowsing_constants.cc
index 22e6de0..65c2b54 100644
--- a/components/safe_browsing/common/safebrowsing_constants.cc
+++ b/components/safe_browsing/common/safebrowsing_constants.cc
@@ -13,4 +13,23 @@
 const base::FilePath::CharType kChannelIDFile[] =
     FILE_PATH_LITERAL(" Channel IDs");
 
+// The default URL prefix where browser fetches chunk updates, hashes,
+// and reports safe browsing hits and malware details.
+const char kSbDefaultURLPrefix[] =
+    "https://safebrowsing.google.com/safebrowsing";
+
+// The backup URL prefix used when there are issues establishing a connection
+// with the server at the primary URL.
+const char kSbBackupConnectErrorURLPrefix[] =
+    "https://alt1-safebrowsing.google.com/safebrowsing";
+
+// The backup URL prefix used when there are HTTP-specific issues with the
+// server at the primary URL.
+const char kSbBackupHttpErrorURLPrefix[] =
+    "https://alt2-safebrowsing.google.com/safebrowsing";
+
+// The backup URL prefix used when there are local network specific issues.
+const char kSbBackupNetworkErrorURLPrefix[] =
+    "https://alt3-safebrowsing.google.com/safebrowsing";
+
 }  // namespace safe_browsing
diff --git a/components/safe_browsing/common/safebrowsing_constants.h b/components/safe_browsing/common/safebrowsing_constants.h
index e6776b5..598352e3 100644
--- a/components/safe_browsing/common/safebrowsing_constants.h
+++ b/components/safe_browsing/common/safebrowsing_constants.h
@@ -14,6 +14,21 @@
 // Filename suffix for the cookie database.
 extern const base::FilePath::CharType kCookiesFile[];
 extern const base::FilePath::CharType kChannelIDFile[];
+
+// The default URL prefix where browser fetches chunk updates, hashes,
+// and reports safe browsing hits and malware details.
+extern const char kSbDefaultURLPrefix[];
+
+// The backup URL prefix used when there are issues establishing a connection
+// with the server at the primary URL.
+extern const char kSbBackupConnectErrorURLPrefix[];
+
+// The backup URL prefix used when there are HTTP-specific issues with the
+// server at the primary URL.
+extern const char kSbBackupHttpErrorURLPrefix[];
+
+// The backup URL prefix used when there are local network specific issues.
+extern const char kSbBackupNetworkErrorURLPrefix[];
 }
 
 #endif  // COMPONENTS_SAFE_BROWSING_COMMON_SAFEBROWSING_CONSTANTS_H_
diff --git a/components/security_state/content/content_utils.cc b/components/security_state/content/content_utils.cc
index 27986e0..f1dc115 100644
--- a/components/security_state/content/content_utils.cc
+++ b/components/security_state/content/content_utils.cc
@@ -182,16 +182,23 @@
   const blink::WebSecurityStyle security_style =
       SecurityLevelToSecurityStyle(security_info.security_level);
 
-  // The HTTP_SHOW_WARNING state may occur if the page is served as a data: URI
-  // or if it is served non-securely AND contains a sensitive form field.
-  if (security_info.security_level == security_state::HTTP_SHOW_WARNING &&
-      (security_info.displayed_password_field_on_http ||
-       security_info.displayed_credit_card_field_on_http)) {
-    security_style_explanations->neutral_explanations.push_back(
-        content::SecurityStyleExplanation(
-            l10n_util::GetStringUTF8(IDS_PRIVATE_USER_DATA_INPUT),
-            l10n_util::GetStringUTF8(IDS_PRIVATE_USER_DATA_INPUT_DESCRIPTION)));
+  if (security_info.security_level == security_state::HTTP_SHOW_WARNING) {
+    if (security_info.displayed_password_field_on_http ||
+        security_info.displayed_credit_card_field_on_http) {
+      security_style_explanations->neutral_explanations.push_back(
+          content::SecurityStyleExplanation(
+              l10n_util::GetStringUTF8(IDS_PRIVATE_USER_DATA_INPUT),
+              l10n_util::GetStringUTF8(
+                  IDS_PRIVATE_USER_DATA_INPUT_DESCRIPTION)));
+    }
+    if (security_info.incognito_downgraded_security_level) {
+      security_style_explanations->neutral_explanations.push_back(
+          content::SecurityStyleExplanation(
+              l10n_util::GetStringUTF8(IDS_INCOGNITO_NONSECURE),
+              l10n_util::GetStringUTF8(IDS_INCOGNITO_NONSECURE_DESCRIPTION)));
+    }
   }
+
   security_style_explanations->ran_insecure_content_style =
       SecurityLevelToSecurityStyle(security_state::kRanInsecureContentLevel);
   security_style_explanations->displayed_insecure_content_style =
diff --git a/components/security_state/content/content_utils_unittest.cc b/components/security_state/content/content_utils_unittest.cc
index e0e37e0..090a835 100644
--- a/components/security_state/content/content_utils_unittest.cc
+++ b/components/security_state/content/content_utils_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "components/security_state/content/content_utils.h"
 
+#include <vector>
+
 #include "base/command_line.h"
 #include "base/test/histogram_tester.h"
 #include "components/security_state/core/security_state.h"
@@ -253,6 +255,15 @@
   EXPECT_EQ(blink::kWebSecurityStyleNeutral, security_style);
   // Verify only one explanation was shown when Form Not Secure is triggered.
   EXPECT_EQ(1u, explanations.neutral_explanations.size());
+
+  // Verify that two explanations are shown when the Incognito and
+  // FormNotSecure flags are both set.
+  explanations.neutral_explanations.clear();
+  security_info.displayed_credit_card_field_on_http = true;
+  security_info.incognito_downgraded_security_level = true;
+  security_style = GetSecurityStyle(security_info, &explanations);
+  EXPECT_EQ(blink::kWebSecurityStyleNeutral, security_style);
+  EXPECT_EQ(2u, explanations.neutral_explanations.size());
 }
 
 // Tests that an explanation is provided if a certificate is missing a
diff --git a/components/security_state/core/security_state.cc b/components/security_state/core/security_state.cc
index a958bf3..1456374 100644
--- a/components/security_state/core/security_state.cc
+++ b/components/security_state/core/security_state.cc
@@ -5,6 +5,7 @@
 #include "components/security_state/core/security_state.h"
 
 #include <stdint.h>
+#include <string>
 
 #include "base/command_line.h"
 #include "base/metrics/field_trial.h"
@@ -30,22 +31,42 @@
 };
 
 // If |switch_or_field_trial_group| corresponds to a valid
-// MarkHttpAs group, sets |*level| and |*histogram_status| to the
+// MarkHttpAs setting, sets |*level| and |*histogram_status| to the
 // appropriate values and returns true. Otherwise, returns false.
 bool GetSecurityLevelAndHistogramValueForNonSecureFieldTrial(
     std::string switch_or_field_trial_group,
     bool displayed_sensitive_input_on_http,
+    bool is_incognito,
     SecurityLevel* level,
     MarkHttpStatus* histogram_status) {
-  if (switch_or_field_trial_group != switches::kMarkHttpAsDangerous)
-    return false;
-  *level = DANGEROUS;
-  *histogram_status = NON_SECURE;
-  return true;
+  if (switch_or_field_trial_group ==
+      switches::kMarkHttpAsNonSecureWhileIncognito) {
+    *histogram_status = NON_SECURE_WHILE_INCOGNITO;
+    *level = (is_incognito || displayed_sensitive_input_on_http)
+                 ? security_state::HTTP_SHOW_WARNING
+                 : NONE;
+    return true;
+  }
+  if (switch_or_field_trial_group ==
+      switches::kMarkHttpAsNonSecureWhileIncognitoOrEditing) {
+    *histogram_status = NON_SECURE_WHILE_INCOGNITO_OR_EDITING;
+    *level = (is_incognito || displayed_sensitive_input_on_http)
+                 ? security_state::HTTP_SHOW_WARNING
+                 : NONE;
+    return true;
+  }
+  if (switch_or_field_trial_group == switches::kMarkHttpAsDangerous) {
+    *histogram_status = NON_SECURE;
+    *level = DANGEROUS;
+    return true;
+  }
+
+  return false;
 }
 
 SecurityLevel GetSecurityLevelForNonSecureFieldTrial(
-    bool displayed_sensitive_input_on_http) {
+    bool displayed_sensitive_input_on_http,
+    bool is_incognito) {
   std::string choice =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
           switches::kMarkHttpAs);
@@ -59,9 +80,11 @@
   // If the command-line switch is set, then it takes precedence over
   // the field trial group.
   if (!GetSecurityLevelAndHistogramValueForNonSecureFieldTrial(
-          choice, displayed_sensitive_input_on_http, &level, &status)) {
+          choice, displayed_sensitive_input_on_http, is_incognito, &level,
+          &status)) {
     if (!GetSecurityLevelAndHistogramValueForNonSecureFieldTrial(
-            group, displayed_sensitive_input_on_http, &level, &status)) {
+            group, displayed_sensitive_input_on_http, is_incognito, &level,
+            &status)) {
       status = HTTP_SHOW_WARNING_ON_SENSITIVE_FIELDS;
       level = displayed_sensitive_input_on_http
                   ? security_state::HTTP_SHOW_WARNING
@@ -128,7 +151,8 @@
         (url.IsStandard() || url.SchemeIs(url::kBlobScheme))) {
       return GetSecurityLevelForNonSecureFieldTrial(
           visible_security_state.displayed_password_field_on_http ||
-          visible_security_state.displayed_credit_card_field_on_http);
+              visible_security_state.displayed_credit_card_field_on_http,
+          visible_security_state.is_incognito);
     }
     return NONE;
   }
@@ -238,10 +262,37 @@
       is_origin_secure_callback, security_info->sha1_in_chain,
       security_info->mixed_content_status,
       security_info->content_with_cert_errors_status);
+
+  security_info->incognito_downgraded_security_level =
+      (visible_security_state.is_incognito &&
+       security_info->security_level == HTTP_SHOW_WARNING &&
+       security_state::IsHttpWarningForIncognitoEnabled());
 }
 
 }  // namespace
 
+bool IsHttpWarningForIncognitoEnabled() {
+  std::string choice =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kMarkHttpAs);
+  std::string group = base::FieldTrialList::FindFullName("MarkNonSecureAs");
+  SecurityLevel level = NONE;
+  MarkHttpStatus status;
+
+  // If the command-line switch is set, then it takes precedence over
+  // the field trial group.
+  if (!GetSecurityLevelAndHistogramValueForNonSecureFieldTrial(
+          choice, false, true, &level, &status)) {
+    if (!GetSecurityLevelAndHistogramValueForNonSecureFieldTrial(
+            group, false, true, &level, &status)) {
+      return false;
+    }
+  }
+
+  return (status == NON_SECURE_WHILE_INCOGNITO ||
+          status == NON_SECURE_WHILE_INCOGNITO_OR_EDITING);
+}
+
 const base::Feature kHttpFormWarningFeature{"HttpFormWarning",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -261,7 +312,8 @@
       displayed_password_field_on_http(false),
       displayed_credit_card_field_on_http(false),
       contained_mixed_form(false),
-      cert_missing_subject_alt_name(false) {}
+      cert_missing_subject_alt_name(false),
+      incognito_downgraded_security_level(false) {}
 
 SecurityInfo::~SecurityInfo() {}
 
@@ -293,7 +345,8 @@
       ran_content_with_cert_errors(false),
       pkp_bypassed(false),
       displayed_password_field_on_http(false),
-      displayed_credit_card_field_on_http(false) {}
+      displayed_credit_card_field_on_http(false),
+      is_incognito(false) {}
 
 VisibleSecurityState::~VisibleSecurityState() {}
 
@@ -315,7 +368,8 @@
               other.displayed_password_field_on_http &&
           displayed_credit_card_field_on_http ==
               other.displayed_credit_card_field_on_http &&
-          contained_mixed_form == other.contained_mixed_form);
+          contained_mixed_form == other.contained_mixed_form &&
+          is_incognito == other.is_incognito);
 }
 
 }  // namespace security_state
diff --git a/components/security_state/core/security_state.h b/components/security_state/core/security_state.h
index 4d1ec3f9..0e737f9 100644
--- a/components/security_state/core/security_state.h
+++ b/components/security_state/core/security_state.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_SECURITY_STATE_SECURITY_STATE_H_
-#define COMPONENTS_SECURITY_STATE_SECURITY_STATE_H_
+#ifndef COMPONENTS_SECURITY_STATE_CORE_SECURITY_STATE_H_
+#define COMPONENTS_SECURITY_STATE_CORE_SECURITY_STATE_H_
 
 #include <stdint.h>
 #include <memory>
@@ -141,6 +141,9 @@
   // True if the server's certificate does not contain a
   // subjectAltName extension with a domain name or IP address.
   bool cert_missing_subject_alt_name;
+  // True if the |security_level| was downgraded to HTTP_SHOW_WARNING because
+  // the page was loaded while Incognito.
+  bool incognito_downgraded_security_level;
 };
 
 // Contains the security state relevant to computing the SecurityInfo
@@ -181,6 +184,8 @@
   bool displayed_password_field_on_http;
   // True if the page was an HTTP page that displayed a credit card field.
   bool displayed_credit_card_field_on_http;
+  // True if the page was displayed in an Incognito context.
+  bool is_incognito;
 };
 
 // These security levels describe the treatment given to pages that
@@ -209,6 +214,10 @@
 // |kHttpFormWarningFeature| feature.
 bool IsHttpWarningInFormEnabled();
 
+// Returns true if the MarkHttpAs setting indicates that a warning
+// should be shown for HTTP pages loaded while in Incognito mode.
+bool IsHttpWarningForIncognitoEnabled();
+
 }  // namespace security_state
 
-#endif  // COMPONENTS_SECURITY_STATE_SECURITY_STATE_H_
+#endif  // COMPONENTS_SECURITY_STATE_CORE_SECURITY_STATE_H_
diff --git a/components/security_state/core/security_state_unittest.cc b/components/security_state/core/security_state_unittest.cc
index 201a91d0..85df06e 100644
--- a/components/security_state/core/security_state_unittest.cc
+++ b/components/security_state/core/security_state_unittest.cc
@@ -5,11 +5,14 @@
 #include "components/security_state/core/security_state.h"
 
 #include <stdint.h>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "base/test/histogram_tester.h"
+#include "base/test/scoped_command_line.h"
+#include "components/security_state/core/switches.h"
 #include "net/cert/x509_certificate.h"
 #include "net/ssl/ssl_cipher_suite_names.h"
 #include "net/ssl/ssl_connection_status_flags.h"
@@ -49,7 +52,8 @@
         ran_mixed_content_(false),
         malicious_content_status_(MALICIOUS_CONTENT_STATUS_NONE),
         displayed_password_field_on_http_(false),
-        displayed_credit_card_field_on_http_(false) {}
+        displayed_credit_card_field_on_http_(false),
+        is_incognito_(false) {}
   virtual ~TestSecurityStateHelper() {}
 
   void SetCertificate(scoped_refptr<net::X509Certificate> cert) {
@@ -85,6 +89,7 @@
       bool displayed_credit_card_field_on_http) {
     displayed_credit_card_field_on_http_ = displayed_credit_card_field_on_http;
   }
+  void set_is_incognito(bool is_incognito) { is_incognito_ = is_incognito; }
 
   void SetUrl(const GURL& url) { url_ = url; }
 
@@ -103,6 +108,7 @@
     state->displayed_password_field_on_http = displayed_password_field_on_http_;
     state->displayed_credit_card_field_on_http =
         displayed_credit_card_field_on_http_;
+    state->is_incognito = is_incognito_;
     return state;
   }
 
@@ -124,6 +130,7 @@
   MaliciousContentStatus malicious_content_status_;
   bool displayed_password_field_on_http_;
   bool displayed_credit_card_field_on_http_;
+  bool is_incognito_;
 };
 
 }  // namespace
@@ -353,6 +360,30 @@
   }
 }
 
+// Tests that |incognito_downgraded_security_level| is set only when the
+// corresponding VisibleSecurityState flag is set and the HTTPBad Phase 2
+// experiment is enabled.
+TEST(SecurityStateTest, IncognitoFlagPropagates) {
+  TestSecurityStateHelper helper;
+  helper.SetUrl(GURL(kHttpUrl));
+  SecurityInfo security_info;
+  helper.GetSecurityInfo(&security_info);
+  EXPECT_FALSE(security_info.incognito_downgraded_security_level);
+
+  helper.set_is_incognito(true);
+  helper.GetSecurityInfo(&security_info);
+  EXPECT_FALSE(security_info.incognito_downgraded_security_level);
+  {
+    // Enable the "non-secure-while-incognito" configuration.
+    base::test::ScopedCommandLine scoped_command_line;
+    scoped_command_line.GetProcessCommandLine()->AppendSwitchASCII(
+        security_state::switches::kMarkHttpAs,
+        security_state::switches::kMarkHttpAsNonSecureWhileIncognito);
+    helper.GetSecurityInfo(&security_info);
+    EXPECT_TRUE(security_info.incognito_downgraded_security_level);
+  }
+}
+
 // Tests that SSL.MarkHttpAsStatus histogram is updated when security state is
 // computed for a page.
 TEST(SecurityStateTest, MarkHttpAsStatusHistogram) {
@@ -367,12 +398,70 @@
   SecurityInfo security_info;
   histograms.ExpectTotalCount(kHistogramName, 0);
   helper.GetSecurityInfo(&security_info);
-  histograms.ExpectUniqueSample(kHistogramName, 2 /* HTTP_SHOW_WARNING */, 1);
+  histograms.ExpectUniqueSample(
+      kHistogramName, 2 /* HTTP_SHOW_WARNING_ON_SENSITIVE_FIELDS */, 1);
 
   // Ensure histogram recorded correctly even without a password input.
   helper.set_displayed_password_field_on_http(false);
   helper.GetSecurityInfo(&security_info);
-  histograms.ExpectUniqueSample(kHistogramName, 2 /* HTTP_SHOW_WARNING */, 2);
+  histograms.ExpectUniqueSample(
+      kHistogramName, 2 /* HTTP_SHOW_WARNING_ON_SENSITIVE_FIELDS */, 2);
+
+  {
+    // Test the "non-secure-while-incognito" configuration.
+    base::test::ScopedCommandLine scoped_command_line;
+    scoped_command_line.GetProcessCommandLine()->AppendSwitchASCII(
+        security_state::switches::kMarkHttpAs,
+        security_state::switches::kMarkHttpAsNonSecureWhileIncognito);
+
+    base::HistogramTester histograms;
+    TestSecurityStateHelper helper;
+    helper.SetUrl(GURL(kHttpUrl));
+
+    // Ensure histogram recorded correctly when the Incognito flag is present.
+    helper.set_is_incognito(true);
+    SecurityInfo security_info;
+    histograms.ExpectTotalCount(kHistogramName, 0);
+    helper.GetSecurityInfo(&security_info);
+    EXPECT_TRUE(security_info.incognito_downgraded_security_level);
+    histograms.ExpectUniqueSample(kHistogramName,
+                                  4 /* NON_SECURE_WHILE_INCOGNITO */, 1);
+
+    // Ensure histogram recorded correctly even without the Incognito flag.
+    helper.set_is_incognito(false);
+    helper.GetSecurityInfo(&security_info);
+    EXPECT_FALSE(security_info.incognito_downgraded_security_level);
+    histograms.ExpectUniqueSample(kHistogramName,
+                                  4 /* NON_SECURE_WHILE_INCOGNITO */, 2);
+  }
+
+  {
+    // Test the "non-secure-while-incognito-or-editing" configuration.
+    base::test::ScopedCommandLine scoped_command_line;
+    scoped_command_line.GetProcessCommandLine()->AppendSwitchASCII(
+        security_state::switches::kMarkHttpAs,
+        security_state::switches::kMarkHttpAsNonSecureWhileIncognitoOrEditing);
+
+    base::HistogramTester histograms;
+    TestSecurityStateHelper helper;
+    helper.SetUrl(GURL(kHttpUrl));
+
+    // Ensure histogram recorded correctly when the Incognito flag is present.
+    helper.set_is_incognito(true);
+    SecurityInfo security_info;
+    histograms.ExpectTotalCount(kHistogramName, 0);
+    helper.GetSecurityInfo(&security_info);
+    EXPECT_TRUE(security_info.incognito_downgraded_security_level);
+    histograms.ExpectUniqueSample(
+        kHistogramName, 5 /* NON_SECURE_WHILE_INCOGNITO_OR_EDITING */, 1);
+
+    // Ensure histogram recorded correctly even without the Incognito flag.
+    helper.set_is_incognito(false);
+    helper.GetSecurityInfo(&security_info);
+    EXPECT_FALSE(security_info.incognito_downgraded_security_level);
+    histograms.ExpectUniqueSample(
+        kHistogramName, 5 /* NON_SECURE_WHILE_INCOGNITO_OR_EDITING */, 2);
+  }
 }
 
 TEST(SecurityStateTest, DetectSubjectAltName) {
diff --git a/components/security_state_strings.grdp b/components/security_state_strings.grdp
index 1982f91..482b6c9 100644
--- a/components/security_state_strings.grdp
+++ b/components/security_state_strings.grdp
@@ -7,6 +7,12 @@
   <message name="IDS_PRIVATE_USER_DATA_INPUT_DESCRIPTION" desc="Description of a security problem where the site collects private user data on an insecure page, which results in an omnibox warning." translateable="false">
     This page includes a password or credit card input over HTTP. A warning has been added to the URL bar.
   </message>
+  <message name="IDS_INCOGNITO_NONSECURE" desc="Summary phrase for a security problem where the site is non-secure (HTTP) and the user is browsing Incognito." translateable="false">
+    Non-secure page loaded in incognito mode
+  </message>
+  <message name="IDS_INCOGNITO_NONSECURE_DESCRIPTION" desc="Description of a security problem where the site is non-secure (HTTP) and the user is browsing Incognito, which results in an omnibox warning." translateable="false">
+    This page was loaded non-securely in an incognito window. A warning has been added to the URL bar.
+  </message>
   <message name="IDS_SAFEBROWSING_WARNING" desc="Summary phrase for a security problem where the site is deemed unsafe by the SafeBrowsing service." translateable="false">
     This page is dangerous (flagged by Google Safe Browsing).
   </message>
diff --git a/components/sync/base/pref_names.cc b/components/sync/base/pref_names.cc
index 940e3f89..4ebb41c6 100644
--- a/components/sync/base/pref_names.cc
+++ b/components/sync/base/pref_names.cc
@@ -71,7 +71,7 @@
 const char kSyncManaged[] = "sync.managed";
 
 // Boolean to prevent sync from automatically starting up.  This is
-// used when sync is disabled by the user via the privacy dashboard.
+// used when sync is disabled by the user in sync settings.
 const char kSyncSuppressStart[] = "sync.suppress_start";
 
 // A string that can be used to restore sync encryption infrastructure on
diff --git a/components/typemaps.gni b/components/typemaps.gni
index 94d94bac..f19028a 100644
--- a/components/typemaps.gni
+++ b/components/typemaps.gni
@@ -4,6 +4,7 @@
 
 typemaps = [
   "//components/autofill/content/common/autofill_types.typemap",
+  "//components/chrome_cleaner/public/typemaps/chrome_prompt.typemap",
   "//components/content_settings/core/common/content_settings.typemap",
   "//components/nacl/common/nacl.typemap",
   "//components/password_manager/content/common/credential_manager.typemap",
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
index d1e1448..743b061 100644
--- a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
+++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
@@ -146,6 +146,7 @@
                                                const char* metric_name) {
     const ukm::TestUkmRecorder* ukm_recoder =
         test_browser_client_.GetTestUkmRecorder();
+
     size_t actual_event_count = 0;
     for (size_t i = 0; i < ukm_recoder->entries_count(); ++i) {
       const ukm::mojom::UkmEntry* entry = ukm_recoder->GetEntry(i);
@@ -226,6 +227,7 @@
 
 TEST_F(RenderWidgetHostLatencyTrackerTest, TestWheelToFirstScrollHistograms) {
   const GURL url(kUrl);
+  size_t total_ukm_entry_count = 0;
   contents()->NavigateAndCommit(url);
   for (bool rendering_on_main : {false, true}) {
     ResetHistograms();
@@ -250,6 +252,10 @@
                                  INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
       tracker()->OnGpuSwapBuffersCompleted(wheel_latency);
 
+      // UKM metrics.
+      total_ukm_entry_count++;
+      EXPECT_TRUE(AssertUkmReported("Event.ScrollBegin.Wheel",
+                                    "TimeToScrollUpdateSwapBegin"));
       // Rappor metrics.
       EXPECT_TRUE(
           RapporSampleAssert("Event.Latency.ScrollUpdate.Touch."
@@ -316,8 +322,8 @@
 
       ukm::TestUkmRecorder* test_ukm_recorder =
           test_browser_client_.GetTestUkmRecorder();
-      EXPECT_EQ(0U, test_ukm_recorder->sources_count());
-      EXPECT_EQ(0U, test_ukm_recorder->entries_count());
+      EXPECT_EQ(1U, test_ukm_recorder->sources_count());
+      EXPECT_EQ(total_ukm_entry_count, test_ukm_recorder->entries_count());
     }
   }
 }
@@ -398,6 +404,7 @@
 TEST_F(RenderWidgetHostLatencyTrackerTest, TestTouchToFirstScrollHistograms) {
   const GURL url(kUrl);
   contents()->NavigateAndCommit(url);
+  size_t total_ukm_entry_count = 0;
   for (bool rendering_on_main : {false, true}) {
     ResetHistograms();
     {
@@ -443,6 +450,10 @@
       tracker()->OnGpuSwapBuffersCompleted(touch_latency);
     }
 
+    // UKM metrics.
+    total_ukm_entry_count++;
+    EXPECT_TRUE(AssertUkmReported("Event.ScrollBegin.Touch",
+                                  "TimeToScrollUpdateSwapBegin"));
     // Rappor metrics.
     EXPECT_TRUE(
         RapporSampleAssert("Event.Latency.ScrollUpdate.Touch."
@@ -511,8 +522,8 @@
 
     ukm::TestUkmRecorder* test_ukm_recorder =
         test_browser_client_.GetTestUkmRecorder();
-    EXPECT_EQ(0U, test_ukm_recorder->sources_count());
-    EXPECT_EQ(0U, test_ukm_recorder->entries_count());
+    EXPECT_EQ(1U, test_ukm_recorder->sources_count());
+    EXPECT_EQ(total_ukm_entry_count, test_ukm_recorder->entries_count());
   }
 }
 
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 9e91869..2c4c7000 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -151,6 +151,10 @@
   return empty;
 }
 
+base::FilePath ContentBrowserClient::GetLoggingFileName() {
+  return base::FilePath();
+}
+
 bool ContentBrowserClient::AllowAppCache(const GURL& manifest_url,
                                          const GURL& first_party,
                                          ResourceContext* context) {
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index b39cd98..e2b1af4 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -320,6 +320,10 @@
   // Returns the default favicon.  The callee doesn't own the given bitmap.
   virtual const gfx::ImageSkia* GetDefaultFavicon();
 
+  // Returns the fully qualified path to the log file name, or an empty path.
+  // This function is used by the sandbox to allow write access to the log.
+  virtual base::FilePath GetLoggingFileName();
+
   // Allow the embedder to control if an AppCache can be used for the given url.
   // This is called on the IO thread.
   virtual bool AllowAppCache(const GURL& manifest_url,
diff --git a/ios/chrome/browser/chrome_switches.cc b/ios/chrome/browser/chrome_switches.cc
index 624df12..1c7fede 100644
--- a/ios/chrome/browser/chrome_switches.cc
+++ b/ios/chrome/browser/chrome_switches.cc
@@ -98,13 +98,6 @@
 // prefixed with the character "t" will be treated as Trigger Variation Ids.
 const char kIOSForceVariationIds[] = "force-variation-ids";
 
-// Enables the recording of metrics reports but disables reporting. In contrast
-// to kDisableMetrics, this executes all the code that a normal client would
-// use for reporting, except the report is dropped rather than sent to the
-// server. This is useful for finding issues in the metrics code during UI and
-// performance tests.
-const char kIOSMetricsRecordingOnly[] = "metrics-recording-only";
-
 // A string used to override the default user agent with a custom one.
 const char kUserAgent[] = "user-agent";
 
diff --git a/ios/chrome/browser/chrome_switches.h b/ios/chrome/browser/chrome_switches.h
index ae7b4fa..a862788 100644
--- a/ios/chrome/browser/chrome_switches.h
+++ b/ios/chrome/browser/chrome_switches.h
@@ -38,7 +38,6 @@
 extern const char kEnableSuggestionsUI[];
 
 extern const char kIOSForceVariationIds[];
-extern const char kIOSMetricsRecordingOnly[];
 extern const char kUserAgent[];
 
 extern const char kIOSHostResolverRules[];
diff --git a/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.h b/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.h
index 73478f5c..7aa390d 100644
--- a/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.h
+++ b/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.h
@@ -44,7 +44,6 @@
 
   net::URLRequestContextGetter* GetURLRequestContext() override;
   bool IsMetricsReportingEnabled() override;
-  bool OnlyDoMetricsRecording() override;
 
   // Gets the MetricsStateManager, creating it if it has not already been
   // created.
diff --git a/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.mm b/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.mm
index 7c7340b..d45a42d0 100644
--- a/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.mm
+++ b/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.mm
@@ -98,11 +98,6 @@
   return enabled_state_provider_->IsReportingEnabled();
 }
 
-bool IOSChromeMetricsServicesManagerClient::OnlyDoMetricsRecording() {
-  const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
-  return cmdline->HasSwitch(switches::kIOSMetricsRecordingOnly);
-}
-
 metrics::MetricsStateManager*
 IOSChromeMetricsServicesManagerClient::GetMetricsStateManager() {
   DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/ios/chrome/browser/ssl/ios_security_state_tab_helper.mm b/ios/chrome/browser/ssl/ios_security_state_tab_helper.mm
index c4634660..4d79542 100644
--- a/ios/chrome/browser/ssl/ios_security_state_tab_helper.mm
+++ b/ios/chrome/browser/ssl/ios_security_state_tab_helper.mm
@@ -10,6 +10,7 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "components/security_state/core/security_state.h"
+#include "ios/web/public/browser_state.h"
 #include "ios/web/public/navigation_item.h"
 #import "ios/web/public/navigation_manager.h"
 #import "ios/web/public/origin_util.h"
@@ -62,5 +63,7 @@
       (ssl.content_status & web::SSLStatus::DISPLAYED_CREDIT_CARD_FIELD_ON_HTTP)
           ? true
           : false;
+  state->is_incognito = web_state_->GetBrowserState()->IsOffTheRecord();
+
   return state;
 }
diff --git a/ios/chrome/test/ocmock/BUILD.gn b/ios/chrome/test/ocmock/BUILD.gn
index 6c079a2..cc3ebdf3 100644
--- a/ios/chrome/test/ocmock/BUILD.gn
+++ b/ios/chrome/test/ocmock/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 source_set("ocmock") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
     "OCMockObject+BreakpadControllerTesting.h",
diff --git a/ios/chrome/test/ocmock/OCMockObject+BreakpadControllerTesting.mm b/ios/chrome/test/ocmock/OCMockObject+BreakpadControllerTesting.mm
index df3cc83..ba86516 100644
--- a/ios/chrome/test/ocmock/OCMockObject+BreakpadControllerTesting.mm
+++ b/ios/chrome/test/ocmock/OCMockObject+BreakpadControllerTesting.mm
@@ -8,11 +8,15 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 @implementation OCMockObject (BreakpadControllerTesting)
 
 - (void)cr_expectGetCrashReportCount:(int)crashReportCount {
   id invocationBlock = ^(NSInvocation* invocation) {
-    void (^block)(int);
+    __unsafe_unretained void (^block)(int);
     [invocation getArgument:&block atIndex:2];
     if (!block) {
       ADD_FAILURE();
diff --git a/ios/testing/BUILD.gn b/ios/testing/BUILD.gn
index e4b8ef8..ea12102 100644
--- a/ios/testing/BUILD.gn
+++ b/ios/testing/BUILD.gn
@@ -46,6 +46,7 @@
 }
 
 test("ocmock_support_unittest") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   deps = [
     ":ocmock_support",
     "//base/test:run_all_unittests",
diff --git a/ios/testing/ocmock_complex_type_helper_unittest.mm b/ios/testing/ocmock_complex_type_helper_unittest.mm
index 709c7b9..ab859ca 100644
--- a/ios/testing/ocmock_complex_type_helper_unittest.mm
+++ b/ios/testing/ocmock_complex_type_helper_unittest.mm
@@ -5,11 +5,14 @@
 #import "ios/testing/ocmock_complex_type_helper.h"
 
 #include "base/logging.h"
-#import "base/mac/scoped_nsobject.h"
 #include "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
 #include "third_party/ocmock/gtest_support.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 // A complex type to test with..
 struct SampleComplexType {
   int number;
@@ -49,9 +52,9 @@
  protected:
   void SetUp() override {
     PlatformTest::SetUp();
-    helped_mock_.reset([[MockClass alloc]
-        initWithRepresentedObject:
-            [OCMockObject mockForProtocol:@protocol(TestedProtocol)]]);
+    OCMockObject* protocol_mock =
+        [OCMockObject mockForProtocol:@protocol(TestedProtocol)];
+    helped_mock_ = [[MockClass alloc] initWithRepresentedObject:protocol_mock];
   }
 
   void TearDown() override {
@@ -59,7 +62,7 @@
     PlatformTest::TearDown();
   }
 
-  base::scoped_nsobject<id> helped_mock_;
+  id helped_mock_;
 };
 
 TEST_F(OCMockComplexTypeHelperTest, nilObjectStillWorks) {
@@ -68,7 +71,7 @@
 }
 
 TEST_F(OCMockComplexTypeHelperTest, anyObjectStillWorks) {
-  base::scoped_nsobject<id> someObject([[NSObject alloc] init]);
+  id someObject = [[NSObject alloc] init];
   [[helped_mock_ expect] passObject:OCMOCK_ANY];
   [helped_mock_ passObject:someObject];
 }
diff --git a/media/base/video_frame.cc b/media/base/video_frame.cc
index 7ac322a..8e3eaff 100644
--- a/media/base/video_frame.cc
+++ b/media/base/video_frame.cc
@@ -23,6 +23,16 @@
 
 namespace media {
 
+namespace {
+
+// Helper to privide gfx::Rect::Intersect() as an expression.
+gfx::Rect Intersection(gfx::Rect a, const gfx::Rect& b) {
+  a.Intersect(b);
+  return a;
+}
+
+}  // namespace
+
 // Static POD class for generating unique identifiers for each VideoFrame.
 static base::StaticAtomicSequenceNumber g_unique_id_generator;
 
@@ -923,13 +933,16 @@
     : format_(format),
       storage_type_(storage_type),
       coded_size_(coded_size),
-      visible_rect_(visible_rect),
+      visible_rect_(Intersection(visible_rect, gfx::Rect(coded_size))),
       natural_size_(natural_size),
       shared_memory_offset_(0),
       timestamp_(timestamp),
       unique_id_(g_unique_id_generator.GetNext()) {
   DCHECK(IsValidConfig(format_, storage_type, coded_size_, visible_rect_,
                        natural_size_));
+  DCHECK(visible_rect_ == visible_rect)
+      << "visible_rect " << visible_rect.ToString() << " exceeds coded_size "
+      << coded_size.ToString();
   memset(&mailbox_holders_, 0, sizeof(mailbox_holders_));
   memset(&strides_, 0, sizeof(strides_));
   memset(&data_, 0, sizeof(data_));
diff --git a/media/mojo/common/media_type_converters.cc b/media/mojo/common/media_type_converters.cc
index 0a415d9..5571a8b 100644
--- a/media/mojo/common/media_type_converters.cc
+++ b/media/mojo/common/media_type_converters.cc
@@ -18,8 +18,6 @@
 #include "media/base/encryption_scheme.h"
 #include "media/base/subsample_entry.h"
 #include "media/base/video_decoder_config.h"
-#include "media/base/video_frame.h"
-#include "media/mojo/common/mojo_shared_buffer_video_frame.h"
 #include "mojo/public/cpp/system/buffer.h"
 
 namespace mojo {
@@ -342,69 +340,4 @@
       input->timestamp);
 }
 
-// static
-media::mojom::VideoFramePtr
-TypeConverter<media::mojom::VideoFramePtr, scoped_refptr<media::VideoFrame>>::
-    Convert(const scoped_refptr<media::VideoFrame>& input) {
-  media::mojom::VideoFramePtr frame(media::mojom::VideoFrame::New());
-  frame->end_of_stream =
-      input->metadata()->IsTrue(media::VideoFrameMetadata::END_OF_STREAM);
-  if (frame->end_of_stream)
-    return frame;
-
-  // Handle non EOS frame. It must be a MojoSharedBufferVideoFrame.
-  // TODO(jrummell): Support other types of VideoFrame.
-  CHECK_EQ(media::VideoFrame::STORAGE_MOJO_SHARED_BUFFER,
-           input->storage_type());
-  media::MojoSharedBufferVideoFrame* input_frame =
-      static_cast<media::MojoSharedBufferVideoFrame*>(input.get());
-  mojo::ScopedSharedBufferHandle duplicated_handle =
-      input_frame->Handle().Clone();
-  CHECK(duplicated_handle.is_valid());
-
-  frame->format = input->format();
-  frame->coded_size = input->coded_size();
-  frame->visible_rect = input->visible_rect();
-  frame->natural_size = input->natural_size();
-  frame->timestamp = input->timestamp();
-
-  media::mojom::SharedBufferVideoFrameDataPtr data =
-      media::mojom::SharedBufferVideoFrameData::New();
-  data->frame_data = std::move(duplicated_handle);
-  data->frame_data_size = input_frame->MappedSize();
-  data->y_stride = input_frame->stride(media::VideoFrame::kYPlane);
-  data->u_stride = input_frame->stride(media::VideoFrame::kUPlane);
-  data->v_stride = input_frame->stride(media::VideoFrame::kVPlane);
-  data->y_offset = input_frame->PlaneOffset(media::VideoFrame::kYPlane);
-  data->u_offset = input_frame->PlaneOffset(media::VideoFrame::kUPlane);
-  data->v_offset = input_frame->PlaneOffset(media::VideoFrame::kVPlane);
-
-  frame->data = media::mojom::VideoFrameData::New();
-  frame->data->set_shared_buffer_data(std::move(data));
-  return frame;
-}
-
-// static
-scoped_refptr<media::VideoFrame>
-TypeConverter<scoped_refptr<media::VideoFrame>, media::mojom::VideoFramePtr>::
-    Convert(const media::mojom::VideoFramePtr& input) {
-  if (input->end_of_stream)
-    return media::VideoFrame::CreateEOSFrame();
-
-  // Handle non EOS frame. It must be a MojoSharedBufferVideoFrame.
-  // TODO(jrummell): Support other types of VideoFrame.
-  DCHECK(input->data->is_shared_buffer_data());
-  const media::mojom::SharedBufferVideoFrameDataPtr& data =
-      input->data->get_shared_buffer_data();
-
-  return media::MojoSharedBufferVideoFrame::Create(
-      input->format, input->coded_size, input->visible_rect,
-      input->natural_size, std::move(data->frame_data),
-      base::saturated_cast<size_t>(data->frame_data_size),
-      base::saturated_cast<size_t>(data->y_offset),
-      base::saturated_cast<size_t>(data->u_offset),
-      base::saturated_cast<size_t>(data->v_offset), data->y_stride,
-      data->u_stride, data->v_stride, input->timestamp);
-}
-
 }  // namespace mojo
diff --git a/media/mojo/common/media_type_converters.h b/media/mojo/common/media_type_converters.h
index d0afe70..c10c277 100644
--- a/media/mojo/common/media_type_converters.h
+++ b/media/mojo/common/media_type_converters.h
@@ -19,7 +19,6 @@
 class DecryptConfig;
 class EncryptionScheme;
 class VideoDecoderConfig;
-class VideoFrame;
 struct CdmConfig;
 struct CdmKeyInformation;
 }
@@ -127,19 +126,6 @@
       const media::mojom::AudioBufferPtr& input);
 };
 
-template <>
-struct TypeConverter<media::mojom::VideoFramePtr,
-                     scoped_refptr<media::VideoFrame>> {
-  static media::mojom::VideoFramePtr Convert(
-      const scoped_refptr<media::VideoFrame>& input);
-};
-template <>
-struct TypeConverter<scoped_refptr<media::VideoFrame>,
-                     media::mojom::VideoFramePtr> {
-  static scoped_refptr<media::VideoFrame> Convert(
-      const media::mojom::VideoFramePtr& input);
-};
-
 }  // namespace mojo
 
 #endif  // MEDIA_MOJO_COMMON_MEDIA_TYPE_CONVERTERS_H_
diff --git a/media/mojo/interfaces/media_types.mojom b/media/mojo/interfaces/media_types.mojom
index e206edb..5448756c 100644
--- a/media/mojo/interfaces/media_types.mojom
+++ b/media/mojo/interfaces/media_types.mojom
@@ -213,22 +213,24 @@
   // Natural size of the frame.
   gfx.mojom.Size natural_size;
 
-  // True if end of stream.
-  bool end_of_stream;
-
   // Timestamp in microseconds of the associated frame.
   mojo.common.mojom.TimeDelta timestamp;
 
-  // Contents of the video frame. Will be null for end of stream frames.
-  VideoFrameData? data;
+  // Contents of the video frame (or EOS marker).
+  VideoFrameData data;
 };
 
 // Possible choices for storing VideoFrame data.
 union VideoFrameData {
+  EosVideoFrameData eos_data;
   SharedBufferVideoFrameData shared_buffer_data;
   MailboxVideoFrameData mailbox_data;
 };
 
+// A marker for EOS frames.
+struct EosVideoFrameData {
+};
+
 // This defines video frame data stored in a Mojo shared buffer.
 struct SharedBufferVideoFrameData {
   // Reference to the shared memory containing the frame's data.
diff --git a/media/mojo/interfaces/video_frame_struct_traits.cc b/media/mojo/interfaces/video_frame_struct_traits.cc
index 4d6aebf4..c68fed42 100644
--- a/media/mojo/interfaces/video_frame_struct_traits.cc
+++ b/media/mojo/interfaces/video_frame_struct_traits.cc
@@ -16,9 +16,10 @@
 
 media::mojom::VideoFrameDataPtr MakeVideoFrameData(
     const scoped_refptr<media::VideoFrame>& input) {
-  // EOS frames contain no data.
-  if (input->metadata()->IsTrue(media::VideoFrameMetadata::END_OF_STREAM))
-    return nullptr;
+  if (input->metadata()->IsTrue(media::VideoFrameMetadata::END_OF_STREAM)) {
+    return media::mojom::VideoFrameData::NewEosData(
+        media::mojom::EosVideoFrameData::New());
+  }
 
   if (input->storage_type() == media::VideoFrame::STORAGE_MOJO_SHARED_BUFFER) {
     media::MojoSharedBufferVideoFrame* mojo_frame =
@@ -86,8 +87,7 @@
   media::mojom::VideoFrameDataDataView data;
   input.GetDataDataView(&data);
 
-  DCHECK_EQ(input.end_of_stream(), data.is_null());
-  if (input.end_of_stream()) {
+  if (data.is_eos_data()) {
     *output = media::VideoFrame::CreateEOSFrame();
     return !!*output;
   }
@@ -104,11 +104,8 @@
   if (!input.ReadVisibleRect(&visible_rect))
     return false;
 
-  // Coded size must contain the visible rect.
-  if (visible_rect.right() > coded_size.width() ||
-      visible_rect.bottom() > coded_size.height()) {
+  if (!gfx::Rect(coded_size).Contains(visible_rect))
     return false;
-  }
 
   gfx::Size natural_size;
   if (!input.ReadNaturalSize(&natural_size))
diff --git a/media/mojo/interfaces/video_frame_struct_traits.h b/media/mojo/interfaces/video_frame_struct_traits.h
index d674b162..3324b09 100644
--- a/media/mojo/interfaces/video_frame_struct_traits.h
+++ b/media/mojo/interfaces/video_frame_struct_traits.h
@@ -52,10 +52,6 @@
     return input->natural_size();
   }
 
-  static bool end_of_stream(const scoped_refptr<media::VideoFrame>& input) {
-    return input->metadata()->IsTrue(media::VideoFrameMetadata::END_OF_STREAM);
-  }
-
   static base::TimeDelta timestamp(
       const scoped_refptr<media::VideoFrame>& input) {
     return input->timestamp();
diff --git a/net/http/http_proxy_client_socket_pool.cc b/net/http/http_proxy_client_socket_pool.cc
index 54b2a1b..650c7a4 100644
--- a/net/http/http_proxy_client_socket_pool.cc
+++ b/net/http/http_proxy_client_socket_pool.cc
@@ -5,11 +5,17 @@
 #include "net/http/http_proxy_client_socket_pool.h"
 
 #include <algorithm>
+#include <map>
+#include <string>
 #include <utility>
 
 #include "base/compiler_specific.h"
 #include "base/memory/ptr_util.h"
-#include "base/time/time.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/optional.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
 #include "base/values.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
@@ -18,6 +24,7 @@
 #include "net/http/http_proxy_client_socket_wrapper.h"
 #include "net/log/net_log_source_type.h"
 #include "net/log/net_log_with_source.h"
+#include "net/nqe/network_quality_provider.h"
 #include "net/socket/client_socket_factory.h"
 #include "net/socket/client_socket_handle.h"
 #include "net/socket/client_socket_pool_base.h"
@@ -45,6 +52,28 @@
 static const int kHttpProxyConnectJobTimeoutInSeconds = 30;
 #endif
 
+static const char kNetAdaptiveProxyConnectionTimeout[] =
+    "NetAdaptiveProxyConnectionTimeout";
+
+bool IsInNetAdaptiveProxyConnectionTimeoutFieldTrial() {
+  // Field trial is enabled if the group name starts with "Enabled".
+  return base::FieldTrialList::FindFullName(kNetAdaptiveProxyConnectionTimeout)
+             .find("Enabled") == 0;
+}
+
+// Return the value of the parameter |param_name| for the field trial
+// |kNetAdaptiveProxyConnectionTimeout|. If the value of the parameter is
+// unavailable, then |default_value| is available.
+int32_t GetInt32Param(const std::string& param_name, int32_t default_value) {
+  int32_t param;
+  if (!base::StringToInt(base::GetFieldTrialParamValue(
+                             kNetAdaptiveProxyConnectionTimeout, param_name),
+                         &param)) {
+    return default_value;
+  }
+  return param;
+}
+
 }  // namespace
 
 HttpProxySocketParams::HttpProxySocketParams(
@@ -162,7 +191,17 @@
     : transport_pool_(transport_pool),
       ssl_pool_(ssl_pool),
       network_quality_provider_(network_quality_provider),
-      net_log_(net_log) {}
+      transport_rtt_multiplier_(GetInt32Param("transport_rtt_multiplier", 5)),
+      min_proxy_connection_timeout_(base::TimeDelta::FromSeconds(
+          GetInt32Param("min_proxy_connection_timeout_seconds", 8))),
+      max_proxy_connection_timeout_(base::TimeDelta::FromSeconds(
+          GetInt32Param("max_proxy_connection_timeout_seconds", 60))),
+      net_log_(net_log) {
+  DCHECK_LT(0, transport_rtt_multiplier_);
+  DCHECK_LE(base::TimeDelta(), min_proxy_connection_timeout_);
+  DCHECK_LE(base::TimeDelta(), max_proxy_connection_timeout_);
+  DCHECK_LE(min_proxy_connection_timeout_, max_proxy_connection_timeout_);
+}
 
 std::unique_ptr<ConnectJob>
 HttpProxyClientSocketPool::HttpProxyConnectJobFactory::NewConnectJob(
@@ -176,11 +215,25 @@
 }
 
 base::TimeDelta
-HttpProxyClientSocketPool::HttpProxyConnectJobFactory::ConnectionTimeout(
-    ) const {
-  // TODO(tbansal): https://crbug.com/704339. Use |network_quality_provider_|
-  // and field trial to determine the connection timeout.
-  ALLOW_UNUSED_LOCAL(network_quality_provider_);
+HttpProxyClientSocketPool::HttpProxyConnectJobFactory::ConnectionTimeout()
+    const {
+  if (IsInNetAdaptiveProxyConnectionTimeoutFieldTrial() &&
+      network_quality_provider_) {
+    base::Optional<base::TimeDelta> transport_rtt_estimate =
+        network_quality_provider_->GetTransportRTT();
+    if (transport_rtt_estimate) {
+      base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(
+          transport_rtt_multiplier_ *
+          transport_rtt_estimate.value().InMilliseconds());
+      // Ensure that connection timeout is between
+      // |min_proxy_connection_timeout_| and |max_proxy_connection_timeout_|.
+      if (timeout < min_proxy_connection_timeout_)
+        return min_proxy_connection_timeout_;
+      if (timeout > max_proxy_connection_timeout_)
+        return max_proxy_connection_timeout_;
+      return timeout;
+    }
+  }
 
   // Return the default proxy connection timeout.
   base::TimeDelta max_pool_timeout = base::TimeDelta();
diff --git a/net/http/http_proxy_client_socket_pool.h b/net/http/http_proxy_client_socket_pool.h
index bf6dede8..24c4517 100644
--- a/net/http/http_proxy_client_socket_pool.h
+++ b/net/http/http_proxy_client_socket_pool.h
@@ -5,6 +5,8 @@
 #ifndef NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_POOL_H_
 #define NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_POOL_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <string>
 
@@ -226,6 +228,9 @@
     TransportClientSocketPool* const transport_pool_;
     SSLClientSocketPool* const ssl_pool_;
     NetworkQualityProvider* network_quality_provider_;
+    const int32_t transport_rtt_multiplier_;
+    const base::TimeDelta min_proxy_connection_timeout_;
+    const base::TimeDelta max_proxy_connection_timeout_;
     NetLog* net_log_;
 
     DISALLOW_COPY_AND_ASSIGN(HttpProxyConnectJobFactory);
diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc
index e05b203..9f3703d0 100644
--- a/net/http/http_proxy_client_socket_pool_unittest.cc
+++ b/net/http/http_proxy_client_socket_pool_unittest.cc
@@ -4,10 +4,16 @@
 
 #include "net/http/http_proxy_client_socket_pool.h"
 
+#include <map>
+#include <string>
 #include <utility>
 
 #include "base/callback.h"
 #include "base/compiler_specific.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/field_trial_param_associator.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/histogram_tester.h"
@@ -75,17 +81,50 @@
                          NULL,
                          session_deps_.ssl_config_service.get(),
                          NetLogWithSource().net_log()),
-        pool_(kMaxSockets,
-              kMaxSocketsPerGroup,
-              &transport_socket_pool_,
-              &ssl_socket_pool_,
-              &estimator_,
-              NULL) {
+        field_trial_list_(nullptr),
+        pool_(
+            base::MakeUnique<HttpProxyClientSocketPool>(kMaxSockets,
+                                                        kMaxSocketsPerGroup,
+                                                        &transport_socket_pool_,
+                                                        &ssl_socket_pool_,
+                                                        &estimator_,
+                                                        nullptr)) {
     session_ = CreateNetworkSession();
   }
 
   virtual ~HttpProxyClientSocketPoolTest() {}
 
+  // Initializes the field trial paramters for the field trial that determines
+  // connection timeout based on the network quality.
+  void InitAdaptiveTimeoutFieldTrialWithParams(
+      bool use_default_params,
+      int transport_rtt_multiplier,
+      base::TimeDelta min_proxy_connection_timeout,
+      base::TimeDelta max_proxy_connection_timeout) {
+    std::string trial_name = "NetAdaptiveProxyConnectionTimeout";
+    std::string group_name = "Enabled";
+
+    std::map<std::string, std::string> params;
+    if (!use_default_params) {
+      params["transport_rtt_multiplier"] =
+          base::IntToString(transport_rtt_multiplier);
+      params["min_proxy_connection_timeout_seconds"] =
+          base::IntToString(min_proxy_connection_timeout.InSeconds());
+      params["max_proxy_connection_timeout_seconds"] =
+          base::IntToString(max_proxy_connection_timeout.InSeconds());
+    }
+    base::FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
+    EXPECT_TRUE(
+        base::AssociateFieldTrialParams(trial_name, group_name, params));
+    EXPECT_TRUE(base::FieldTrialList::CreateFieldTrial(trial_name, group_name));
+
+    // Reset |pool_| so that the field trial parameters are read by the
+    // |pool_|.
+    pool_ = base::MakeUnique<HttpProxyClientSocketPool>(
+        kMaxSockets, kMaxSocketsPerGroup, &transport_socket_pool_,
+        &ssl_socket_pool_, &estimator_, NetLogWithSource().net_log());
+  }
+
   void AddAuthToCache() {
     const base::string16 kFoo(base::ASCIIToUTF16("foo"));
     const base::string16 kBar(base::ASCIIToUTF16("bar"));
@@ -196,6 +235,8 @@
 
   const base::HistogramTester& histogram_tester() { return histogram_tester_; }
 
+  TestNetworkQualityEstimator* estimator() { return &estimator_; }
+
  private:
   SpdySessionDependencies session_deps_;
 
@@ -210,11 +251,13 @@
 
   base::HistogramTester histogram_tester_;
 
+  base::FieldTrialList field_trial_list_;
+
  protected:
   SpdyTestUtil spdy_util_;
   std::unique_ptr<SSLSocketDataProvider> ssl_data_;
   std::unique_ptr<SequencedSocketData> data_;
-  HttpProxyClientSocketPool pool_;
+  std::unique_ptr<HttpProxyClientSocketPool> pool_;
   ClientSocketHandle handle_;
   TestCompletionCallback callback_;
 };
@@ -231,7 +274,7 @@
   std::unique_ptr<TestProxyDelegate> proxy_delegate(new TestProxyDelegate());
   int rv = handle_.Init("a", CreateNoTunnelParams(proxy_delegate.get()), LOW,
                         ClientSocketPool::RespectLimits::ENABLED,
-                        CompletionCallback(), &pool_, NetLogWithSource());
+                        CompletionCallback(), pool_.get(), NetLogWithSource());
   EXPECT_THAT(rv, IsOk());
   EXPECT_TRUE(handle_.is_initialized());
   ASSERT_TRUE(handle_.socket());
@@ -251,9 +294,10 @@
 // (non-SSL) socket request on Init.
 TEST_P(HttpProxyClientSocketPoolTest, SetSocketRequestPriorityOnInit) {
   Initialize(NULL, 0, NULL, 0, NULL, 0, NULL, 0);
-  EXPECT_EQ(OK, handle_.Init("a", CreateNoTunnelParams(NULL), HIGHEST,
-                             ClientSocketPool::RespectLimits::ENABLED,
-                             CompletionCallback(), &pool_, NetLogWithSource()));
+  EXPECT_EQ(
+      OK, handle_.Init("a", CreateNoTunnelParams(NULL), HIGHEST,
+                       ClientSocketPool::RespectLimits::ENABLED,
+                       CompletionCallback(), pool_.get(), NetLogWithSource()));
   EXPECT_EQ(HIGHEST, GetLastTransportRequestPriority());
 }
 
@@ -293,7 +337,7 @@
 
   int rv = handle_.Init("a", CreateTunnelParams(NULL), LOW,
                         ClientSocketPool::RespectLimits::ENABLED,
-                        callback_.callback(), &pool_, NetLogWithSource());
+                        callback_.callback(), pool_.get(), NetLogWithSource());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
   EXPECT_FALSE(handle_.is_initialized());
   EXPECT_FALSE(handle_.socket());
@@ -342,7 +386,7 @@
   std::unique_ptr<TestProxyDelegate> proxy_delegate(new TestProxyDelegate());
   int rv = handle_.Init("a", CreateTunnelParams(proxy_delegate.get()), LOW,
                         ClientSocketPool::RespectLimits::ENABLED,
-                        callback_.callback(), &pool_, NetLogWithSource());
+                        callback_.callback(), pool_.get(), NetLogWithSource());
   EXPECT_THAT(rv, IsOk());
   EXPECT_TRUE(handle_.is_initialized());
   ASSERT_TRUE(handle_.socket());
@@ -393,7 +437,7 @@
   std::unique_ptr<TestProxyDelegate> proxy_delegate(new TestProxyDelegate());
   int rv = handle_.Init("a", CreateTunnelParams(proxy_delegate.get()), LOW,
                         ClientSocketPool::RespectLimits::ENABLED,
-                        callback_.callback(), &pool_, NetLogWithSource());
+                        callback_.callback(), pool_.get(), NetLogWithSource());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
   EXPECT_FALSE(handle_.is_initialized());
   EXPECT_FALSE(handle_.socket());
@@ -427,10 +471,11 @@
              spdy_writes, arraysize(spdy_writes));
   AddAuthToCache();
 
-  EXPECT_EQ(ERR_IO_PENDING,
-            handle_.Init("a", CreateTunnelParams(NULL), MEDIUM,
-                         ClientSocketPool::RespectLimits::ENABLED,
-                         callback_.callback(), &pool_, NetLogWithSource()));
+  EXPECT_EQ(
+      ERR_IO_PENDING,
+      handle_.Init("a", CreateTunnelParams(NULL), MEDIUM,
+                   ClientSocketPool::RespectLimits::ENABLED,
+                   callback_.callback(), pool_.get(), NetLogWithSource()));
   EXPECT_EQ(MEDIUM, GetLastTransportRequestPriority());
 
   EXPECT_THAT(callback_.WaitForResult(), IsOk());
@@ -446,7 +491,7 @@
 
   int rv = handle_.Init("a", CreateTunnelParams(NULL), LOW,
                         ClientSocketPool::RespectLimits::ENABLED,
-                        callback_.callback(), &pool_, NetLogWithSource());
+                        callback_.callback(), pool_.get(), NetLogWithSource());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
   EXPECT_FALSE(handle_.is_initialized());
   EXPECT_FALSE(handle_.socket());
@@ -479,7 +524,7 @@
 
   int rv = handle_.Init("a", CreateTunnelParams(NULL), LOW,
                         ClientSocketPool::RespectLimits::ENABLED,
-                        callback_.callback(), &pool_, NetLogWithSource());
+                        callback_.callback(), pool_.get(), NetLogWithSource());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
   EXPECT_FALSE(handle_.is_initialized());
   EXPECT_FALSE(handle_.socket());
@@ -511,7 +556,7 @@
 
   int rv = handle_.Init("a", CreateTunnelParams(NULL), LOW,
                         ClientSocketPool::RespectLimits::ENABLED,
-                        callback_.callback(), &pool_, NetLogWithSource());
+                        callback_.callback(), pool_.get(), NetLogWithSource());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
   EXPECT_FALSE(handle_.is_initialized());
   EXPECT_FALSE(handle_.socket());
@@ -554,7 +599,7 @@
 
   int rv = handle_.Init("a", CreateTunnelParams(NULL), LOW,
                         ClientSocketPool::RespectLimits::ENABLED,
-                        callback_.callback(), &pool_, NetLogWithSource());
+                        callback_.callback(), pool_.get(), NetLogWithSource());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
   EXPECT_FALSE(handle_.is_initialized());
   EXPECT_FALSE(handle_.socket());
@@ -594,7 +639,7 @@
 
   int rv = handle_.Init("a", CreateTunnelParams(NULL), LOW,
                         ClientSocketPool::RespectLimits::ENABLED,
-                        callback_.callback(), &pool_, NetLogWithSource());
+                        callback_.callback(), pool_.get(), NetLogWithSource());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
   EXPECT_FALSE(handle_.is_initialized());
   EXPECT_FALSE(handle_.socket());
@@ -633,7 +678,7 @@
 
   int rv = handle_.Init("a", CreateTunnelParams(NULL), LOW,
                         ClientSocketPool::RespectLimits::ENABLED,
-                        callback_.callback(), &pool_, NetLogWithSource());
+                        callback_.callback(), pool_.get(), NetLogWithSource());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
   EXPECT_FALSE(handle_.is_initialized());
   EXPECT_FALSE(handle_.socket());
@@ -690,7 +735,7 @@
 
   int rv = handle_.Init("a", CreateTunnelParams(NULL), LOW,
                         ClientSocketPool::RespectLimits::ENABLED,
-                        callback_.callback(), &pool_, NetLogWithSource());
+                        callback_.callback(), pool_.get(), NetLogWithSource());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
   EXPECT_FALSE(handle_.is_initialized());
   EXPECT_FALSE(handle_.socket());
@@ -727,17 +772,118 @@
 }
 
 TEST_P(HttpProxyClientSocketPoolTest, ProxyPoolTimeout) {
-  ASSERT_LE(base::TimeDelta(), pool_.ConnectionTimeout());
+  EXPECT_LE(base::TimeDelta(), pool_->ConnectionTimeout());
 
   // Test against a large value.
-  ASSERT_GE(base::TimeDelta::FromMinutes(10), pool_.ConnectionTimeout());
+  EXPECT_GE(base::TimeDelta::FromMinutes(10), pool_->ConnectionTimeout());
 
 #if (defined(OS_ANDROID) || defined(OS_IOS))
   // On Android and iOS, the timeout is fixed to 10 seconds.
-  ASSERT_EQ(base::TimeDelta::FromSeconds(10), pool_.ConnectionTimeout());
+  EXPECT_EQ(base::TimeDelta::FromSeconds(10), pool_->ConnectionTimeout());
 #endif
 }
 
+// Tests the connection timeout values when the field trial parameters are
+// specified.
+TEST_P(HttpProxyClientSocketPoolTest, ProxyPoolTimeoutWithExperiment) {
+  int transport_rtt_multiplier = 2;
+  base::TimeDelta min_timeout = base::TimeDelta::FromSeconds(8);
+  base::TimeDelta max_timeout = base::TimeDelta::FromSeconds(20);
+
+  InitAdaptiveTimeoutFieldTrialWithParams(false, transport_rtt_multiplier,
+                                          min_timeout, max_timeout);
+  EXPECT_LE(base::TimeDelta(), pool_->ConnectionTimeout());
+
+  // Timeout should be |transport_rtt_multiplier| times the transport RTT
+  // estimate.
+  base::TimeDelta rtt_estimate = base::TimeDelta::FromSeconds(7);
+  estimator()->set_start_time_null_transport_rtt(rtt_estimate);
+  EXPECT_EQ(rtt_estimate + rtt_estimate, pool_->ConnectionTimeout());
+
+  // A change in RTT estimate should also change the connection timeout.
+  rtt_estimate = base::TimeDelta::FromSeconds(8);
+  estimator()->set_start_time_null_transport_rtt(rtt_estimate);
+  EXPECT_EQ(rtt_estimate + rtt_estimate, pool_->ConnectionTimeout());
+
+  // Connection timeout should not exceed |max_timeout|.
+  rtt_estimate = base::TimeDelta::FromSeconds(25);
+  estimator()->set_start_time_null_transport_rtt(rtt_estimate);
+  EXPECT_EQ(max_timeout, pool_->ConnectionTimeout());
+
+  // Connection timeout should not be less than |min_timeout|.
+  rtt_estimate = base::TimeDelta::FromSeconds(0);
+  estimator()->set_start_time_null_transport_rtt(rtt_estimate);
+  EXPECT_EQ(min_timeout, pool_->ConnectionTimeout());
+}
+
+// Tests the connection timeout values when the field trial parameters are
+// specified.
+TEST_P(HttpProxyClientSocketPoolTest,
+       ProxyPoolTimeoutWithExperimentDifferentParams) {
+  int transport_rtt_multiplier = 3;
+  base::TimeDelta min_timeout = base::TimeDelta::FromSeconds(2);
+  base::TimeDelta max_timeout = base::TimeDelta::FromSeconds(30);
+
+  InitAdaptiveTimeoutFieldTrialWithParams(false, transport_rtt_multiplier,
+                                          min_timeout, max_timeout);
+  EXPECT_LE(base::TimeDelta(), pool_->ConnectionTimeout());
+
+  // Timeout should be |transport_rtt_multiplier| times the transport RTT
+  // estimate.
+  base::TimeDelta rtt_estimate = base::TimeDelta::FromSeconds(2);
+  estimator()->set_start_time_null_transport_rtt(rtt_estimate);
+  EXPECT_EQ(rtt_estimate + rtt_estimate + rtt_estimate,
+            pool_->ConnectionTimeout());
+
+  // A change in RTT estimate should also change the connection timeout.
+  rtt_estimate = base::TimeDelta::FromSeconds(7);
+  estimator()->set_start_time_null_transport_rtt(rtt_estimate);
+  EXPECT_EQ(rtt_estimate + rtt_estimate + rtt_estimate,
+            pool_->ConnectionTimeout());
+
+  // Connection timeout should not exceed |max_timeout|.
+  rtt_estimate = base::TimeDelta::FromSeconds(35);
+  estimator()->set_start_time_null_transport_rtt(rtt_estimate);
+  EXPECT_EQ(max_timeout, pool_->ConnectionTimeout());
+
+  // Connection timeout should not be less than |min_timeout|.
+  rtt_estimate = base::TimeDelta::FromSeconds(0);
+  estimator()->set_start_time_null_transport_rtt(rtt_estimate);
+  EXPECT_EQ(min_timeout, pool_->ConnectionTimeout());
+}
+
+// Tests the connection timeout values when the field trial parameters are not
+// specified.
+TEST_P(HttpProxyClientSocketPoolTest,
+       ProxyPoolTimeoutWithExperimentDefaultParams) {
+  InitAdaptiveTimeoutFieldTrialWithParams(true, 0, base::TimeDelta(),
+                                          base::TimeDelta());
+  EXPECT_LE(base::TimeDelta(), pool_->ConnectionTimeout());
+
+  // Timeout should be |transport_rtt_multiplier| times the transport RTT
+  // estimate.
+  base::TimeDelta rtt_estimate = base::TimeDelta::FromMilliseconds(10);
+  estimator()->set_start_time_null_transport_rtt(rtt_estimate);
+  // Connection timeout should not be less than the transport RTT estimate.
+  EXPECT_LE(rtt_estimate, pool_->ConnectionTimeout());
+
+  // A change in RTT estimate should also change the connection timeout.
+  rtt_estimate = base::TimeDelta::FromSeconds(10);
+  estimator()->set_start_time_null_transport_rtt(rtt_estimate);
+  // Connection timeout should not be less than the transport RTT estimate.
+  EXPECT_LE(rtt_estimate, pool_->ConnectionTimeout());
+
+  // Set RTT to a very large value.
+  rtt_estimate = base::TimeDelta::FromMinutes(60);
+  estimator()->set_start_time_null_transport_rtt(rtt_estimate);
+  EXPECT_GT(rtt_estimate, pool_->ConnectionTimeout());
+
+  // Set RTT to a very small value.
+  rtt_estimate = base::TimeDelta::FromSeconds(0);
+  estimator()->set_start_time_null_transport_rtt(rtt_estimate);
+  EXPECT_LT(rtt_estimate, pool_->ConnectionTimeout());
+}
+
 // It would be nice to also test the timeouts in HttpProxyClientSocketPool.
 
 }  // namespace net
diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc
index c97829f..99ad8291 100644
--- a/net/http/http_stream_factory_impl_job.cc
+++ b/net/http/http_stream_factory_impl_job.cc
@@ -125,7 +125,8 @@
     const NetLogSource& source,
     const GURL* original_url,
     const GURL* url,
-    const AlternativeService* alternative_service,
+    bool expect_spdy,
+    bool using_quic,
     RequestPriority priority,
     NetLogCaptureMode /* capture_mode */) {
   auto dict = base::MakeUnique<base::DictionaryValue>();
@@ -133,7 +134,8 @@
     source.AddToEventParameters(dict.get());
   dict->SetString("original_url", original_url->GetOrigin().spec());
   dict->SetString("url", url->GetOrigin().spec());
-  dict->SetString("alternative_service", alternative_service->ToString());
+  dict->SetString("expect_spdy", expect_spdy ? "true" : "false");
+  dict->SetString("using_quic", using_quic ? "true" : "false");
   dict->SetString("priority", RequestPriorityToString(priority));
   return std::move(dict);
 }
@@ -159,34 +161,7 @@
                                 const SSLConfig& proxy_ssl_config,
                                 HostPortPair destination,
                                 GURL origin_url,
-                                bool enable_ip_based_pooling,
-                                NetLog* net_log)
-    : Job(delegate,
-          job_type,
-          session,
-          request_info,
-          priority,
-          proxy_info,
-          server_ssl_config,
-          proxy_ssl_config,
-          destination,
-          origin_url,
-          AlternativeService(),
-          ProxyServer(),
-          enable_ip_based_pooling,
-          net_log) {}
-
-HttpStreamFactoryImpl::Job::Job(Delegate* delegate,
-                                JobType job_type,
-                                HttpNetworkSession* session,
-                                const HttpRequestInfo& request_info,
-                                RequestPriority priority,
-                                const ProxyInfo& proxy_info,
-                                const SSLConfig& server_ssl_config,
-                                const SSLConfig& proxy_ssl_config,
-                                HostPortPair destination,
-                                GURL origin_url,
-                                AlternativeService alternative_service,
+                                NextProto alternative_protocol,
                                 const ProxyServer& alternative_proxy_server,
                                 bool enable_ip_based_pooling,
                                 NetLog* net_log)
@@ -204,15 +179,17 @@
       next_state_(STATE_NONE),
       destination_(destination),
       origin_url_(origin_url),
-      alternative_service_(alternative_service),
       alternative_proxy_server_(alternative_proxy_server),
       enable_ip_based_pooling_(enable_ip_based_pooling),
       delegate_(delegate),
       job_type_(job_type),
       using_ssl_(origin_url_.SchemeIs(url::kHttpsScheme) ||
                  origin_url_.SchemeIs(url::kWssScheme)),
+      using_quic_(
+          alternative_protocol == kProtoQUIC ||
+          ShouldForceQuic(session, destination, origin_url, proxy_info)),
+      expect_spdy_(alternative_protocol == kProtoHTTP2 && !using_quic_),
       using_spdy_(false),
-      using_quic_(false),
       should_reconsider_proxy_(false),
       quic_request_(session_->quic_stream_factory()),
       using_existing_quic_session_(false),
@@ -222,34 +199,34 @@
       num_streams_(0),
       spdy_session_direct_(
           !(proxy_info.is_https() && origin_url_.SchemeIs(url::kHttpScheme))),
-      spdy_session_key_(GetSpdySessionKey()),
+      spdy_session_key_(GetSpdySessionKey(spdy_session_direct_,
+                                          proxy_info_.proxy_server(),
+                                          origin_url_,
+                                          request_info_.privacy_mode)),
       stream_type_(HttpStreamRequest::BIDIRECTIONAL_STREAM),
       ptr_factory_(this) {
   DCHECK(session);
-  // The job can't have alternative service and alternative proxy server set at
-  // the same time since alternative services are used for requests that are
-  // fetched directly, while the alternative proxy server is used for requests
-  // that should be fetched using proxy.
-  DCHECK(alternative_service_.protocol == kProtoUnknown ||
-         !alternative_proxy_server_.is_valid());
-  DCHECK(!alternative_proxy_server_.is_valid() ||
-         !(IsSpdyAlternative() || IsQuicAlternative()));
-  // If either the alternative service protocol is specified or if the
-  // alternative proxy server is valid, then the job type must be set to
-  // either ALTERNATIVE or PRECONNECT.
-  DCHECK((alternative_service_.protocol == kProtoUnknown &&
-          !alternative_proxy_server_.is_valid()) ||
-         (job_type_ == ALTERNATIVE || job_type_ == PRECONNECT));
-  // If the alternative proxy server is valid, then the job type must be
-  // set to ALTERNATIVE.
-  DCHECK(!alternative_proxy_server_.is_valid() || job_type_ == ALTERNATIVE);
+  if (alternative_protocol != kProtoUnknown) {
+    // The job cannot have protocol requirements dictated by alternative service
+    // and have an alternative proxy server set at the same time, since
+    // alternative services are used for requests that are fetched directly,
+    // while the alternative proxy server is used for requests that should be
+    // fetched using proxy.
+    DCHECK(!alternative_proxy_server_.is_valid());
+    // If the alternative service protocol is specified, then the job type must
+    // be either ALTERNATIVE or PRECONNECT.
+    DCHECK(job_type_ == ALTERNATIVE || job_type_ == PRECONNECT);
+  }
+  // If the alternative proxy server is set, then the job must be ALTERNATIVE.
+  if (alternative_proxy_server_.is_valid()) {
+    DCHECK(job_type_ == ALTERNATIVE);
+  }
 
-  if (IsSpdyAlternative()) {
+  if (expect_spdy_) {
     DCHECK(origin_url_.SchemeIs(url::kHttpsScheme));
   }
-  if (IsQuicAlternative()) {
+  if (using_quic_) {
     DCHECK(session_->IsQuicEnabled());
-    using_quic_ = true;
   }
 }
 
@@ -396,16 +373,38 @@
   ssl_socket->GetSSLInfo(ssl_info);
 }
 
-SpdySessionKey HttpStreamFactoryImpl::Job::GetSpdySessionKey() const {
+// static
+bool HttpStreamFactoryImpl::Job::ShouldForceQuic(
+    HttpNetworkSession* session,
+    const HostPortPair& destination,
+    const GURL& origin_url,
+    const ProxyInfo& proxy_info) {
+  if (!session->IsQuicEnabled())
+    return false;
+  if (proxy_info.is_quic())
+    return true;
+  return (base::ContainsKey(session->params().origins_to_force_quic_on,
+                            HostPortPair()) ||
+          base::ContainsKey(session->params().origins_to_force_quic_on,
+                            destination)) &&
+         proxy_info.is_direct() && origin_url.SchemeIs(url::kHttpsScheme);
+}
+
+// static
+SpdySessionKey HttpStreamFactoryImpl::Job::GetSpdySessionKey(
+    bool spdy_session_direct,
+    const ProxyServer& proxy_server,
+    const GURL& origin_url,
+    PrivacyMode privacy_mode) {
   // In the case that we're using an HTTPS proxy for an HTTP url,
   // we look for a SPDY session *to* the proxy, instead of to the
   // origin server.
-  if (!spdy_session_direct_) {
-    return SpdySessionKey(proxy_info_.proxy_server().host_port_pair(),
-                          ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
+  if (!spdy_session_direct) {
+    return SpdySessionKey(proxy_server.host_port_pair(), ProxyServer::Direct(),
+                          PRIVACY_MODE_DISABLED);
   }
-  return SpdySessionKey(HostPortPair::FromURL(origin_url_),
-                        proxy_info_.proxy_server(), request_info_.privacy_mode);
+  return SpdySessionKey(HostPortPair::FromURL(origin_url), proxy_server,
+                        privacy_mode);
 }
 
 bool HttpStreamFactoryImpl::Job::CanUseExistingSpdySession() const {
@@ -459,7 +458,7 @@
 void HttpStreamFactoryImpl::Job::OnNewSpdySessionReadyCallback() {
   DCHECK(stream_.get() || bidirectional_stream_impl_.get());
   DCHECK_NE(job_type_, PRECONNECT);
-  DCHECK(using_spdy());
+  DCHECK(using_spdy_);
   // Note: an event loop iteration has passed, so |new_spdy_session_| may be
   // NULL at this point if the SpdySession closed immediately after creation.
   base::WeakPtr<SpdySession> spdy_session = new_spdy_session_;
@@ -733,7 +732,7 @@
     net_log_.BeginEvent(
         NetLogEventType::HTTP_STREAM_JOB,
         base::Bind(&NetLogHttpStreamJobCallback, net_log->source(),
-                   &request_info_.url, &origin_url_, &alternative_service_,
+                   &request_info_.url, &origin_url_, expect_spdy_, using_quic_,
                    priority_));
     net_log->AddEvent(NetLogEventType::HTTP_STREAM_REQUEST_STARTED_JOB,
                       net_log_.source().ToEventParametersCallback());
@@ -749,15 +748,6 @@
   return OK;
 }
 
-bool HttpStreamFactoryImpl::Job::ShouldForceQuic() const {
-  return session_->IsQuicEnabled() &&
-         (base::ContainsKey(session_->params().origins_to_force_quic_on,
-                            HostPortPair()) ||
-          base::ContainsKey(session_->params().origins_to_force_quic_on,
-                            destination_)) &&
-         proxy_info_.is_direct() && origin_url_.SchemeIs(url::kHttpsScheme);
-}
-
 int HttpStreamFactoryImpl::Job::DoWait() {
   next_state_ = STATE_WAIT_COMPLETE;
   bool should_wait = delegate_->ShouldWait(this);
@@ -806,18 +796,6 @@
     return OK;
   }
 
-  using_spdy_ = false;
-
-  if (ShouldForceQuic())
-    using_quic_ = true;
-
-  DCHECK(!using_quic_ || session_->IsQuicEnabled());
-
-  if (proxy_info_.is_quic()) {
-    using_quic_ = true;
-    DCHECK(session_->IsQuicEnabled());
-  }
-
   if (proxy_info_.is_https() || proxy_info_.is_quic()) {
     InitSSLConfig(&proxy_ssl_config_, /*is_proxy=*/true);
     // Disable revocation checking for HTTPS proxies since the revocation
@@ -904,8 +882,6 @@
   if (proxy_info_.is_http() || proxy_info_.is_https())
     establishing_tunnel_ = using_ssl_;
 
-  const bool expect_spdy = IsSpdyAlternative();
-
   HttpServerProperties* http_server_properties =
       session_->http_server_properties();
   if (http_server_properties) {
@@ -920,9 +896,9 @@
     DCHECK(!delegate_->for_websockets());
     return PreconnectSocketsForHttpRequest(
         GetSocketGroup(), destination_, request_info_.extra_headers,
-        request_info_.load_flags, priority_, session_, proxy_info_, expect_spdy,
-        server_ssl_config_, proxy_ssl_config_, request_info_.privacy_mode,
-        net_log_, num_streams_);
+        request_info_.load_flags, priority_, session_, proxy_info_,
+        expect_spdy_, server_ssl_config_, proxy_ssl_config_,
+        request_info_.privacy_mode, net_log_, num_streams_);
   }
 
   // If we can't use a SPDY session, don't bother checking for one after
@@ -937,15 +913,15 @@
     websocket_server_ssl_config.alpn_protos.clear();
     return InitSocketHandleForWebSocketRequest(
         GetSocketGroup(), destination_, request_info_.extra_headers,
-        request_info_.load_flags, priority_, session_, proxy_info_, expect_spdy,
-        websocket_server_ssl_config, proxy_ssl_config_,
+        request_info_.load_flags, priority_, session_, proxy_info_,
+        expect_spdy_, websocket_server_ssl_config, proxy_ssl_config_,
         request_info_.privacy_mode, net_log_, connection_.get(),
         resolution_callback, io_callback_);
   }
 
   return InitSocketHandleForHttpRequest(
       GetSocketGroup(), destination_, request_info_.extra_headers,
-      request_info_.load_flags, priority_, session_, proxy_info_, expect_spdy,
+      request_info_.load_flags, priority_, session_, proxy_info_, expect_spdy_,
       server_ssl_config_, proxy_ssl_config_, request_info_.privacy_mode,
       net_log_, connection_.get(), resolution_callback, io_callback_);
 }
@@ -1030,16 +1006,13 @@
     return result;
   }
 
-  if (proxy_info_.is_quic() && using_quic_ && result < 0) {
-    using_quic_ = false;
+  if (proxy_info_.is_quic() && using_quic_ && result < 0)
     return ReconsiderProxyAfterError(result);
-  }
 
-  if (IsSpdyAlternative() && !using_spdy_)
+  if (expect_spdy_ && !using_spdy_)
     return ERR_ALPN_NEGOTIATION_FAILED;
 
-  if (!ssl_started && result < 0 &&
-      (IsSpdyAlternative() || IsQuicAlternative()))
+  if (!ssl_started && result < 0 && (expect_spdy_ || using_quic_))
     return result;
 
   if (using_quic_) {
@@ -1126,7 +1099,7 @@
       FROM_HERE_WITH_EXPLICIT_FUNCTION(
           "462811 HttpStreamFactoryImpl::Job::DoCreateStream"));
   DCHECK(connection_->socket() || existing_spdy_session_.get() || using_quic_);
-  DCHECK(!IsQuicAlternative());
+  DCHECK(!using_quic_);
 
   next_state_ = STATE_CREATE_STREAM_COMPLETE;
 
@@ -1144,7 +1117,7 @@
     SetSocketMotivation();
 
   if (!using_spdy_) {
-    DCHECK(!IsSpdyAlternative());
+    DCHECK(!expect_spdy_);
     // We may get ftp scheme when fetching ftp resources through proxy.
     bool using_proxy = (proxy_info_.is_http() || proxy_info_.is_https()) &&
                        (request_info_.url.SchemeIs(url::kHttpScheme) ||
@@ -1272,14 +1245,6 @@
   // TODO(mbelshe): Add other motivations (like EARLY_LOAD_MOTIVATED).
 }
 
-bool HttpStreamFactoryImpl::Job::IsSpdyAlternative() const {
-  return alternative_service_.protocol == kProtoHTTP2;
-}
-
-bool HttpStreamFactoryImpl::Job::IsQuicAlternative() const {
-  return alternative_service_.protocol == kProtoQUIC;
-}
-
 void HttpStreamFactoryImpl::Job::InitSSLConfig(SSLConfig* ssl_config,
                                                bool is_proxy) const {
   if (!is_proxy) {
@@ -1446,7 +1411,7 @@
   return base::MakeUnique<HttpStreamFactoryImpl::Job>(
       delegate, job_type, session, request_info, priority, proxy_info,
       server_ssl_config, proxy_ssl_config, destination, origin_url,
-      enable_ip_based_pooling, net_log);
+      kProtoUnknown, ProxyServer(), enable_ip_based_pooling, net_log);
 }
 
 std::unique_ptr<HttpStreamFactoryImpl::Job>
@@ -1461,13 +1426,13 @@
     const SSLConfig& proxy_ssl_config,
     HostPortPair destination,
     GURL origin_url,
-    AlternativeService alternative_service,
+    NextProto alternative_protocol,
     bool enable_ip_based_pooling,
     NetLog* net_log) {
   return base::MakeUnique<HttpStreamFactoryImpl::Job>(
       delegate, job_type, session, request_info, priority, proxy_info,
       server_ssl_config, proxy_ssl_config, destination, origin_url,
-      alternative_service, ProxyServer(), enable_ip_based_pooling, net_log);
+      alternative_protocol, ProxyServer(), enable_ip_based_pooling, net_log);
 }
 
 std::unique_ptr<HttpStreamFactoryImpl::Job>
@@ -1488,7 +1453,7 @@
   return base::MakeUnique<HttpStreamFactoryImpl::Job>(
       delegate, job_type, session, request_info, priority, proxy_info,
       server_ssl_config, proxy_ssl_config, destination, origin_url,
-      AlternativeService(), alternative_proxy_server, enable_ip_based_pooling,
+      kProtoUnknown, alternative_proxy_server, enable_ip_based_pooling,
       net_log);
 }
 
diff --git a/net/http/http_stream_factory_impl_job.h b/net/http/http_stream_factory_impl_job.h
index 91c3def5..80b9e52 100644
--- a/net/http/http_stream_factory_impl_job.h
+++ b/net/http/http_stream_factory_impl_job.h
@@ -145,9 +145,29 @@
     virtual bool for_websockets() = 0;
   };
 
-  // Constructor for non-alternative Job.
-  // Job is owned by |delegate|, hence |delegate| is valid for the
-  // lifetime of the Job.
+  // Job is owned by |delegate|, hence |delegate| is valid for the lifetime of
+  // the Job.
+  //
+  // |alternative_protocol| is the protocol required by Alternative Service, if
+  // any:
+  // * |alternative_protocol == kProtoUnknown| means that the Job can pool to an
+  //   existing SpdySession, or bind to a idle TCP socket that might use either
+  //   HTTP/1.1 or HTTP/2.
+  // * |alternative_protocol == kProtoHTTP2| means that the Job can pool to an
+  //   existing SpdySession, or bind to a idle TCP socket.  In the latter case,
+  //   if the socket does not use HTTP/2, then the Job fails.
+  // * |alternative_protocol == kProtoQUIC| means that the Job can pool to an
+  //   existing QUIC connection or open a new one.
+  // Note that this can be overwritten by specifying a QUIC proxy in
+  // |proxy_info|, or by setting
+  // HttpNetworkSession::Params::origins_to_force_quic_on.
+  //
+  // If |alternative_proxy_server| is a valid proxy server, then the Job will
+  // use that instead of using ProxyService for proxy resolution.  Further, if
+  // |alternative_proxy_server| is a valid but bad proxy, then fallback proxies
+  // are not used. It is illegal to call this constructor with a valid
+  // |alternative_proxy_server| and an |alternate_protocol| different from
+  // kProtoUnknown.
   Job(Delegate* delegate,
       JobType job_type,
       HttpNetworkSession* session,
@@ -158,28 +178,7 @@
       const SSLConfig& proxy_ssl_config,
       HostPortPair destination,
       GURL origin_url,
-      bool enable_ip_based_pooling,
-      NetLog* net_log);
-
-  // Constructor for the alternative Job. The Job is owned by |delegate|, hence
-  // |delegate| is valid for the lifetime of the Job. If |alternative_service|
-  // is initialized, then the Job will use the alternative service. On the
-  // other hand, if |alternative_proxy_server| is a valid proxy server, then the
-  // job will use that instead of using ProxyService for proxy resolution.
-  // Further, if |alternative_proxy_server| is a valid but bad proxy, then
-  // fallback proxies are not used. It is illegal to call this with an
-  // initialized |alternative_service|, and a valid |alternative_proxy_server|.
-  Job(Delegate* delegate,
-      JobType job_type,
-      HttpNetworkSession* session,
-      const HttpRequestInfo& request_info,
-      RequestPriority priority,
-      const ProxyInfo& proxy_info,
-      const SSLConfig& server_ssl_config,
-      const SSLConfig& proxy_ssl_config,
-      HostPortPair destination,
-      GURL origin_url,
-      AlternativeService alternative_service,
+      NextProto alternative_protocol,
       const ProxyServer& alternative_proxy_server,
       bool enable_ip_based_pooling,
       NetLog* net_log);
@@ -231,10 +230,6 @@
 
   JobType job_type() const { return job_type_; }
 
-  const AlternativeService alternative_service() const {
-    return alternative_service_;
-  }
-
   const ProxyServer alternative_proxy_server() const {
     return alternative_proxy_server_;
   }
@@ -334,10 +329,6 @@
   // Set the motivation for this request onto the underlying socket.
   void SetSocketMotivation();
 
-  // Is this a SPDY or QUIC alternative Job?
-  bool IsSpdyAlternative() const;
-  bool IsQuicAlternative() const;
-
   // Sets several fields of |ssl_config| based on the proxy info and other
   // factors.
   void InitSSLConfig(SSLConfig* ssl_config, bool is_proxy) const;
@@ -346,8 +337,17 @@
   // This must only be called when we are using an SSLSocket.
   void GetSSLInfo(SSLInfo* ssl_info);
 
+  // Called in Job constructor: should Job be forced to use QUIC.
+  static bool ShouldForceQuic(HttpNetworkSession* session,
+                              const HostPortPair& destination,
+                              const GURL& origin_url,
+                              const ProxyInfo& proxy_info);
+
   // Called in Job constructor. Use |spdy_session_key_| after construction.
-  SpdySessionKey GetSpdySessionKey() const;
+  static SpdySessionKey GetSpdySessionKey(bool spdy_session_direct,
+                                          const ProxyServer& proxy_server,
+                                          const GURL& origin_url,
+                                          PrivacyMode privacy_mode);
 
   // Returns true if the current request can use an existing spdy session.
   bool CanUseExistingSpdySession() const;
@@ -368,9 +368,6 @@
   // Called to handle a client certificate request.
   int HandleCertificateRequest(int error);
 
-  // Should we force QUIC for this stream request.
-  bool ShouldForceQuic() const;
-
   ClientSocketPoolManager::SocketGroupType GetSocketGroup() const;
 
   void MaybeCopyConnectionAttemptsFromSocketOrHandle();
@@ -413,9 +410,6 @@
   // original request when host mapping rules are set-up.
   const GURL origin_url_;
 
-  // AlternativeService for this Job if this is an alternative Job.
-  const AlternativeService alternative_service_;
-
   // Alternative proxy server that should be used by |this| to fetch the
   // request.
   const ProxyServer alternative_proxy_server_;
@@ -432,11 +426,16 @@
   // True if handling a HTTPS request.
   const bool using_ssl_;
 
-  // True if this network transaction is using SPDY instead of HTTP.
-  bool using_spdy_;
+  // True if Job uses QUIC.
+  const bool using_quic_;
 
-  // True if this network transaction is using QUIC instead of HTTP.
-  bool using_quic_;
+  // True if Alternative Service protocol field requires that HTTP/2 is used.
+  // In this case, Job fails if it cannot pool to an existing SpdySession and
+  // the server does not negotiate HTTP/2 on a new socket.
+  const bool expect_spdy_;
+
+  // True if Job actually uses HTTP/2.
+  bool using_spdy_;
 
   // True if this job might succeed with a different proxy config.
   bool should_reconsider_proxy_;
@@ -517,7 +516,7 @@
       const SSLConfig& proxy_ssl_config,
       HostPortPair destination,
       GURL origin_url,
-      AlternativeService alternative_service,
+      NextProto alternative_protocol,
       bool enable_ip_based_pooling,
       NetLog* net_log);
 
diff --git a/net/http/http_stream_factory_impl_job_controller.cc b/net/http/http_stream_factory_impl_job_controller.cc
index 69209d4..a3134b8 100644
--- a/net/http/http_stream_factory_impl_job_controller.cc
+++ b/net/http/http_stream_factory_impl_job_controller.cc
@@ -769,7 +769,7 @@
   GURL origin_url = ApplyHostMappingRules(request_info_.url, &destination);
 
   // Create an alternative job if alternative service is set up for this domain.
-  const AlternativeService alternative_service =
+  alternative_service_ =
       GetAlternativeServiceInfoFor(request_info_, delegate_, stream_type_)
           .alternative_service();
 
@@ -778,15 +778,15 @@
     // priority currently makes sense for preconnects. The priority for
     // preconnects is currently ignored (see RequestSocketsForPool()), but could
     // be used at some point for proxy resolution or something.
-    if (alternative_service.protocol != kProtoUnknown) {
+    if (alternative_service_.protocol != kProtoUnknown) {
       HostPortPair alternative_destination(
-          alternative_service.host_port_pair());
+          alternative_service_.host_port_pair());
       ignore_result(
           ApplyHostMappingRules(request_info_.url, &alternative_destination));
       main_job_ = job_factory_->CreateAltSvcJob(
           this, PRECONNECT, session_, request_info_, IDLE, proxy_info_,
           server_ssl_config_, proxy_ssl_config_, alternative_destination,
-          origin_url, alternative_service, enable_ip_based_pooling_,
+          origin_url, alternative_service_.protocol, enable_ip_based_pooling_,
           session_->net_log());
     } else {
       main_job_ = job_factory_->CreateMainJob(
@@ -803,21 +803,22 @@
       enable_ip_based_pooling_, net_log_.net_log());
   // Alternative Service can only be set for HTTPS requests while Alternative
   // Proxy is set for HTTP requests.
-  if (alternative_service.protocol != kProtoUnknown) {
+  if (alternative_service_.protocol != kProtoUnknown) {
     // Never share connection with other jobs for FTP requests.
     DVLOG(1) << "Selected alternative service (host: "
-             << alternative_service.host_port_pair().host()
-             << " port: " << alternative_service.host_port_pair().port() << ")";
+             << alternative_service_.host_port_pair().host()
+             << " port: " << alternative_service_.host_port_pair().port()
+             << ")";
 
     DCHECK(!request_info_.url.SchemeIs(url::kFtpScheme));
-    HostPortPair alternative_destination(alternative_service.host_port_pair());
+    HostPortPair alternative_destination(alternative_service_.host_port_pair());
     ignore_result(
         ApplyHostMappingRules(request_info_.url, &alternative_destination));
 
     alternative_job_ = job_factory_->CreateAltSvcJob(
         this, ALTERNATIVE, session_, request_info_, priority_, proxy_info_,
         server_ssl_config_, proxy_ssl_config_, alternative_destination,
-        origin_url, alternative_service, enable_ip_based_pooling_,
+        origin_url, alternative_service_.protocol, enable_ip_based_pooling_,
         net_log_.net_log());
 
     main_job_is_blocked_ = true;
@@ -945,10 +946,9 @@
     int net_error) {
   DCHECK_EQ(alternative_job_->job_type(), ALTERNATIVE);
   DCHECK_NE(OK, net_error);
-  DCHECK_NE(kProtoUnknown, alternative_job_->alternative_service().protocol);
+  DCHECK_NE(kProtoUnknown, alternative_service_.protocol);
 
   alternative_job_net_error_ = net_error;
-  failed_alternative_service_ = alternative_job_->alternative_service();
 
   if (IsJobOrphaned(alternative_job_.get())) {
     // If |request_| is gone then it must have been successfully served by
@@ -974,7 +974,7 @@
 }
 
 void HttpStreamFactoryImpl::JobController::ReportBrokenAlternativeService() {
-  DCHECK(failed_alternative_service_.protocol != kProtoUnknown);
+  DCHECK(alternative_service_.protocol != kProtoUnknown);
   DCHECK_NE(OK, alternative_job_net_error_);
 
   int error_to_report = alternative_job_net_error_;
@@ -990,7 +990,7 @@
   HistogramBrokenAlternateProtocolLocation(
       BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_ALT);
   session_->http_server_properties()->MarkAlternativeServiceBroken(
-      failed_alternative_service_);
+      alternative_service_);
 }
 
 void HttpStreamFactoryImpl::JobController::MaybeNotifyFactoryOfCompletion() {
diff --git a/net/http/http_stream_factory_impl_job_controller.h b/net/http/http_stream_factory_impl_job_controller.h
index a5ccf46..9b29742 100644
--- a/net/http/http_stream_factory_impl_job_controller.h
+++ b/net/http/http_stream_factory_impl_job_controller.h
@@ -11,6 +11,7 @@
 #include "net/base/privacy_mode.h"
 #include "net/http/http_stream_factory_impl_job.h"
 #include "net/http/http_stream_factory_impl_request.h"
+#include "net/socket/next_proto.h"
 
 namespace net {
 
@@ -312,11 +313,12 @@
   // |main_job_| to proceed and then race the two jobs.
   std::unique_ptr<Job> main_job_;
   std::unique_ptr<Job> alternative_job_;
+  // The alternative service used by |alternative_job_|
+  // (or by |main_job_| if |is_preconnect_|.)
+  AlternativeService alternative_service_;
 
   // Net error code of the failed alternative job. Set to OK by default.
   int alternative_job_net_error_;
-  // The alternative service server that |alternative_job_| uses failed.
-  AlternativeService failed_alternative_service_;
 
   // True if a Job has ever been bound to the |request_|.
   bool job_bound_;
diff --git a/net/http/http_stream_factory_test_util.cc b/net/http/http_stream_factory_test_util.cc
index 2c8fc625..0f5371d 100644
--- a/net/http/http_stream_factory_test_util.cc
+++ b/net/http/http_stream_factory_test_util.cc
@@ -26,35 +26,7 @@
     const SSLConfig& proxy_ssl_config,
     HostPortPair destination,
     GURL origin_url,
-    bool enable_ip_based_pooling,
-    NetLog* net_log)
-    : HttpStreamFactoryImpl::Job(delegate,
-                                 job_type,
-                                 session,
-                                 request_info,
-                                 priority,
-                                 proxy_info,
-                                 server_ssl_config,
-                                 proxy_ssl_config,
-                                 destination,
-                                 origin_url,
-                                 enable_ip_based_pooling,
-                                 net_log) {
-  DCHECK(!is_waiting());
-}
-
-MockHttpStreamFactoryImplJob::MockHttpStreamFactoryImplJob(
-    HttpStreamFactoryImpl::Job::Delegate* delegate,
-    HttpStreamFactoryImpl::JobType job_type,
-    HttpNetworkSession* session,
-    const HttpRequestInfo& request_info,
-    RequestPriority priority,
-    ProxyInfo proxy_info,
-    const SSLConfig& server_ssl_config,
-    const SSLConfig& proxy_ssl_config,
-    HostPortPair destination,
-    GURL origin_url,
-    AlternativeService alternative_service,
+    NextProto alternative_protocol,
     const ProxyServer& alternative_proxy_server,
     bool enable_ip_based_pooling,
     NetLog* net_log)
@@ -68,10 +40,12 @@
                                  proxy_ssl_config,
                                  destination,
                                  origin_url,
-                                 alternative_service,
+                                 alternative_protocol,
                                  alternative_proxy_server,
                                  enable_ip_based_pooling,
-                                 net_log) {}
+                                 net_log) {
+  DCHECK(!is_waiting());
+}
 
 MockHttpStreamFactoryImplJob::~MockHttpStreamFactoryImplJob() {}
 
@@ -100,8 +74,8 @@
 
   auto main_job = base::MakeUnique<MockHttpStreamFactoryImplJob>(
       delegate, job_type, session, request_info, priority, proxy_info,
-      SSLConfig(), SSLConfig(), destination, origin_url,
-      enable_ip_based_pooling, nullptr);
+      SSLConfig(), SSLConfig(), destination, origin_url, kProtoUnknown,
+      ProxyServer(), enable_ip_based_pooling, nullptr);
 
   // Keep raw pointer to Job but pass ownership.
   main_job_ = main_job.get();
@@ -120,12 +94,12 @@
     const SSLConfig& proxy_ssl_config,
     HostPortPair destination,
     GURL origin_url,
-    AlternativeService alternative_service,
+    NextProto alternative_protocol,
     bool enable_ip_based_pooling,
     NetLog* net_log) {
   auto alternative_job = base::MakeUnique<MockHttpStreamFactoryImplJob>(
       delegate, job_type, session, request_info, priority, proxy_info,
-      SSLConfig(), SSLConfig(), destination, origin_url, alternative_service,
+      SSLConfig(), SSLConfig(), destination, origin_url, alternative_protocol,
       ProxyServer(), enable_ip_based_pooling, nullptr);
 
   // Keep raw pointer to Job but pass ownership.
@@ -150,7 +124,7 @@
     NetLog* net_log) {
   auto alternative_job = base::MakeUnique<MockHttpStreamFactoryImplJob>(
       delegate, job_type, session, request_info, priority, proxy_info,
-      SSLConfig(), SSLConfig(), destination, origin_url, AlternativeService(),
+      SSLConfig(), SSLConfig(), destination, origin_url, kProtoUnknown,
       alternative_proxy_server, enable_ip_based_pooling, nullptr);
 
   // Keep raw pointer to Job but pass ownership.
diff --git a/net/http/http_stream_factory_test_util.h b/net/http/http_stream_factory_test_util.h
index 461ee2b5..49a3d96 100644
--- a/net/http/http_stream_factory_test_util.h
+++ b/net/http/http_stream_factory_test_util.h
@@ -15,6 +15,7 @@
 #include "net/http/http_stream_factory_impl_job_controller.h"
 #include "net/proxy/proxy_info.h"
 #include "net/proxy/proxy_server.h"
+#include "net/socket/next_proto.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 using testing::_;
@@ -114,20 +115,7 @@
                                const SSLConfig& proxy_ssl_config,
                                HostPortPair destination,
                                GURL origin_url,
-                               bool enable_ip_based_pooling,
-                               NetLog* net_log);
-
-  MockHttpStreamFactoryImplJob(HttpStreamFactoryImpl::Job::Delegate* delegate,
-                               HttpStreamFactoryImpl::JobType job_type,
-                               HttpNetworkSession* session,
-                               const HttpRequestInfo& request_info,
-                               RequestPriority priority,
-                               ProxyInfo proxy_info,
-                               const SSLConfig& server_ssl_config,
-                               const SSLConfig& proxy_ssl_config,
-                               HostPortPair destination,
-                               GURL origin_url,
-                               AlternativeService alternative_service,
+                               NextProto alternative_protocol,
                                const ProxyServer& alternative_proxy_server,
                                bool enable_ip_based_pooling,
                                NetLog* net_log);
@@ -170,7 +158,7 @@
       const SSLConfig& proxy_ssl_config,
       HostPortPair destination,
       GURL origin_url,
-      AlternativeService alternative_service,
+      NextProto alternative_protocol,
       bool enable_ip_based_pooling,
       NetLog* net_log) override;
 
diff --git a/net/nqe/network_quality_estimator_test_util.cc b/net/nqe/network_quality_estimator_test_util.cc
index 81372b4..84cc856 100644
--- a/net/nqe/network_quality_estimator_test_util.cc
+++ b/net/nqe/network_quality_estimator_test_util.cc
@@ -183,6 +183,13 @@
   return NetworkQualityEstimator::GetRecentTransportRTT(start_time, rtt);
 }
 
+base::Optional<base::TimeDelta> TestNetworkQualityEstimator::GetTransportRTT()
+    const {
+  if (start_time_null_transport_rtt_)
+    return start_time_null_transport_rtt_;
+  return NetworkQualityEstimator::GetTransportRTT();
+}
+
 bool TestNetworkQualityEstimator::GetRecentDownlinkThroughputKbps(
     const base::TimeTicks& start_time,
     int32_t* kbps) const {
diff --git a/net/nqe/network_quality_estimator_test_util.h b/net/nqe/network_quality_estimator_test_util.h
index 4b12fce..4549c92 100644
--- a/net/nqe/network_quality_estimator_test_util.h
+++ b/net/nqe/network_quality_estimator_test_util.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/files/file_path.h"
@@ -155,6 +156,9 @@
     DCHECK(!effective_connection_type_ && !recent_effective_connection_type_);
     recent_transport_rtt_ = recent_transport_rtt;
   }
+
+  base::Optional<base::TimeDelta> GetTransportRTT() const override;
+
   // Returns the recent transport RTT that was set using
   // |set_recent_transport_rtt|. If the recent transport RTT has not been set,
   // then the base implementation is called.
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 2b362ec..a9141a1 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1375,7 +1375,10 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled",
+                    "name": "EnableAppNap",
+                    "params": {
+                        "app_nap": "true"
+                    },
                     "enable_features": [
                         "MacAllowBackgroundingProcesses"
                     ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 1651fe30..92302c0c 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -17582,20 +17582,20 @@
 crbug.com/591099 inspector/elements/styles/url-multiple-collapsing.html [ Crash ]
 crbug.com/591099 inspector/elements/user-properties.html [ Crash ]
 crbug.com/591099 inspector/evaluate-in-page.html [ Failure ]
-crbug.com/591099 inspector/extensions/extensions-api.html [ Failure ]
-crbug.com/591099 inspector/extensions/extensions-audits-api.html [ Crash Failure ]
-crbug.com/591099 inspector/extensions/extensions-audits-content-script.html [ Crash Failure ]
-crbug.com/591099 inspector/extensions/extensions-audits.html [ Crash Failure ]
-crbug.com/591099 inspector/extensions/extensions-eval-content-script.html [ Failure ]
-crbug.com/591099 inspector/extensions/extensions-eval.html [ Crash Failure ]
-crbug.com/591099 inspector/extensions/extensions-events.html [ Crash ]
-crbug.com/591099 inspector/extensions/extensions-network.html [ Crash Failure ]
-crbug.com/591099 inspector/extensions/extensions-panel.html [ Crash ]
-crbug.com/591099 inspector/extensions/extensions-reload.html [ Failure ]
-crbug.com/591099 inspector/extensions/extensions-resources.html [ Crash Failure ]
-crbug.com/591099 inspector/extensions/extensions-sidebar.html [ Crash ]
-crbug.com/591099 inspector/extensions/extensions-timeline-api.html [ Crash ]
-crbug.com/591099 inspector/extensions/multiple-extensions.html [ Failure ]
+crbug.com/591099 http/tests/inspector/extensions/extensions-api.html [ Failure ]
+crbug.com/591099 http/tests/inspector/extensions/extensions-audits-api.html [ Crash Failure ]
+crbug.com/591099 http/tests/inspector/extensions/extensions-audits-content-script.html [ Crash Failure ]
+crbug.com/591099 http/tests/inspector/extensions/extensions-audits.html [ Crash Failure ]
+crbug.com/591099 http/tests/inspector/extensions/extensions-eval-content-script.html [ Failure ]
+crbug.com/591099 http/tests/inspector/extensions/extensions-eval.html [ Crash Failure ]
+crbug.com/591099 http/tests/inspector/extensions/extensions-events.html [ Crash ]
+crbug.com/591099 http/tests/inspector/extensions/extensions-network.html [ Crash Failure ]
+crbug.com/591099 http/tests/inspector/extensions/extensions-panel.html [ Crash ]
+crbug.com/591099 http/tests/inspector/extensions/extensions-reload.html [ Failure ]
+crbug.com/591099 http/tests/inspector/extensions/extensions-resources.html [ Crash Failure ]
+crbug.com/591099 http/tests/inspector/extensions/extensions-sidebar.html [ Crash ]
+crbug.com/591099 http/tests/inspector/extensions/extensions-timeline-api.html [ Crash ]
+crbug.com/591099 http/tests/inspector/extensions/multiple-extensions.html [ Failure ]
 crbug.com/591099 inspector/file-reader-with-network-panel.html [ Failure ]
 crbug.com/591099 inspector/file-system-mapping.html [ Failure ]
 crbug.com/591099 inspector/file-system-project.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service b/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
index ccb1b75..5b8a7ce 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
@@ -281,7 +281,7 @@
 Bug(none) external/wpt/cssom-view/scrollingElement.html [ Failure Timeout ]
 Bug(none) external/wpt/custom-elements/custom-element-registry/per-global.html [ Failure Timeout ]
 Bug(none) external/wpt/dom/nodes/Document-URL.sub.html [ Failure Timeout ]
-Bug(none) external/wpt/dom/nodes/Document-characterSet-normalization.html [ Failure Timeout ]
+Bug(none) external/wpt/dom/nodes/Document-characterSet-normalization.html [ Crash Failure Timeout ]
 Bug(none) external/wpt/dom/nodes/Document-createElement-namespace.html [ Failure Timeout ]
 Bug(none) external/wpt/dom/nodes/Element-remove.html [ Failure Timeout ]
 Bug(none) external/wpt/domxpath/xml_xpath_runner.html [ Failure Timeout ]
@@ -1048,39 +1048,119 @@
 Bug(none) external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-https/xhr-request/generic.keep-origin-redirect.http.html [ Failure Timeout ]
 Bug(none) external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-https/xhr-request/generic.no-redirect.http.html [ Failure Timeout ]
 Bug(none) external/wpt/referrer-policy/origin/meta-referrer/same-origin/http-https/xhr-request/generic.swap-origin-redirect.http.html [ Failure Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/attr-referrer/cross-origin/http-https/iframe-tag/cross-origin.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/attr-referrer/cross-origin/http-https/iframe-tag/cross-origin.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/attr-referrer/cross-origin/http-https/img-tag/cross-origin.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/attr-referrer/cross-origin/http-https/img-tag/cross-origin.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/attr-referrer/same-origin/http-https/iframe-tag/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/attr-referrer/same-origin/http-https/img-tag/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/fetch-request/cross-origin.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/fetch-request/cross-origin.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/iframe-tag/cross-origin.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/img-tag/cross-origin.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/img-tag/cross-origin.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/script-tag/cross-origin.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/script-tag/cross-origin.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/http-rp/cross-origin/http-https/xhr-request/cross-origin.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-https/fetch-request/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-https/img-tag/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/http-rp/same-origin/http-https/script-tag/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/fetch-request/cross-origin.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/iframe-tag/cross-origin.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/img-tag/cross-origin.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/img-tag/cross-origin.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/script-tag/cross-origin.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/cross-origin/http-https/script-tag/cross-origin.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-https/fetch-request/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-https/iframe-tag/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/same-origin/meta-referrer/same-origin/http-https/img-tag/same-origin-insecure.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/cross-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/cross-origin/http-https/img-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/same-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/same-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/fetch-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/fetch-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/fetch-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/iframe-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/iframe-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/img-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/script-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/xhr-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/xhr-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/cross-origin/http-https/xhr-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/fetch-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/img-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/script-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/script-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/xhr-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/http-rp/same-origin/http-https/xhr-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/fetch-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/img-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/script-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/script-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/xhr-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/cross-origin/http-https/xhr-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/fetch-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/fetch-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/img-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/script-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/script-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/script-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/xhr-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin-when-cross-origin/meta-referrer/same-origin/http-https/xhr-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/cross-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/cross-origin/http-https/img-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/cross-origin/http-https/img-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/same-origin/http-https/iframe-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/same-origin/http-https/iframe-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/same-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/attr-referrer/same-origin/http-https/img-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/fetch-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/iframe-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/img-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/img-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/script-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/xhr-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/cross-origin/http-https/xhr-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/fetch-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/fetch-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/script-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/http-rp/same-origin/http-https/xhr-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/fetch-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/iframe-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/img-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/img-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/img-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/script-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/script-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/script-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/cross-origin/http-https/xhr-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/iframe-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/img-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/img-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/script-tag/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/script-tag/upgrade-protocol.no-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/script-tag/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/xhr-request/upgrade-protocol.keep-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/xhr-request/upgrade-protocol.no-redirect.http.html [ Timeout ]
+Bug(none) external/wpt/referrer-policy/strict-origin/meta-referrer/same-origin/http-https/xhr-request/upgrade-protocol.swap-origin-redirect.http.html [ Timeout ]
 Bug(none) external/wpt/referrer-policy/unsafe-url/attr-referrer/cross-origin/http-https/iframe-tag/generic.keep-origin-redirect.http.html [ Failure Timeout ]
 Bug(none) external/wpt/referrer-policy/unsafe-url/attr-referrer/cross-origin/http-https/iframe-tag/generic.no-redirect.http.html [ Failure Timeout ]
 Bug(none) external/wpt/referrer-policy/unsafe-url/attr-referrer/cross-origin/http-https/iframe-tag/generic.swap-origin-redirect.http.html [ Failure Timeout ]
@@ -1586,7 +1666,7 @@
 Bug(none) http/tests/appcache/offline-access.html [ Timeout ]
 Bug(none) http/tests/appcache/online-fallback-layering.html [ Timeout ]
 Bug(none) http/tests/appcache/online-whitelist.html [ Failure ]
-Bug(none) http/tests/appcache/reload.html [ Failure ]
+Bug(none) http/tests/appcache/reload.html [ Failure Timeout ]
 Bug(none) http/tests/appcache/remove-cache.html [ Timeout ]
 Bug(none) http/tests/appcache/simple.html [ Timeout ]
 Bug(none) http/tests/appcache/top-frame-1.html [ Timeout ]
@@ -1663,7 +1743,7 @@
 Bug(none) http/tests/csspaint/registerPaint.html [ Timeout ]
 Bug(none) http/tests/csspaint/registered-properties-in-custom-paint.html [ Timeout ]
 Bug(none) http/tests/csspaint/style-background-image.html [ Timeout ]
-Bug(none) http/tests/csspaint/style-before-pseudo.html [ Timeout ]
+Bug(none) http/tests/csspaint/style-before-pseudo.html [ Crash Pass Timeout ]
 Bug(none) http/tests/csspaint/style-first-letter-pseudo.html [ Timeout ]
 Bug(none) http/tests/dom/create-contextual-fragment-from-bodyless-svg-document-range.html [ Timeout ]
 Bug(none) http/tests/download/basic-ascii.html [ Crash ]
@@ -1934,6 +2014,16 @@
 Bug(none) http/tests/inspector [ Crash Failure Timeout ]
 Bug(none) http/tests/inspector-enabled [ Failure Timeout ]
 Bug(none) http/tests/inspector-protocol [ Failure Timeout ]
+Bug(none) http/tests/inspector/extensions/extensions-api.html [ Timeout ]
+Bug(none) http/tests/inspector/extensions/extensions-audits-api.html [ Timeout ]
+Bug(none) http/tests/inspector/extensions/extensions-audits-content-script.html [ Timeout ]
+Bug(none) http/tests/inspector/extensions/extensions-audits.html [ Timeout ]
+Bug(none) http/tests/inspector/extensions/extensions-events.html [ Timeout ]
+Bug(none) http/tests/inspector/extensions/extensions-network.html [ Timeout ]
+Bug(none) http/tests/inspector/extensions/extensions-reload.html [ Timeout ]
+Bug(none) http/tests/inspector/extensions/extensions-resources.html [ Timeout ]
+Bug(none) http/tests/inspector/extensions/extensions-timeline-api.html [ Timeout ]
+Bug(none) http/tests/inspector/extensions/multiple-extensions.html [ Timeout ]
 Bug(none) http/tests/linkHeader/link-preconnect-schemeless.https.php [ Timeout ]
 Bug(none) http/tests/loading/307-after-303-after-post.html [ Failure ]
 Bug(none) http/tests/loading/bad-scheme-subframe.html [ Failure ]
@@ -2489,16 +2579,6 @@
 Bug(none) inspector/elements/styles/cancel-upon-invalid-property.html [ Timeout ]
 Bug(none) inspector/elements/styles/original-content-provider.html [ Timeout ]
 Bug(none) inspector/elements/styles/url-multiple-collapsing.html [ Timeout ]
-Bug(none) inspector/extensions/extensions-api.html [ Timeout ]
-Bug(none) inspector/extensions/extensions-audits-api.html [ Timeout ]
-Bug(none) inspector/extensions/extensions-audits-content-script.html [ Timeout ]
-Bug(none) inspector/extensions/extensions-audits.html [ Timeout ]
-Bug(none) inspector/extensions/extensions-events.html [ Timeout ]
-Bug(none) inspector/extensions/extensions-network.html [ Timeout ]
-Bug(none) inspector/extensions/extensions-reload.html [ Timeout ]
-Bug(none) inspector/extensions/extensions-resources.html [ Timeout ]
-Bug(none) inspector/extensions/extensions-timeline-api.html [ Timeout ]
-Bug(none) inspector/extensions/multiple-extensions.html [ Timeout ]
 Bug(none) inspector/file-system-mapping.html [ Timeout ]
 Bug(none) inspector/file-system-project.html [ Failure ]
 Bug(none) inspector/geolocation-emulation-tests.html [ Timeout ]
diff --git a/third_party/WebKit/LayoutTests/SlowTests b/third_party/WebKit/LayoutTests/SlowTests
index 0f02144..99808d1 100644
--- a/third_party/WebKit/LayoutTests/SlowTests
+++ b/third_party/WebKit/LayoutTests/SlowTests
@@ -98,9 +98,9 @@
 crbug.com/451577 [ Debug ] fast/dom/gc-treescope.html  [ Slow ]
 crbug.com/451577 [ Debug ] fast/frames/calculate-round.html [ Slow ]
 crbug.com/451577 external/wpt/IndexedDB/idbindex-multientry-big.htm [ Slow ]
-crbug.com/451577 [ Mac ] inspector/extensions/extensions-reload.html [ Slow ]
-crbug.com/451577 [ Mac ] inspector/extensions/extensions-resources.html [ Slow ]
-crbug.com/451577 [ Win10 ] inspector/extensions/extensions-sidebar.html [ Slow ]
+crbug.com/451577 [ Mac ] http/tests/inspector/extensions/extensions-reload.html [ Slow ]
+crbug.com/451577 [ Mac ] http/tests/inspector/extensions/extensions-resources.html [ Slow ]
+crbug.com/451577 [ Win10 ] http/tests/inspector/extensions/extensions-sidebar.html [ Slow ]
 crbug.com/451577 [ Mac ] inspector/layers/layer-canvas-log.html [ Slow ]
 crbug.com/451577 [ Mac ] inspector/network/network-domain-filter.html [ Slow ]
 
@@ -129,8 +129,8 @@
 crbug.com/420008 virtual/threaded/inspector/tracing/ [ Slow ]
 crbug.com/246190 [ Release ] http/tests/inspector/indexeddb/ [ Slow ]
 crbug.com/246190 [ Release ] virtual/mojo-loading/http/tests/inspector/indexeddb/ [ Slow ]
-crbug.com/451577 [ Mac ] inspector/extensions/extensions-sidebar.html [ Slow ]
-crbug.com/451577 [ Mac ] inspector/extensions/extensions-events.html [ Slow ]
+crbug.com/451577 [ Mac ] http/tests/inspector/extensions/extensions-sidebar.html [ Slow ]
+crbug.com/451577 [ Mac ] http/tests/inspector/extensions/extensions-events.html [ Slow ]
 
 # Random slow tests
 webkit.org/b/90488 [ Release ] http/tests/inspector/compiler-source-mapping-debug.html [ Slow ]
@@ -221,7 +221,7 @@
 crbug.com/241576 [ Win ] virtual/mojo-loading/http/tests/appcache/404-manifest.html [ Slow ]
 crbug.com/241869 [ Debug ] css3/flexbox/multiline-justify-content.html [ Slow ]
 crbug.com/245154 editing/selection/modify_move/move-by-character-brute-force.html [ Slow ]
-crbug.com/246749 inspector/extensions/extensions-panel.html [ Slow ]
+crbug.com/246749 http/tests/inspector/extensions/extensions-panel.html [ Slow ]
 
 # This test takes 5+ seconds as intended because it tests connection throttling.
 crbug.com/459377 http/tests/websocket/multiple-connections-throttled.html [ Slow ]
diff --git a/third_party/WebKit/LayoutTests/SmokeTests b/third_party/WebKit/LayoutTests/SmokeTests
index 4f186b9..7aa6bb2 100644
--- a/third_party/WebKit/LayoutTests/SmokeTests
+++ b/third_party/WebKit/LayoutTests/SmokeTests
@@ -974,7 +974,6 @@
 transforms/3d/hit-testing/composited-hit-test.html
 transforms/3d/hit-testing/rotated-hit-test-with-child.html
 transforms/container-transform-crash.html
-transforms/cssmatrix-crash.html
 transforms/focus-on-transformed-node.html
 transforms/no_transform_hit_testing.html
 transforms/topmost-becomes-bottomost-for-scrolling.html
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 0c961d2..8cc8f3b 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1560,22 +1560,20 @@
 crbug.com/501659 virtual/mojo-loading/http/tests/security/xss-DENIED-xml-external-entity.xhtml [ Failure ]
 crbug.com/501659 fast/css/stylesheet-candidate-nodes-crash.xhtml [ Failure ]
 
-
 # Change to higher precision for exotic blend modes
-crbug.com/732829 css3/blending/background-blend-mode-crossfade-image-gradient.html [ NeedsManualRebaseline ]
-crbug.com/732829 css3/blending/background-blend-mode-gif-color-2.html [ NeedsManualRebaseline ]
-crbug.com/732829 css3/blending/background-blend-mode-gif-color.html [ NeedsManualRebaseline ]
-crbug.com/732829 css3/blending/background-blend-mode-svg-color.html [ NeedsManualRebaseline ]
-crbug.com/732829 css3/blending/mix-blend-mode-isolated-group-1.html [ NeedsManualRebaseline ]
-crbug.com/732829 css3/blending/mix-blend-mode-isolated-group-2.html [ NeedsManualRebaseline ]
-crbug.com/732829 css3/blending/mix-blend-mode-isolated-group-3.html [ NeedsManualRebaseline ]
-crbug.com/732829 css3/blending/mix-blend-mode-simple-text.html [ NeedsManualRebaseline ]
-crbug.com/732829 svg/filters/feBlend-all-modes.html [ NeedsManualRebaseline ]
-crbug.com/732829 css3/blending/svg-blend-color.html [ NeedsManualRebaseline ]
-crbug.com/732829 css3/blending/svg-blend-hue.html [ NeedsManualRebaseline ]
-crbug.com/732829 css3/blending/svg-blend-luminosity.html [ NeedsManualRebaseline ]
-crbug.com/732829 css3/blending/svg-blend-saturation.html [ NeedsManualRebaseline ]
-
+crbug.com/732829 css3/blending/background-blend-mode-crossfade-image-gradient.html [ NeedsRebaseline ]
+crbug.com/732829 css3/blending/background-blend-mode-gif-color-2.html [ NeedsRebaseline ]
+crbug.com/732829 css3/blending/background-blend-mode-gif-color.html [ NeedsRebaseline ]
+crbug.com/732829 css3/blending/background-blend-mode-svg-color.html [ NeedsRebaseline ]
+crbug.com/732829 css3/blending/mix-blend-mode-isolated-group-1.html [ NeedsRebaseline ]
+crbug.com/732829 css3/blending/mix-blend-mode-isolated-group-2.html [ NeedsRebaseline ]
+crbug.com/732829 css3/blending/mix-blend-mode-isolated-group-3.html [ NeedsRebaseline ]
+crbug.com/732829 css3/blending/mix-blend-mode-simple-text.html [ NeedsRebaseline ]
+crbug.com/732829 svg/filters/feBlend-all-modes.html [ NeedsRebaseline ]
+crbug.com/732829 css3/blending/svg-blend-color.html [ NeedsRebaseline ]
+crbug.com/732829 css3/blending/svg-blend-hue.html [ NeedsRebaseline ]
+crbug.com/732829 css3/blending/svg-blend-luminosity.html [ NeedsRebaseline ]
+crbug.com/732829 css3/blending/svg-blend-saturation.html [ NeedsRebaseline ]
 
 # Mac10.10-specific failures that still need triaging.
 # Form controls need rebaseline because of the default font change.
@@ -2871,3 +2869,6 @@
 crbug.com/732103 [ Mac ] virtual/mojo-loading/http/tests/shapedetection/shapedetection-cross-origin.html [ Failure Pass Timeout ]
 # Sheriff failures 2017-06-10
 crbug.com/732839 [ Win ] external/wpt/payment-request/payment-request-constructor-crash.https.html [ Pass Timeout ]
+
+# Sheriff failures 2017-06-14
+crbug.com/733357 [ Win Mac ] inspector/elements/styles-4/styles-history.html [ Failure Pass ]
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index a609fab..152ce22c 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -96304,6 +96304,11 @@
      {}
     ]
    ],
+   "payment-request/payment-request-update-event-updatewith-method.https-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "performance-timeline/performanceobservers.js": [
     [
      {}
@@ -238025,6 +238030,10 @@
    "6b546870fd384a5bf2106d25fd3159a72f8537b2",
    "testharness"
   ],
+  "payment-request/payment-request-update-event-updatewith-method.https-expected.txt": [
+   "6f7d4ed0c005b4a8af0644148b174c7911531688",
+   "support"
+  ],
   "payment-request/payment-request-update-event-updatewith-method.https.html": [
    "d85bca2ccb865c11c550a9d9c1d8770b2c68d6bd",
    "testharness"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/geometry-1/DOMMatrix-stringifier-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/geometry-1/DOMMatrix-stringifier-expected.txt
deleted file mode 100644
index 22ad39c5..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/css/geometry-1/DOMMatrix-stringifier-expected.txt
+++ /dev/null
@@ -1,82 +0,0 @@
-This is a testharness.js-based test.
-Found 78 tests; 54 PASS, 24 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS DOMMatrix stringifier: identity (2d) 
-PASS DOMMatrix stringifier: identity (3d) 
-PASS DOMMatrix stringifier: NaN (2d) 
-PASS DOMMatrix stringifier: NaN (3d) 
-PASS DOMMatrix stringifier: Infinity (2d) 
-PASS DOMMatrix stringifier: Infinity (3d) 
-PASS DOMMatrix stringifier: -Infinity (2d) 
-PASS DOMMatrix stringifier: -Infinity (3d) 
-PASS DOMMatrix stringifier: 1/3 (2d) 
-PASS DOMMatrix stringifier: 1/3 (3d) 
-PASS DOMMatrix stringifier: 1/300000 (2d) 
-PASS DOMMatrix stringifier: 1/300000 (3d) 
-PASS DOMMatrix stringifier: 1/300000000 (2d) 
-PASS DOMMatrix stringifier: 1/300000000 (3d) 
-PASS DOMMatrix stringifier: 100000 + (1/3) (2d) 
-PASS DOMMatrix stringifier: 100000 + (1/3) (3d) 
-PASS DOMMatrix stringifier: Math.pow(2, 53) + 1 (2d) 
-PASS DOMMatrix stringifier: Math.pow(2, 53) + 1 (3d) 
-PASS DOMMatrix stringifier: Math.pow(2, 53) + 2 (2d) 
-PASS DOMMatrix stringifier: Math.pow(2, 53) + 2 (3d) 
-PASS DOMMatrix stringifier: Number.MAX_VALUE (2d) 
-PASS DOMMatrix stringifier: Number.MAX_VALUE (3d) 
-PASS DOMMatrix stringifier: Number.MIN_VALUE (2d) 
-PASS DOMMatrix stringifier: Number.MIN_VALUE (3d) 
-PASS DOMMatrix stringifier: throwing getters (2d) 
-PASS DOMMatrix stringifier: throwing getters (3d) 
-PASS DOMMatrixReadOnly stringifier: identity (2d) 
-PASS DOMMatrixReadOnly stringifier: identity (3d) 
-PASS DOMMatrixReadOnly stringifier: NaN (2d) 
-PASS DOMMatrixReadOnly stringifier: NaN (3d) 
-PASS DOMMatrixReadOnly stringifier: Infinity (2d) 
-PASS DOMMatrixReadOnly stringifier: Infinity (3d) 
-PASS DOMMatrixReadOnly stringifier: -Infinity (2d) 
-PASS DOMMatrixReadOnly stringifier: -Infinity (3d) 
-PASS DOMMatrixReadOnly stringifier: 1/3 (2d) 
-PASS DOMMatrixReadOnly stringifier: 1/3 (3d) 
-PASS DOMMatrixReadOnly stringifier: 1/300000 (2d) 
-PASS DOMMatrixReadOnly stringifier: 1/300000 (3d) 
-PASS DOMMatrixReadOnly stringifier: 1/300000000 (2d) 
-PASS DOMMatrixReadOnly stringifier: 1/300000000 (3d) 
-PASS DOMMatrixReadOnly stringifier: 100000 + (1/3) (2d) 
-PASS DOMMatrixReadOnly stringifier: 100000 + (1/3) (3d) 
-PASS DOMMatrixReadOnly stringifier: Math.pow(2, 53) + 1 (2d) 
-PASS DOMMatrixReadOnly stringifier: Math.pow(2, 53) + 1 (3d) 
-PASS DOMMatrixReadOnly stringifier: Math.pow(2, 53) + 2 (2d) 
-PASS DOMMatrixReadOnly stringifier: Math.pow(2, 53) + 2 (3d) 
-PASS DOMMatrixReadOnly stringifier: Number.MAX_VALUE (2d) 
-PASS DOMMatrixReadOnly stringifier: Number.MAX_VALUE (3d) 
-PASS DOMMatrixReadOnly stringifier: Number.MIN_VALUE (2d) 
-PASS DOMMatrixReadOnly stringifier: Number.MIN_VALUE (3d) 
-PASS DOMMatrixReadOnly stringifier: throwing getters (2d) 
-PASS DOMMatrixReadOnly stringifier: throwing getters (3d) 
-PASS WebKitCSSMatrix stringifier: identity (2d) 
-FAIL WebKitCSSMatrix stringifier: identity (3d) self[constr].fromMatrix is not a function
-FAIL WebKitCSSMatrix stringifier: NaN (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,NaN'.
-FAIL WebKitCSSMatrix stringifier: NaN (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,NaN,0,1'.
-FAIL WebKitCSSMatrix stringifier: Infinity (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,Infinity'.
-FAIL WebKitCSSMatrix stringifier: Infinity (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,Infinity,0,1'.
-FAIL WebKitCSSMatrix stringifier: -Infinity (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,-Infinity'.
-FAIL WebKitCSSMatrix stringifier: -Infinity (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,-Infinity,0,1'.
-FAIL WebKitCSSMatrix stringifier: 1/3 (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,0.3333333333333333'.
-FAIL WebKitCSSMatrix stringifier: 1/3 (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,0.3333333333333333,0,1'.
-FAIL WebKitCSSMatrix stringifier: 1/300000 (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,0.0000033333333333333333'.
-FAIL WebKitCSSMatrix stringifier: 1/300000 (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,0.0000033333333333333333,0,1'.
-FAIL WebKitCSSMatrix stringifier: 1/300000000 (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,3.3333333333333334e-9'.
-FAIL WebKitCSSMatrix stringifier: 1/300000000 (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,3.3333333333333334e-9,0,1'.
-FAIL WebKitCSSMatrix stringifier: 100000 + (1/3) (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,100000.33333333333'.
-FAIL WebKitCSSMatrix stringifier: 100000 + (1/3) (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,100000.33333333333,0,1'.
-FAIL WebKitCSSMatrix stringifier: Math.pow(2, 53) + 1 (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,9007199254740992'.
-FAIL WebKitCSSMatrix stringifier: Math.pow(2, 53) + 1 (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,9007199254740992,0,1'.
-FAIL WebKitCSSMatrix stringifier: Math.pow(2, 53) + 2 (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,9007199254740994'.
-FAIL WebKitCSSMatrix stringifier: Math.pow(2, 53) + 2 (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,9007199254740994,0,1'.
-FAIL WebKitCSSMatrix stringifier: Number.MAX_VALUE (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,1.7976931348623157e+308'.
-FAIL WebKitCSSMatrix stringifier: Number.MAX_VALUE (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,1.7976931348623157e+308,0,1'.
-FAIL WebKitCSSMatrix stringifier: Number.MIN_VALUE (2d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,1,0,5e-324'.
-FAIL WebKitCSSMatrix stringifier: Number.MIN_VALUE (3d) Failed to construct 'WebKitCSSMatrix': Failed to parse '1,0,0,0,0,1,0,0,0,0,1,0,0,5e-324,0,1'.
-PASS WebKitCSSMatrix stringifier: throwing getters (2d) 
-FAIL WebKitCSSMatrix stringifier: throwing getters (3d) self[constr].fromMatrix is not a function
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/geometry-1/WebKitCSSMatrix-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/geometry-1/WebKitCSSMatrix-expected.txt
deleted file mode 100644
index dd0272a..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/css/geometry-1/WebKitCSSMatrix-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL Equivalence test assert_equals: interface object expected function "function DOMMatrix() { [native code] }" but got function "function WebKitCSSMatrix() { [native code] }"
-PASS Property descriptor for WebKitCSSMatrix 
-PASS Property descriptor for DOMMatrix 
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter_null.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter_null.tentative.html
new file mode 100644
index 0000000..314a7035
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter_null.tentative.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<!-- SEKRITS! -->
+<input id="sekrit" value="omg!">
+
+<script>
+  function postMessageToFrame(frame, message) {
+    return new Promise(resolve => {
+      var c = new MessageChannel();
+      c.port1.onmessage = e => {
+        resolve({ data: e.data, frame: frame })
+      };
+      frame.contentWindow.postMessage(message, '*', [c.port2]);
+    });
+  }
+
+  function createFrame() {
+    return new Promise(resolve => {
+      var i = document.createElement('iframe');
+      i.src = "./support/document_domain_frame.html";
+      window.addEventListener('message', m => {
+        if (m.source == i.contentWindow)
+          resolve(i);
+      });
+      document.body.appendChild(i);
+    });
+  }
+
+  promise_test(t => {
+    return createFrame()
+      .then(f => postMessageToFrame(f, 'poke-at-parent'))
+      .then(result => {
+        assert_equals(result.data, document.querySelector('#sekrit').value);
+        result.frame.remove();
+      });
+  }, "Access allowed with no 'document.domain' modification. (Sanity check)");
+
+  promise_test(t => {
+    return createFrame()
+      .then(f => postMessageToFrame(f, { domain: null }))
+      .then(result => {
+        assert_equals(result.data, 'Done');
+        return postMessageToFrame(result.frame, 'poke-at-parent')
+          .then(result => {
+            assert_equals(result.data, 'SecurityError');
+            result.frame.remove();
+          });
+      });
+  }, "No access when frame sets a `null` 'document.domain'.");
+
+  promise_test(t => {
+    return createFrame()
+      .then(f => {
+        document.domain = null;
+        assert_equals(document.domain, "null");
+        return postMessageToFrame(f, 'poke-at-parent');
+      })
+      .then(result => {
+        assert_equals(result.data, 'SecurityError');
+        result.frame.remove();
+      });
+  }, "No access when parent sets a `null` 'document.domain'.");
+
+  promise_test(t => {
+    return createFrame()
+      .then(f => {
+        document.domain = null;
+        assert_equals(document.domain, "null");
+        return postMessageToFrame(f, { domain: null });
+      })
+      .then(result => {
+        assert_equals(result.data, 'Done');
+        return postMessageToFrame(result.frame, 'poke-at-parent')
+          .then(result => {
+            assert_equals(result.data, 'SecurityError');
+            result.frame.remove();
+          });
+      });
+  }, "No access when both sides set a `null` 'document.domain'.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter_srcdoc.html b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter_srcdoc.html
new file mode 100644
index 0000000..42d2d97
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter_srcdoc.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<!-- SEKRITS! -->
+<input id="sekrit" value="omg!">
+
+<script>
+  function postMessageToFrame(frame, message) {
+    return new Promise(resolve => {
+      var c = new MessageChannel();
+      c.port1.onmessage = e => { 
+        resolve({ data: e.data, frame: frame })
+      };
+      frame.contentWindow.postMessage(message, '*', [c.port2]);
+    });
+  }
+
+  function createFrame() {
+    return new Promise(resolve => {
+      var i = document.createElement('iframe');
+      i.srcdoc = `
+          <script>
+            window.addEventListener('message', e => {
+              if (e.data.domain !== undefined) {
+                try {
+                  document.domain = e.data.domain;
+                  e.ports[0].postMessage('Done');
+                } catch(error) {
+                  e.ports[0].postMessage(error.name);
+                }
+              } else if (e.data == 'poke-at-parent') {
+                try {
+                  var sekrit = window.parent.document.body.querySelector('#sekrit').value;
+                  e.ports[0].postMessage(sekrit);
+                } catch(error) {
+                  e.ports[0].postMessage(error.name);
+                }
+              }
+            });
+            window.parent.postMessage('Hi!', '*');
+          </scr` + `ipt>`;
+      window.addEventListener('message', m => {
+        if (m.source == i.contentWindow)
+          resolve(i);
+      });
+      document.body.appendChild(i);
+    });
+  }
+
+  promise_test(t => {
+    return createFrame()
+      .then(f => postMessageToFrame(f, 'poke-at-parent'))
+      .then(result => {
+        assert_equals(result.data, document.querySelector('#sekrit').value);
+        result.frame.remove();
+      });
+  }, "srcdoc can access with no 'document.domain' modification.");
+
+  promise_test(t => {
+    return createFrame()
+      .then(f => postMessageToFrame(f, { domain: window.location.hostname }))
+      .then(result => {
+        assert_equals(result.data, 'Done');
+        return postMessageToFrame(result.frame, 'poke-at-parent')
+          .then(result => {
+        assert_equals(result.data, document.querySelector('#sekrit').value);
+            result.frame.remove();
+          });
+      });
+  }, "srcdoc can access with valid 'document.domain'.");
+
+  promise_test(t => {
+    return createFrame()
+      .then(f => {
+        document.domain = window.location.hostname;
+        return postMessageToFrame(f, 'poke-at-parent');
+      })
+      .then(result => {
+        assert_equals(result.data, document.querySelector('#sekrit').value);
+        result.frame.remove();
+      });
+  }, "srcdoc can access when parent modifies 'document.domain'.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.html b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.html
new file mode 100644
index 0000000..42e8137
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/origin/relaxing-the-same-origin-restriction/support/document_domain_frame.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<script>
+  window.addEventListener('message', e => {
+    if (e.data.domain !== undefined) {
+      try {
+        document.domain = e.data.domain;
+        e.ports[0].postMessage('Done');
+      } catch(error) {
+        e.ports[0].postMessage(error.name);
+      }
+    } else if (e.data == 'poke-at-parent') {
+      try {
+        var sekrit = window.parent.document.body.querySelector('#sekrit').value;
+        e.ports[0].postMessage(sekrit);
+      } catch(error) {
+        e.ports[0].postMessage(error.name);
+      }
+    }
+  });
+  window.parent.postMessage('Hi!', '*');
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Window/custom-constructors-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Window/custom-constructors-expected.txt
index 7d0014c..294eef2 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Window/custom-constructors-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/Window/custom-constructors-expected.txt
@@ -7,7 +7,7 @@
 PASS Option.prototype.toString.call(new Option) is '[object HTMLOptionElement]'
 PASS WebKitCSSMatrix.prototype.toString.call(new WebKitCSSMatrix) is 'matrix(1, 0, 0, 1, 0, 0)'
 PASS WebKitCSSMatrix.prototype.toString.call(new WebKitCSSMatrix()) is 'matrix(1, 0, 0, 1, 0, 0)'
-PASS new WebKitCSSMatrix(null) threw exception SyntaxError: Failed to construct 'WebKitCSSMatrix': Failed to parse 'null'..
+PASS new WebKitCSSMatrix(null) threw exception SyntaxError: Failed to construct 'DOMMatrix': Failed to parse 'null'..
 FAIL new WebKitCSSMatrix(undefined) should throw an exception. Was matrix(1, 0, 0, 1, 0, 0).
 PASS XMLHttpRequest.prototype.toString.call(new XMLHttpRequest) is '[object XMLHttpRequest]'
 PASS XSLTProcessor.prototype.toString.call(new XSLTProcessor) is '[object XSLTProcessor]'
diff --git a/third_party/WebKit/LayoutTests/fast/dom/call-a-constructor-as-a-function-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/call-a-constructor-as-a-function-expected.txt
index bcd6af3..d91c597 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/call-a-constructor-as-a-function-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/call-a-constructor-as-a-function-expected.txt
@@ -22,7 +22,7 @@
 PASS Uint16Array() threw exception TypeError: Constructor Uint16Array requires 'new'.
 PASS Uint32Array() threw exception TypeError: Constructor Uint32Array requires 'new'.
 PASS Uint8Array() threw exception TypeError: Constructor Uint8Array requires 'new'.
-PASS WebKitCSSMatrix() threw exception TypeError: Failed to construct 'WebKitCSSMatrix': Please use the 'new' operator, this DOM object constructor cannot be called as a function..
+PASS WebKitCSSMatrix() threw exception TypeError: Failed to construct 'DOMMatrix': Please use the 'new' operator, this DOM object constructor cannot be called as a function..
 PASS WebSocket() threw exception TypeError: Failed to construct 'WebSocket': Please use the 'new' operator, this DOM object constructor cannot be called as a function..
 PASS Worker() threw exception TypeError: Failed to construct 'Worker': Please use the 'new' operator, this DOM object constructor cannot be called as a function..
 PASS XMLHttpRequest() threw exception TypeError: Failed to construct 'XMLHttpRequest': Please use the 'new' operator, this DOM object constructor cannot be called as a function..
diff --git a/third_party/WebKit/LayoutTests/inspector/audits/audits-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector/audits-test.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/audits/audits-test.js
rename to third_party/WebKit/LayoutTests/http/tests/inspector/audits-test.js
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/extensions-network-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions-network-test.js
index a5dc23d..44b66a99 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/extensions-network-test.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions-network-test.js
@@ -11,6 +11,8 @@
                 }
             }
         }
+        output("no item found");
+        callback(null);
     }
     webInspector.network.getHAR(onHAR);
 }
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/extensions-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions-test.js
index 9e744db..b49489f 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/extensions-test.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions-test.js
@@ -9,8 +9,14 @@
     return functions;
 }
 
+var extensionsOrigin = "http://devtools-extensions.test:8000";
+
 var initialize_ExtensionsTest = function()
 {
+
+var extensionsHost = "devtools-extensions.test";
+var extensionsOrigin = `http://${extensionsHost}:8000`;
+
 Extensions.extensionServer._registerHandler("evaluateForTestInFrontEnd", onEvaluate);
 
 Extensions.extensionServer._extensionAPITestHook = function(extensionServerClient, coreAPI)
@@ -56,7 +62,8 @@
     var extensionURL = (/^https?:/.test(pageURL) ?
         pageURL.replace(/^(https?:\/\/[^/]*\/).*$/,"$1") :
         pageURL.replace(/\/inspector\/extensions\/[^/]*$/, "/http/tests")) +
-        "/inspector/resources/extension-main.html";
+        "inspector/resources/extension-main.html";
+    extensionURL = extensionURL.replace("127.0.0.1", extensionsHost);
     InspectorFrontendAPI.addExtensions([{ startPage: extensionURL, name: "test extension", exposeWebInspectorNamespace: true }]);
     Extensions.extensionServer.initializeExtensions();
 }
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-api-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-api-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-api-expected.txt
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-api-expected.txt
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-api.html b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-api.html
similarity index 64%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-api.html
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-api.html
index 629c74db..b4789e4 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-api.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-api.html
@@ -1,15 +1,13 @@
 <html>
 <head>
-<script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="../../http/tests/inspector/extensions-test.js"></script>
+<script src="../inspector-test.js"></script>
+<script src="../extensions-test.js"></script>
 <script type="text/javascript">
-
 function extension_testAPI(nextTest)
 {
     dumpObject(webInspector);
     nextTest();
 }
-
 </script>
 </head>
 <body onload="runTest()">
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-audits-api-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-audits-api-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-audits-api-expected.txt
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-audits-api-expected.txt
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-audits-api.html b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-audits-api.html
similarity index 83%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-audits-api.html
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-audits-api.html
index 0e4cb03..074f35d 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-audits-api.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-audits-api.html
@@ -1,8 +1,8 @@
 <html>
 <head>
-<script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="../../http/tests/inspector/extensions-test.js"></script>
-<script src="../audits/audits-test.js"></script>
+<script src="../inspector-test.js"></script>
+<script src="../extensions-test.js"></script>
+<script src="../audits-test.js"></script>
 <script src="extensions-audits-tests.js"></script>
 
 <script type="text/javascript">
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-audits-content-script-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-audits-content-script-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-audits-content-script-expected.txt
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-audits-content-script-expected.txt
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-audits-content-script.html b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-audits-content-script.html
similarity index 86%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-audits-content-script.html
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-audits-content-script.html
index 9e03190..16925c9 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-audits-content-script.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-audits-content-script.html
@@ -1,15 +1,15 @@
 <html>
 <head>
-<script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="../../http/tests/inspector/extensions-test.js"></script>
-<script src="../audits/audits-test.js"></script>
+<script src="../inspector-test.js"></script>
+<script src="../extensions-test.js"></script>
+<script src="../audits-test.js"></script>
 <script src="extensions-audits-tests.js"></script>
 
 <script type="text/javascript">
 
 window.whereAmI = "main world";
 
-testRunner.setIsolatedWorldSecurityOrigin(632, "file:///");
+testRunner.setIsolatedWorldSecurityOrigin(632, extensionsOrigin);
 testRunner.evaluateScriptInIsolatedWorld(632, "window.whereAmI = 'brave new world'");
 
 function extension_testAudits(nextTest)
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-audits-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-audits-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-audits-expected.txt
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-audits-expected.txt
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-audits-tests.js b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-audits-tests.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-audits-tests.js
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-audits-tests.js
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-audits.html b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-audits.html
similarity index 93%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-audits.html
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-audits.html
index 7c5dada..997586100 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-audits.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-audits.html
@@ -1,8 +1,8 @@
 <html>
 <head>
-<script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="../../http/tests/inspector/extensions-test.js"></script>
-<script src="../audits/audits-test.js"></script>
+<script src="../inspector-test.js"></script>
+<script src="../extensions-test.js"></script>
+<script src="../audits-test.js"></script>
 <script src="extensions-audits-tests.js"></script>
 
 <script type="text/javascript">
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-eval-content-script-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-eval-content-script-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-eval-content-script-expected.txt
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-eval-content-script-expected.txt
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-eval-content-script.html b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-eval-content-script.html
similarity index 82%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-eval-content-script.html
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-eval-content-script.html
index ac0404f0..8c4c0ef 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-eval-content-script.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-eval-content-script.html
@@ -1,12 +1,12 @@
 <html>
 <head>
-<script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="../../http/tests/inspector/extensions-test.js"></script>
+<script src="../inspector-test.js"></script>
+<script src="../extensions-test.js"></script>
 <script type="text/javascript">
 
 window.whereAmI = "main world";
 
-testRunner.setIsolatedWorldSecurityOrigin(632, "file:///");
+testRunner.setIsolatedWorldSecurityOrigin(632, extensionsOrigin);
 testRunner.evaluateScriptInIsolatedWorld(632, "window.whereAmI = 'brave new world'");
 
 function extension_testEvalInMainWorldImplicit(nextTest)
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-eval-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-eval-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-eval-expected.txt
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-eval-expected.txt
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-eval.html b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-eval.html
similarity index 93%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-eval.html
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-eval.html
index ebb7db4..15e74a57 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-eval.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-eval.html
@@ -1,7 +1,7 @@
 <html>
 <head>
-<script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="../../http/tests/inspector/extensions-test.js"></script>
+<script src="../inspector-test.js"></script>
+<script src="../extensions-test.js"></script>
 <script type="text/javascript">
 
 window.inspectedValue = { str: "foo", num: 42 };
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-events-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-events-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-events-expected.txt
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-events-expected.txt
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-events.html b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-events.html
similarity index 92%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-events.html
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-events.html
index 8a09ce0..004f32d 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-events.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-events.html
@@ -1,9 +1,9 @@
 <html>
 <head>
-<script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="../../http/tests/inspector/extensions-test.js"></script>
-<script src="../../http/tests/inspector/debugger-test.js"></script>
-<script src="../sources/debugger/resources/edit-me.js"></script>
+<script src="../inspector-test.js"></script>
+<script src="../extensions-test.js"></script>
+<script src="../debugger-test.js"></script>
+<script src="../../../inspector/sources/debugger/resources/edit-me.js"></script>
 <script type="text/javascript">
 
 function initialize_extensionsSidebarTest()
@@ -49,7 +49,7 @@
         nextTest();
     }
     webInspector.network.onRequestFinished.addListener(onRequestFinished);
-    webInspector.inspectedWindow.eval("var xhr = new XMLHttpRequest(); xhr.open('GET', '" + location.href + "', false); xhr.send(null);");
+    webInspector.inspectedWindow.eval("var xhr = new XMLHttpRequest(); xhr.open('GET', location.href, false); xhr.send(null);");
 }
 
 function extension_testOnNavigated(nextTest)
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-network-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-network-expected.txt
similarity index 96%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-network-expected.txt
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-network-expected.txt
index 1036a2c..074fe12 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-network-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-network-expected.txt
@@ -5,15 +5,15 @@
 Started extension.
 Running tests...
 RUNNING TEST: extension_testGetHAR
-resource: .../tests/inspector/extensions-network-test.js
-resource: .../tests/inspector/extensions-test.js
-resource: .../tests/inspector/inspector-test.js
-resource: .../inspector/extensions/extensions-network.html
-resource: .../inspector/extensions/extensions-network.html
-resource: .../extensions/resources/abe.png
-resource: .../extensions/resources/audits-style1.css
-resource: .../extensions/resources/missing-image.png
-resource: .../LayoutTests/resources/Ahem.ttf
+resource: http://127.0.0.1:8000/inspector/extensions-network-test.js
+resource: http://127.0.0.1:8000/inspector/extensions-test.js
+resource: http://127.0.0.1:8000/inspector/extensions/extensions-network.html
+resource: http://127.0.0.1:8000/inspector/extensions/extensions-network.html
+resource: http://127.0.0.1:8000/inspector/extensions/resources/abe.png
+resource: http://127.0.0.1:8000/inspector/extensions/resources/audits-style1.css
+resource: http://127.0.0.1:8000/inspector/extensions/resources/missing-image.png
+resource: http://127.0.0.1:8000/inspector/inspector-test.js
+resource: http://127.0.0.1:8000/resources/Ahem.ttf
 RUNNING TEST: extension_testGetRequestContent
 {
     0 : ".some-style {
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-network.html b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-network.html
similarity index 77%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-network.html
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-network.html
index 7a78707..b16e954f 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-network.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-network.html
@@ -1,6 +1,5 @@
 <html>
 <head>
-<link rel="stylesheet" href="resources/audits-style1.css" type="text/css">
 <style>
 @font-face {
     font-family: 'test';
@@ -10,9 +9,10 @@
 p { font-family: 'test'; }
 </style>
 
-<script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="../../http/tests/inspector/extensions-test.js"></script>
-<script src="../../http/tests/inspector/extensions-network-test.js"></script>
+<script src="../inspector-test.js"></script>
+<script src="../extensions-test.js"></script>
+<script src="../extensions-network-test.js"></script>
+<link rel="stylesheet" href="resources/audits-style1.css" type="text/css">
 <script type="text/javascript">
 
 function extension_testGetHAR(nextTest)
@@ -27,7 +27,7 @@
         result.entries.sort(compareEntries);
 
         for (var i = 0; i < result.entries.length; ++i)
-            output("resource: " + result.entries[i].request.url.replace(/.*((\/[^/]*){3}$)/,"...$1"));
+            output("resource: " + result.entries[i].request.url);
     }
     extension_doXHR(function() {
         webInspector.network.getHAR(callbackAndNextTest(onHAR, nextTest));
@@ -36,14 +36,20 @@
 
 function doXHR()
 {
-    var xhr = new XMLHttpRequest();
-    xhr.open("GET", "", false);
-    xhr.send(null);
+    return new Promise((fulfill) => {
+        var xhr = new XMLHttpRequest();
+        xhr.open("GET", "", true);
+        xhr.send(null);
+        xhr.onreadystatechange = () => {
+          if (xhr.readyState == 4 && xhr.status == 200)
+            fulfill();
+        };
+    });
 }
 
 function extension_doXHR(callback)
 {
-    webInspector.inspectedWindow.eval("doXHR()", callback);
+    invokePageFunctionAsync("doXHR", callback);
 }
 
 function extension_testRequestNotification(nextTest)
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-panel-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-panel-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-panel-expected.txt
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-panel-expected.txt
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-panel.html b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-panel.html
similarity index 96%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-panel.html
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-panel.html
index 45532d9..0dc5b48 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-panel.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-panel.html
@@ -1,11 +1,11 @@
 <html>
 <head>
-<script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="../../http/tests/inspector/console-test.js"></script>
-<script src="../../http/tests/inspector/network-test.js"></script>
-<script src="../../http/tests/inspector/sources-test.js"></script>
-<script src="../../http/tests/inspector/resources-test.js"></script>
-<script src="../../http/tests/inspector/extensions-test.js"></script>
+<script src="../inspector-test.js"></script>
+<script src="../console-test.js"></script>
+<script src="../network-test.js"></script>
+<script src="../sources-test.js"></script>
+<script src="../resources-test.js"></script>
+<script src="../extensions-test.js"></script>
 <script type="text/javascript">
 function logMessage()
 {
@@ -225,7 +225,7 @@
 
         function performSearch(query)
         {
-            UI.inspectorView.panel("file://TestPanelforsearch").then(panel => {
+            UI.inspectorView.panel(extensionsOrigin + "TestPanelforsearch").then(panel => {
                 panel.searchableView().showSearchField();
                 panel.searchableView()._searchInputElement.value = query;
                 panel.searchableView()._performSearch(true, true);
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-reload-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-reload-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-reload-expected.txt
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-reload-expected.txt
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-reload.html b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-reload.html
similarity index 91%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-reload.html
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-reload.html
index dfb3528..1c27c0d 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-reload.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-reload.html
@@ -1,9 +1,9 @@
 <html>
 <head>
-<script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="../../http/tests/inspector/debugger-test.js"></script>
-<script src="../../http/tests/inspector/extensions-test.js"></script>
-<script src="../../http/tests/inspector/console-test.js"></script>
+<script src="../inspector-test.js"></script>
+<script src="../debugger-test.js"></script>
+<script src="../extensions-test.js"></script>
+<script src="../console-test.js"></script>
 <script type="text/javascript">
 
 window.bar = "foo = " + window.foo;
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-resources-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-resources-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-resources-expected.txt
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-resources-expected.txt
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-resources.html b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-resources.html
similarity index 96%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-resources.html
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-resources.html
index c03206b..63f69c6 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-resources.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-resources.html
@@ -1,9 +1,9 @@
 <html>
 <head>
-<script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="../../http/tests/inspector/console-test.js"></script>
-<script src="../../http/tests/inspector/extensions-test.js"></script>
-<script src="../../http/tests/inspector/debugger-test.js"></script>
+<script src="../inspector-test.js"></script>
+<script src="../console-test.js"></script>
+<script src="../extensions-test.js"></script>
+<script src="../debugger-test.js"></script>
 <script type="text/javascript">
 function loadFrame()
 {
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-sidebar-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-sidebar-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-sidebar-expected.txt
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-sidebar-expected.txt
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-sidebar.html b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-sidebar.html
similarity index 98%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-sidebar.html
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-sidebar.html
index 2a70803..1dd3c51 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-sidebar.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-sidebar.html
@@ -1,7 +1,7 @@
 <html>
 <head>
-<script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="../../http/tests/inspector/extensions-test.js"></script>
+<script src="../inspector-test.js"></script>
+<script src="../extensions-test.js"></script>
 <script type="text/javascript">
 
 function initialize_extensionsSidebarTest()
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-timeline-api-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-timeline-api-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-timeline-api-expected.txt
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-timeline-api-expected.txt
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-timeline-api.html b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-timeline-api.html
similarity index 93%
rename from third_party/WebKit/LayoutTests/inspector/extensions/extensions-timeline-api.html
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-timeline-api.html
index c216f2a..b4ed623 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/extensions-timeline-api.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/extensions-timeline-api.html
@@ -1,8 +1,8 @@
 <html>
 <head>
-<script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="../../http/tests/inspector/extensions-test.js"></script>
-<script src="../../http/tests/inspector/timeline-test.js"></script>
+<script src="../inspector-test.js"></script>
+<script src="../extensions-test.js"></script>
+<script src="../timeline-test.js"></script>
 
 <script type="text/javascript">
 function initialize_timelineExtensionTest()
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/multiple-extensions-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/multiple-extensions-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/extensions/multiple-extensions-expected.txt
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/multiple-extensions-expected.txt
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/multiple-extensions.html b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/multiple-extensions.html
similarity index 86%
rename from third_party/WebKit/LayoutTests/inspector/extensions/multiple-extensions.html
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/multiple-extensions.html
index fdfae56c..861d3589 100644
--- a/third_party/WebKit/LayoutTests/inspector/extensions/multiple-extensions.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/multiple-extensions.html
@@ -1,7 +1,7 @@
 <html>
 <head>
-<script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="../../http/tests/inspector/extensions-test.js"></script>
+<script src="../inspector-test.js"></script>
+<script src="../extensions-test.js"></script>
 <script type="text/javascript">
 function initialize_multipleExtensionsTest()
 {
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/resources/abe.png b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/resources/abe.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/extensions/resources/abe.png
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/resources/abe.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/resources/audits-style1.css b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/resources/audits-style1.css
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/extensions/resources/audits-style1.css
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/resources/audits-style1.css
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/resources/subframe.html b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/resources/subframe.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/extensions/resources/subframe.html
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/resources/subframe.html
diff --git a/third_party/WebKit/LayoutTests/inspector/extensions/resources/test-script.js b/third_party/WebKit/LayoutTests/http/tests/inspector/extensions/resources/test-script.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector/extensions/resources/test-script.js
rename to third_party/WebKit/LayoutTests/http/tests/inspector/extensions/resources/test-script.js
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/resources/extension-main.js b/third_party/WebKit/LayoutTests/http/tests/inspector/resources/extension-main.js
index 63c40089..5416cdf 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/resources/extension-main.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/resources/extension-main.js
@@ -63,7 +63,7 @@
 function onError(event)
 {
     window.removeEventListener("error", onError);
-    output("Uncaught exception in extension context: " + event.message + " [" + event.filename + ":" + event.lineno + "]");
+    output("Uncaught exception in extension context: " + event.message + " [" + event.filename + ":" + event.lineno + "]\n");
     evaluateOnFrontend("InspectorTest.completeTest();");
 }
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/document-domain-invalid.html b/third_party/WebKit/LayoutTests/http/tests/security/document-domain-invalid.html
index 91fd118..893dd017 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/document-domain-invalid.html
+++ b/third_party/WebKit/LayoutTests/http/tests/security/document-domain-invalid.html
@@ -9,15 +9,6 @@
             assert_equals(document.domain, '127.0.0.1');
             assert_throws('SecurityError',
                           function () {
-                            document.domain = null;
-                          });
-            assert_equals(document.domain, '127.0.0.1');
-        }, 'Setting `document.domain` to null fails.');
-
-        test(function () {
-            assert_equals(document.domain, '127.0.0.1');
-            assert_throws('SecurityError',
-                          function () {
                             document.domain = '';
                           });
             assert_equals(document.domain, '127.0.0.1');
diff --git a/third_party/WebKit/LayoutTests/inspector/audits/audits-empty-stylesheet.html b/third_party/WebKit/LayoutTests/inspector/audits/audits-empty-stylesheet.html
index 2a11d0e..56c7ecf7 100644
--- a/third_party/WebKit/LayoutTests/inspector/audits/audits-empty-stylesheet.html
+++ b/third_party/WebKit/LayoutTests/inspector/audits/audits-empty-stylesheet.html
@@ -5,7 +5,7 @@
 /* Empty stylesheet */
 </style>
 <script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="audits-test.js"></script>
+<script src="../../http/tests/inspector/audits-test.js"></script>
 
 <script>
 function test()
diff --git a/third_party/WebKit/LayoutTests/inspector/audits/audits-panel-functional.html b/third_party/WebKit/LayoutTests/inspector/audits/audits-panel-functional.html
index ce53f03..6cf35f417 100644
--- a/third_party/WebKit/LayoutTests/inspector/audits/audits-panel-functional.html
+++ b/third_party/WebKit/LayoutTests/inspector/audits/audits-panel-functional.html
@@ -42,7 +42,7 @@
 JSON = {};
 </script>
 <script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="audits-test.js"></script>
+<script src="../../http/tests/inspector/audits-test.js"></script>
 
 <!-- These scripts are needed to result in a violation of the max JS resource count from the same domain -->
 <script src="resources/audits-script1.js"></script>
diff --git a/third_party/WebKit/LayoutTests/inspector/audits/audits-panel-noimages-functional.html b/third_party/WebKit/LayoutTests/inspector/audits/audits-panel-noimages-functional.html
index edfdb02..b738592 100644
--- a/third_party/WebKit/LayoutTests/inspector/audits/audits-panel-noimages-functional.html
+++ b/third_party/WebKit/LayoutTests/inspector/audits/audits-panel-noimages-functional.html
@@ -13,7 +13,7 @@
 JSON = {};
 </script>
 <script src="../../http/tests/inspector/inspector-test.js"></script>
-<script src="audits-test.js"></script>
+<script src="../../http/tests/inspector/audits-test.js"></script>
 
 <!-- These scripts are needed to result in a violation of the max JS resource count from the same domain -->
 <script src="resources/audits-script1.js"></script>
diff --git a/third_party/WebKit/LayoutTests/inspector/user-metrics.html b/third_party/WebKit/LayoutTests/inspector/user-metrics.html
index ca156a6..8867bea 100644
--- a/third_party/WebKit/LayoutTests/inspector/user-metrics.html
+++ b/third_party/WebKit/LayoutTests/inspector/user-metrics.html
@@ -3,7 +3,7 @@
 <script src="../http/tests/inspector/inspector-test.js"></script>
 <script src="../http/tests/inspector/timeline-test.js"></script>
 <script src="profiler/profiler-test.js"></script>
-<script src="audits/audits-test.js"></script>
+<script src="../http/tests/inspector/audits-test.js"></script>
 <script>
 
 function test()
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt
index b743349..4289e61 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -7369,63 +7369,9 @@
     getter animationName
     getter elapsedTime
     method constructor
-interface WebKitCSSMatrix
+interface WebKitCSSMatrix : DOMMatrixReadOnly
     attribute @@toStringTag
-    getter a
-    getter b
-    getter c
-    getter d
-    getter e
-    getter f
-    getter m11
-    getter m12
-    getter m13
-    getter m14
-    getter m21
-    getter m22
-    getter m23
-    getter m24
-    getter m31
-    getter m32
-    getter m33
-    getter m34
-    getter m41
-    getter m42
-    getter m43
-    getter m44
     method constructor
-    method inverse
-    method multiply
-    method rotate
-    method rotateAxisAngle
-    method scale
-    method setMatrixValue
-    method skewX
-    method skewY
-    method toString
-    method translate
-    setter a
-    setter b
-    setter c
-    setter d
-    setter e
-    setter f
-    setter m11
-    setter m12
-    setter m13
-    setter m14
-    setter m21
-    setter m22
-    setter m23
-    setter m24
-    setter m31
-    setter m32
-    setter m33
-    setter m34
-    setter m41
-    setter m42
-    setter m43
-    setter m44
 interface WebKitMutationObserver
     attribute @@toStringTag
     method constructor
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
index ff81a8a..f88813a 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -7298,63 +7298,9 @@
     getter animationName
     getter elapsedTime
     method constructor
-interface WebKitCSSMatrix
+interface WebKitCSSMatrix : DOMMatrixReadOnly
     attribute @@toStringTag
-    getter a
-    getter b
-    getter c
-    getter d
-    getter e
-    getter f
-    getter m11
-    getter m12
-    getter m13
-    getter m14
-    getter m21
-    getter m22
-    getter m23
-    getter m24
-    getter m31
-    getter m32
-    getter m33
-    getter m34
-    getter m41
-    getter m42
-    getter m43
-    getter m44
     method constructor
-    method inverse
-    method multiply
-    method rotate
-    method rotateAxisAngle
-    method scale
-    method setMatrixValue
-    method skewX
-    method skewY
-    method toString
-    method translate
-    setter a
-    setter b
-    setter c
-    setter d
-    setter e
-    setter f
-    setter m11
-    setter m12
-    setter m13
-    setter m14
-    setter m21
-    setter m22
-    setter m23
-    setter m24
-    setter m31
-    setter m32
-    setter m33
-    setter m34
-    setter m41
-    setter m42
-    setter m43
-    setter m44
 interface WebKitMutationObserver
     attribute @@toStringTag
     method constructor
diff --git a/third_party/WebKit/LayoutTests/transforms/3d/general/cssmatrix-3d-zoom-expected.png b/third_party/WebKit/LayoutTests/transforms/3d/general/cssmatrix-3d-zoom-expected.png
index cdd5578c..ba6d122 100644
--- a/third_party/WebKit/LayoutTests/transforms/3d/general/cssmatrix-3d-zoom-expected.png
+++ b/third_party/WebKit/LayoutTests/transforms/3d/general/cssmatrix-3d-zoom-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/transforms/cssmatrix-2d-interface-expected.txt b/third_party/WebKit/LayoutTests/transforms/cssmatrix-2d-interface-expected.txt
index fb5cb01..66b3e49c 100644
--- a/third_party/WebKit/LayoutTests/transforms/cssmatrix-2d-interface-expected.txt
+++ b/third_party/WebKit/LayoutTests/transforms/cssmatrix-2d-interface-expected.txt
@@ -23,7 +23,7 @@
 PASS a3[1] is ""
 
 Test bad input to string constructor
-PASS new WebKitCSSMatrix("banana") threw exception SyntaxError: Failed to construct 'WebKitCSSMatrix': Failed to parse 'banana'..
+PASS new WebKitCSSMatrix("banana") threw exception SyntaxError: Failed to construct 'DOMMatrix': Failed to parse 'banana'..
 
 Test attributes on default matrix
 PASS m.a is 1
@@ -58,9 +58,9 @@
 PASS m.f is 20
 
 Test throwing exception from setMatrixValue
-PASS m.setMatrixValue("banana") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'WebKitCSSMatrix': Failed to parse 'banana'..
-PASS m.setMatrixValue("translate(10em, 20%)") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'WebKitCSSMatrix': The transformation depends on the box size, which is not supported..
-PASS m.setMatrixValue("translate(10px, 20px) scale()") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'WebKitCSSMatrix': Failed to parse 'translate(10px, 20px) scale()'..
+PASS m.setMatrixValue("banana") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'DOMMatrix': Failed to parse 'banana'..
+PASS m.setMatrixValue("translate(10em, 20%)") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'DOMMatrix': Lengths must be absolute, not relative.
+PASS m.setMatrixValue("translate(10px, 20px) scale()") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'DOMMatrix': Failed to parse 'translate(10px, 20px) scale()'..
 
 Test attributes on translate() and accumulation
 PASS m2.a is 1
@@ -189,7 +189,12 @@
 PASS sx.f is 0
 
 Test multiply with missing argument
-PASS m2 is null
+PASS m2.a is 1
+PASS m2.b is 2
+PASS m2.c is 3
+PASS m2.d is 4
+PASS m2.e is 5
+PASS m2.f is 6
 
 Test inverse
 PASS parseFloat(m2.a) is 0.5
@@ -207,8 +212,21 @@
 PASS parseFloat(m.e) is 10
 PASS parseFloat(m.f) is 20
 
-Test throwing exception from inverse
-PASS m.inverse() threw exception NotSupportedError: Failed to execute 'inverse' on 'WebKitCSSMatrix': The matrix is not invertable..
+Test not invertible matrix
+PASS m2.a is NaN
+PASS m2.b is NaN
+PASS m2.c is NaN
+PASS m2.d is NaN
+PASS m2.e is NaN
+PASS m2.f is NaN
+
+Test immutability of inverse
+PASS m.a is 0
+PASS m.b is 0
+PASS m.c is 0
+PASS m.d is 0
+PASS m.e is 0
+PASS m.f is 0
 
 PASS successfullyParsed is true
 
diff --git a/third_party/WebKit/LayoutTests/transforms/cssmatrix-2d-interface.xhtml b/third_party/WebKit/LayoutTests/transforms/cssmatrix-2d-interface.xhtml
index cc85536..ed7b00c 100644
--- a/third_party/WebKit/LayoutTests/transforms/cssmatrix-2d-interface.xhtml
+++ b/third_party/WebKit/LayoutTests/transforms/cssmatrix-2d-interface.xhtml
@@ -255,7 +255,12 @@
 debug("Test multiply with missing argument");
 m = new WebKitCSSMatrix("matrix(1, 2, 3, 4, 5, 6)");
 m2 = m.multiply();
-shouldBe('m2', 'null');
+shouldBe('m2.a', '1');
+shouldBe('m2.b', '2');
+shouldBe('m2.c', '3');
+shouldBe('m2.d', '4');
+shouldBe('m2.e', '5');
+shouldBe('m2.f', '6');
 
 debug("");
 debug("Test inverse");
@@ -279,9 +284,25 @@
 shouldBe('parseFloat(m.f)', '20');
 
 debug("");
-debug("Test throwing exception from inverse");
+debug("Test not invertible matrix");
 m = new WebKitCSSMatrix("matrix(0, 0, 0, 0, 0, 0)"); // not invertible
-shouldThrow('m.inverse()');
+m2 = m.inverse();
+
+shouldBeNaN('m2.a');
+shouldBeNaN('m2.b');
+shouldBeNaN('m2.c');
+shouldBeNaN('m2.d');
+shouldBeNaN('m2.e');
+shouldBeNaN('m2.f');
+
+debug("");
+debug("Test immutability of inverse");
+shouldBe('m.a', '0');
+shouldBe('m.b', '0');
+shouldBe('m.c', '0');
+shouldBe('m.d', '0');
+shouldBe('m.e', '0');
+shouldBe('m.f', '0');
 
 debug("");
 
diff --git a/third_party/WebKit/LayoutTests/transforms/cssmatrix-3d-interface-expected.txt b/third_party/WebKit/LayoutTests/transforms/cssmatrix-3d-interface-expected.txt
index 247a66d..3563c33 100644
--- a/third_party/WebKit/LayoutTests/transforms/cssmatrix-3d-interface-expected.txt
+++ b/third_party/WebKit/LayoutTests/transforms/cssmatrix-3d-interface-expected.txt
@@ -29,7 +29,7 @@
 PASS a3[1] is ""
 
 Test bad input to string constructor
-PASS new WebKitCSSMatrix("banana") threw exception SyntaxError: Failed to construct 'WebKitCSSMatrix': Failed to parse 'banana'..
+PASS new WebKitCSSMatrix("banana") threw exception SyntaxError: Failed to construct 'DOMMatrix': Failed to parse 'banana'..
 
 Test attributes on default matrix
 PASS m.m11 is 1
@@ -104,9 +104,9 @@
 PASS m.m44 is 1
 
 Test throwing exception from setMatrixValue
-PASS m.setMatrixValue("banana") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'WebKitCSSMatrix': Failed to parse 'banana'..
-PASS m.setMatrixValue("translate3d(10em, 20%, 40)") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'WebKitCSSMatrix': Failed to parse 'translate3d(10em, 20%, 40)'..
-PASS m.setMatrixValue("translate3d(10px, 20px, 30px) scale3d()") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'WebKitCSSMatrix': Failed to parse 'translate3d(10px, 20px, 30px) scale3d()'..
+PASS m.setMatrixValue("banana") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'DOMMatrix': Failed to parse 'banana'..
+PASS m.setMatrixValue("translate3d(10em, 20%, 40)") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'DOMMatrix': Failed to parse 'translate3d(10em, 20%, 40)'..
+PASS m.setMatrixValue("translate3d(10px, 20px, 30px) scale3d()") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'DOMMatrix': Failed to parse 'translate3d(10px, 20px, 30px) scale3d()'..
 
 Test multiply
 PASS parseFloat(m3.m11) is 538
@@ -216,8 +216,41 @@
 PASS parseFloat(m.m43) is 30
 PASS parseFloat(m.m44) is 1
 
-Test throwing exception from inverse
-PASS m.inverse() threw exception NotSupportedError: Failed to execute 'inverse' on 'WebKitCSSMatrix': The matrix is not invertable..
+Test not invertible 3d matrix
+PASS m2.m11 is NaN
+PASS m2.m12 is NaN
+PASS m2.m13 is NaN
+PASS m2.m14 is NaN
+PASS m2.m21 is NaN
+PASS m2.m22 is NaN
+PASS m2.m23 is NaN
+PASS m2.m24 is NaN
+PASS m2.m31 is NaN
+PASS m2.m32 is NaN
+PASS m2.m33 is NaN
+PASS m2.m34 is NaN
+PASS m2.m41 is NaN
+PASS m2.m42 is NaN
+PASS m2.m43 is NaN
+PASS m2.m44 is NaN
+
+Test immutability of inverse
+PASS m.m11 is 0
+PASS m.m12 is 0
+PASS m.m13 is 0
+PASS m.m14 is 0
+PASS m.m21 is 0
+PASS m.m22 is 0
+PASS m.m23 is 0
+PASS m.m24 is 0
+PASS m.m31 is 0
+PASS m.m32 is 0
+PASS m.m33 is 0
+PASS m.m34 is 0
+PASS m.m41 is 0
+PASS m.m42 is 0
+PASS m.m43 is 0
+PASS m.m44 is 0
 
 Test translate
 PASS m2.m11 is 1
diff --git a/third_party/WebKit/LayoutTests/transforms/cssmatrix-3d-interface.xhtml b/third_party/WebKit/LayoutTests/transforms/cssmatrix-3d-interface.xhtml
index 392949f..2ea85b43 100644
--- a/third_party/WebKit/LayoutTests/transforms/cssmatrix-3d-interface.xhtml
+++ b/third_party/WebKit/LayoutTests/transforms/cssmatrix-3d-interface.xhtml
@@ -277,9 +277,45 @@
 shouldBe('parseFloat(m.m44)', '1');
 
 debug("");
-debug("Test throwing exception from inverse");
+debug("Test not invertible 3d matrix");
 m = new WebKitCSSMatrix("matrix3d(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)"); // not invertible
-shouldThrow('m.inverse()');
+m2 = m.inverse();
+
+shouldBeNaN('m2.m11');
+shouldBeNaN('m2.m12');
+shouldBeNaN('m2.m13');
+shouldBeNaN('m2.m14');
+shouldBeNaN('m2.m21');
+shouldBeNaN('m2.m22');
+shouldBeNaN('m2.m23');
+shouldBeNaN('m2.m24');
+shouldBeNaN('m2.m31');
+shouldBeNaN('m2.m32');
+shouldBeNaN('m2.m33');
+shouldBeNaN('m2.m34');
+shouldBeNaN('m2.m41');
+shouldBeNaN('m2.m42');
+shouldBeNaN('m2.m43');
+shouldBeNaN('m2.m44');
+
+debug("");
+debug("Test immutability of inverse");
+shouldBe('m.m11', '0');
+shouldBe('m.m12', '0');
+shouldBe('m.m13', '0');
+shouldBe('m.m14', '0');
+shouldBe('m.m21', '0');
+shouldBe('m.m22', '0');
+shouldBe('m.m23', '0');
+shouldBe('m.m24', '0');
+shouldBe('m.m31', '0');
+shouldBe('m.m32', '0');
+shouldBe('m.m33', '0');
+shouldBe('m.m34', '0');
+shouldBe('m.m41', '0');
+shouldBe('m.m42', '0');
+shouldBe('m.m43', '0');
+shouldBe('m.m44', '0');
 
 debug("");
 debug("Test translate");
diff --git a/third_party/WebKit/LayoutTests/transforms/cssmatrix-crash-expected.txt b/third_party/WebKit/LayoutTests/transforms/cssmatrix-crash-expected.txt
deleted file mode 100644
index ddad5eaf..0000000
--- a/third_party/WebKit/LayoutTests/transforms/cssmatrix-crash-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-PASS if no crash
diff --git a/third_party/WebKit/LayoutTests/transforms/cssmatrix-crash.html b/third_party/WebKit/LayoutTests/transforms/cssmatrix-crash.html
deleted file mode 100644
index ad928d2..0000000
--- a/third_party/WebKit/LayoutTests/transforms/cssmatrix-crash.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<script>
-if (window.testRunner)
-    testRunner.dumpAsText();
-
-new WebKitCSSMatrix("translateX(1ex)");
-new WebKitCSSMatrix("translateX(1ch)");
-</script>
-<p>PASS if no crash</p>
diff --git a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
index 22f8ae8..c9903255 100644
--- a/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/service-worker-navigation-preload-disabled/webexposed/global-interface-listing-expected.txt
@@ -8495,7 +8495,7 @@
     getter animationName
     getter elapsedTime
     method constructor
-interface WebKitCSSMatrix
+interface WebKitCSSMatrix : DOMMatrixReadOnly
     attribute @@toStringTag
     getter a
     getter b
@@ -8520,16 +8520,18 @@
     getter m43
     getter m44
     method constructor
-    method inverse
-    method multiply
-    method rotate
-    method rotateAxisAngle
-    method scale
+    method invertSelf
+    method multiplySelf
+    method preMultiplySelf
+    method rotateAxisAngleSelf
+    method rotateFromVectorSelf
+    method rotateSelf
+    method scale3dSelf
+    method scaleSelf
     method setMatrixValue
-    method skewX
-    method skewY
-    method toString
-    method translate
+    method skewXSelf
+    method skewYSelf
+    method translateSelf
     setter a
     setter b
     setter c
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index a0f6f0de..eca5b15 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -8503,7 +8503,7 @@
     getter animationName
     getter elapsedTime
     method constructor
-interface WebKitCSSMatrix
+interface WebKitCSSMatrix : DOMMatrixReadOnly
     attribute @@toStringTag
     getter a
     getter b
@@ -8528,16 +8528,18 @@
     getter m43
     getter m44
     method constructor
-    method inverse
-    method multiply
-    method rotate
-    method rotateAxisAngle
-    method scale
+    method invertSelf
+    method multiplySelf
+    method preMultiplySelf
+    method rotateAxisAngleSelf
+    method rotateFromVectorSelf
+    method rotateSelf
+    method scale3dSelf
+    method scaleSelf
     method setMatrixValue
-    method skewX
-    method skewY
-    method toString
-    method translate
+    method skewXSelf
+    method skewYSelf
+    method translateSelf
     setter a
     setter b
     setter c
diff --git a/third_party/WebKit/Source/core/core_idl_files.gni b/third_party/WebKit/Source/core/core_idl_files.gni
index 63aa5c8..36809bdc 100644
--- a/third_party/WebKit/Source/core/core_idl_files.gni
+++ b/third_party/WebKit/Source/core/core_idl_files.gni
@@ -62,7 +62,6 @@
                                  "css/StyleMedia.idl",
                                  "css/StyleSheet.idl",
                                  "css/StyleSheetList.idl",
-                                 "css/WebKitCSSMatrix.idl",
                                  "css/cssom/CSSImageValue.idl",
                                  "css/cssom/CSSKeywordValue.idl",
                                  "css/cssom/CSSMatrixComponent.idl",
diff --git a/third_party/WebKit/Source/core/css/BUILD.gn b/third_party/WebKit/Source/core/css/BUILD.gn
index 827bc67f..32af1bf 100644
--- a/third_party/WebKit/Source/core/css/BUILD.gn
+++ b/third_party/WebKit/Source/core/css/BUILD.gn
@@ -94,8 +94,6 @@
     "CSSKeyframesRule.h",
     "CSSMarkup.cpp",
     "CSSMarkup.h",
-    "CSSMatrix.cpp",
-    "CSSMatrix.h",
     "CSSMediaRule.cpp",
     "CSSMediaRule.h",
     "CSSNamespaceRule.cpp",
diff --git a/third_party/WebKit/Source/core/css/CSSMatrix.cpp b/third_party/WebKit/Source/core/css/CSSMatrix.cpp
deleted file mode 100644
index c123e4b..0000000
--- a/third_party/WebKit/Source/core/css/CSSMatrix.cpp
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2008 Apple 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:
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
- */
-
-#include "core/css/CSSMatrix.h"
-
-#include "bindings/core/v8/ExceptionState.h"
-#include "core/CSSPropertyNames.h"
-#include "core/CSSValueKeywords.h"
-#include "core/css/CSSIdentifierValue.h"
-#include "core/css/CSSToLengthConversionData.h"
-#include "core/css/StylePropertySet.h"
-#include "core/css/parser/CSSParser.h"
-#include "core/css/resolver/TransformBuilder.h"
-#include "core/dom/ExceptionCode.h"
-#include "core/frame/UseCounter.h"
-#include "core/layout/api/LayoutViewItem.h"
-#include "core/style/ComputedStyle.h"
-#include "platform/wtf/MathExtras.h"
-
-namespace blink {
-
-CSSMatrix* CSSMatrix::Create(ExecutionContext* execution_context,
-                             const String& s,
-                             ExceptionState& exception_state) {
-  UseCounter::Count(execution_context, WebFeature::kWebKitCSSMatrix);
-  if (!s.IsEmpty()) {
-    UseCounter::Count(execution_context,
-                      WebFeature::kWebkitCSSMatrixConstructFromString);
-  }
-  return new CSSMatrix(s, exception_state);
-}
-
-CSSMatrix::CSSMatrix(const TransformationMatrix& m)
-    : matrix_(TransformationMatrix::Create(m)) {}
-
-CSSMatrix::CSSMatrix(const String& s, ExceptionState& exception_state)
-    : matrix_(TransformationMatrix::Create()) {
-  setMatrixValue(s, exception_state);
-}
-
-static inline PassRefPtr<ComputedStyle> CreateInitialStyle() {
-  RefPtr<ComputedStyle> initial_style = ComputedStyle::Create();
-  initial_style->GetFont().Update(nullptr);
-  return initial_style;
-}
-
-void CSSMatrix::setMatrixValue(const String& string,
-                               ExceptionState& exception_state) {
-  if (string.IsEmpty())
-    return;
-
-  if (const CSSValue* value =
-          CSSParser::ParseSingleValue(CSSPropertyTransform, string)) {
-    // Check for a "none" transform. In these cases we can use the default
-    // identity matrix.
-    if (value->IsIdentifierValue() &&
-        (ToCSSIdentifierValue(value))->GetValueID() == CSSValueNone)
-      return;
-
-    DEFINE_STATIC_REF(ComputedStyle, initial_style, CreateInitialStyle());
-    TransformOperations operations =
-        TransformBuilder::CreateTransformOperations(
-            *value, CSSToLengthConversionData(initial_style, initial_style,
-                                              LayoutViewItem(nullptr), 1.0f));
-
-    // Convert transform operations to a TransformationMatrix. This can fail
-    // if a param has a percentage ('%')
-    if (operations.DependsOnBoxSize())
-      exception_state.ThrowDOMException(kSyntaxError,
-                                        "The transformation depends on the box "
-                                        "size, which is not supported.");
-    matrix_ = TransformationMatrix::Create();
-    operations.Apply(FloatSize(0, 0), *matrix_);
-  } else {  // There is something there but parsing failed.
-    exception_state.ThrowDOMException(kSyntaxError,
-                                      "Failed to parse '" + string + "'.");
-  }
-}
-
-// Perform a concatenation of the matrices (this * secondMatrix)
-CSSMatrix* CSSMatrix::multiply(CSSMatrix* second_matrix) const {
-  if (!second_matrix)
-    return nullptr;
-
-  return CSSMatrix::Create(
-      TransformationMatrix(*matrix_).Multiply(*second_matrix->matrix_));
-}
-
-CSSMatrix* CSSMatrix::inverse(ExceptionState& exception_state) const {
-  if (!matrix_->IsInvertible()) {
-    exception_state.ThrowDOMException(kNotSupportedError,
-                                      "The matrix is not invertable.");
-    return nullptr;
-  }
-
-  return CSSMatrix::Create(matrix_->Inverse());
-}
-
-CSSMatrix* CSSMatrix::translate(double x, double y, double z) const {
-  if (std::isnan(x))
-    x = 0;
-  if (std::isnan(y))
-    y = 0;
-  if (std::isnan(z))
-    z = 0;
-  return CSSMatrix::Create(TransformationMatrix(*matrix_).Translate3d(x, y, z));
-}
-
-CSSMatrix* CSSMatrix::scale(double scale_x,
-                            double scale_y,
-                            double scale_z) const {
-  if (std::isnan(scale_x))
-    scale_x = 1;
-  if (std::isnan(scale_y))
-    scale_y = scale_x;
-  if (std::isnan(scale_z))
-    scale_z = 1;
-  return CSSMatrix::Create(
-      TransformationMatrix(*matrix_).Scale3d(scale_x, scale_y, scale_z));
-}
-
-CSSMatrix* CSSMatrix::rotate(double rot_x, double rot_y, double rot_z) const {
-  if (std::isnan(rot_x))
-    rot_x = 0;
-
-  if (std::isnan(rot_y) && std::isnan(rot_z)) {
-    rot_z = rot_x;
-    rot_x = 0;
-    rot_y = 0;
-  }
-
-  if (std::isnan(rot_y))
-    rot_y = 0;
-  if (std::isnan(rot_z))
-    rot_z = 0;
-  return CSSMatrix::Create(
-      TransformationMatrix(*matrix_).Rotate3d(rot_x, rot_y, rot_z));
-}
-
-CSSMatrix* CSSMatrix::rotateAxisAngle(double x,
-                                      double y,
-                                      double z,
-                                      double angle) const {
-  if (std::isnan(x))
-    x = 0;
-  if (std::isnan(y))
-    y = 0;
-  if (std::isnan(z))
-    z = 0;
-  if (std::isnan(angle))
-    angle = 0;
-  if (!x && !y && !z)
-    z = 1;
-  return CSSMatrix::Create(
-      TransformationMatrix(*matrix_).Rotate3d(x, y, z, angle));
-}
-
-CSSMatrix* CSSMatrix::skewX(double angle) const {
-  if (std::isnan(angle))
-    angle = 0;
-  return CSSMatrix::Create(TransformationMatrix(*matrix_).SkewX(angle));
-}
-
-CSSMatrix* CSSMatrix::skewY(double angle) const {
-  if (std::isnan(angle))
-    angle = 0;
-  return CSSMatrix::Create(TransformationMatrix(*matrix_).SkewY(angle));
-}
-
-String CSSMatrix::toString() const {
-  // FIXME - Need to ensure valid CSS floating point values
-  // (https://bugs.webkit.org/show_bug.cgi?id=20674)
-  if (matrix_->IsAffine())
-    return String::Format("matrix(%g, %g, %g, %g, %g, %g)", matrix_->A(),
-                          matrix_->B(), matrix_->C(), matrix_->D(),
-                          matrix_->E(), matrix_->F());
-  return String::Format(
-      "matrix3d(%g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, "
-      "%g)",
-      matrix_->M11(), matrix_->M12(), matrix_->M13(), matrix_->M14(),
-      matrix_->M21(), matrix_->M22(), matrix_->M23(), matrix_->M24(),
-      matrix_->M31(), matrix_->M32(), matrix_->M33(), matrix_->M34(),
-      matrix_->M41(), matrix_->M42(), matrix_->M43(), matrix_->M44());
-}
-
-}  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/CSSMatrix.h b/third_party/WebKit/Source/core/css/CSSMatrix.h
deleted file mode 100644
index 1c25efb2..0000000
--- a/third_party/WebKit/Source/core/css/CSSMatrix.h
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2008 Apple 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:
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
- */
-
-#ifndef CSSMatrix_h
-#define CSSMatrix_h
-
-#include <memory>
-#include "platform/bindings/ScriptWrappable.h"
-#include "platform/transforms/TransformationMatrix.h"
-#include "platform/wtf/text/WTFString.h"
-
-namespace blink {
-
-class ExceptionState;
-class ExecutionContext;
-
-class CSSMatrix final : public GarbageCollectedFinalized<CSSMatrix>,
-                        public ScriptWrappable {
-  DEFINE_WRAPPERTYPEINFO();
-
- public:
-  static CSSMatrix* Create(const TransformationMatrix& m) {
-    return new CSSMatrix(m);
-  }
-  static CSSMatrix* Create(ExecutionContext*, const String&, ExceptionState&);
-
-  double a() const { return matrix_->A(); }
-  double b() const { return matrix_->B(); }
-  double c() const { return matrix_->C(); }
-  double d() const { return matrix_->D(); }
-  double e() const { return matrix_->E(); }
-  double f() const { return matrix_->F(); }
-
-  void setA(double f) { matrix_->SetA(f); }
-  void setB(double f) { matrix_->SetB(f); }
-  void setC(double f) { matrix_->SetC(f); }
-  void setD(double f) { matrix_->SetD(f); }
-  void setE(double f) { matrix_->SetE(f); }
-  void setF(double f) { matrix_->SetF(f); }
-
-  double m11() const { return matrix_->M11(); }
-  double m12() const { return matrix_->M12(); }
-  double m13() const { return matrix_->M13(); }
-  double m14() const { return matrix_->M14(); }
-  double m21() const { return matrix_->M21(); }
-  double m22() const { return matrix_->M22(); }
-  double m23() const { return matrix_->M23(); }
-  double m24() const { return matrix_->M24(); }
-  double m31() const { return matrix_->M31(); }
-  double m32() const { return matrix_->M32(); }
-  double m33() const { return matrix_->M33(); }
-  double m34() const { return matrix_->M34(); }
-  double m41() const { return matrix_->M41(); }
-  double m42() const { return matrix_->M42(); }
-  double m43() const { return matrix_->M43(); }
-  double m44() const { return matrix_->M44(); }
-
-  void setM11(double f) { matrix_->SetM11(f); }
-  void setM12(double f) { matrix_->SetM12(f); }
-  void setM13(double f) { matrix_->SetM13(f); }
-  void setM14(double f) { matrix_->SetM14(f); }
-  void setM21(double f) { matrix_->SetM21(f); }
-  void setM22(double f) { matrix_->SetM22(f); }
-  void setM23(double f) { matrix_->SetM23(f); }
-  void setM24(double f) { matrix_->SetM24(f); }
-  void setM31(double f) { matrix_->SetM31(f); }
-  void setM32(double f) { matrix_->SetM32(f); }
-  void setM33(double f) { matrix_->SetM33(f); }
-  void setM34(double f) { matrix_->SetM34(f); }
-  void setM41(double f) { matrix_->SetM41(f); }
-  void setM42(double f) { matrix_->SetM42(f); }
-  void setM43(double f) { matrix_->SetM43(f); }
-  void setM44(double f) { matrix_->SetM44(f); }
-
-  void setMatrixValue(const String&, ExceptionState&);
-
-  // The following math function return a new matrix with the
-  // specified operation applied. The this value is not modified.
-
-  // Multiply this matrix by secondMatrix, on the right
-  // (result = this * secondMatrix)
-  CSSMatrix* multiply(CSSMatrix* second_matrix) const;
-
-  // Return the inverse of this matrix. Throw an exception if the matrix is not
-  // invertible.
-  CSSMatrix* inverse(ExceptionState&) const;
-
-  // Return this matrix translated by the passed values.
-  // Passing a NaN will use a value of 0. This allows the 3D form to used for 2D
-  // operations.
-  // Operation is performed as though the this matrix is multiplied by a matrix
-  // with the translation values on the left
-  // (result = translation(x,y,z) * this)
-  CSSMatrix* translate(double x, double y, double z) const;
-
-  // Returns this matrix scaled by the passed values.
-  // Passing scaleX or scaleZ as NaN uses a value of 1, but passing scaleY of
-  // NaN makes it the same as scaleX. This allows the 3D form to used for 2D
-  // operations Operation is performed as though the this matrix is multiplied
-  // by a matrix with the scale values on the left
-  // (result = scale(x,y,z) * this)
-  CSSMatrix* scale(double scale_x, double scale_y, double scale_z) const;
-
-  // Returns this matrix rotated by the passed values.
-  // If rotY and rotZ are NaN, rotate about Z (rotX=0, rotateY=0, rotateZ=rotX).
-  // Otherwise use a rotation value of 0 for any passed NaN.
-  // Operation is performed as though the this matrix is multiplied by a matrix
-  // with the rotation values on the left (result = rotation(x,y,z) * this)
-  CSSMatrix* rotate(double rot_x, double rot_y, double rot_z) const;
-
-  // Returns this matrix rotated about the passed axis by the passed angle.
-  // Passing a NaN will use a value of 0. If the axis is (0,0,0) use a value
-  // Operation is performed as though the this matrix is multiplied by a matrix
-  // with the rotation values on the left
-  // (result = rotation(x,y,z,angle) * this)
-  CSSMatrix* rotateAxisAngle(double x, double y, double z, double angle) const;
-
-  // Return this matrix skewed along the X axis by the passed values.
-  // Passing a NaN will use a value of 0.
-  // Operation is performed as though the this matrix is multiplied by a matrix
-  // with the skew values on the left (result = skewX(angle) * this)
-  CSSMatrix* skewX(double angle) const;
-
-  // Return this matrix skewed along the Y axis by the passed values.
-  // Passing a NaN will use a value of 0.
-  // Operation is performed as though the this matrix is multiplied by a matrix
-  // with the skew values on the left (result = skewY(angle) * this)
-  CSSMatrix* skewY(double angle) const;
-
-  const TransformationMatrix& Transform() const { return *matrix_; }
-
-  String toString() const;
-
-  DEFINE_INLINE_TRACE() {}
-
- protected:
-  CSSMatrix(const TransformationMatrix&);
-  CSSMatrix(const String&, ExceptionState&);
-
-  // TransformationMatrix needs to be 16-byte aligned. PartitionAlloc
-  // supports 16-byte alignment but Oilpan doesn't. So we use an std::unique_ptr
-  // to allocate TransformationMatrix on PartitionAlloc.
-  // TODO(oilpan): Oilpan should support 16-byte aligned allocations.
-  std::unique_ptr<TransformationMatrix> matrix_;
-};
-
-}  // namespace blink
-
-#endif  // CSSMatrix_h
diff --git a/third_party/WebKit/Source/core/css/WebKitCSSMatrix.idl b/third_party/WebKit/Source/core/css/WebKitCSSMatrix.idl
deleted file mode 100644
index d9227cf0..0000000
--- a/third_party/WebKit/Source/core/css/WebKitCSSMatrix.idl
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2008, 2010 Apple 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:
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
- */
-
-// Introduced in DOM Level ?:
-[
-    Constructor(optional DOMString cssValue = null),
-    ConstructorCallWith=ExecutionContext,
-    ImplementedAs=CSSMatrix,
-    RaisesException=Constructor,
-] interface WebKitCSSMatrix {
-
-    // These attributes are simple aliases for certain elements of the 4x4 matrix
-    attribute unrestricted double a; // alias for m11
-    attribute unrestricted double b; // alias for m12
-    attribute unrestricted double c; // alias for m21
-    attribute unrestricted double d; // alias for m22
-    attribute unrestricted double e; // alias for m41
-    attribute unrestricted double f; // alias for m42
-
-    attribute unrestricted double m11;
-    attribute unrestricted double m12;
-    attribute unrestricted double m13;
-    attribute unrestricted double m14;
-    attribute unrestricted double m21;
-    attribute unrestricted double m22;
-    attribute unrestricted double m23;
-    attribute unrestricted double m24;
-    attribute unrestricted double m31;
-    attribute unrestricted double m32;
-    attribute unrestricted double m33;
-    attribute unrestricted double m34;
-    attribute unrestricted double m41;
-    attribute unrestricted double m42;
-    attribute unrestricted double m43;
-    attribute unrestricted double m44;
-
-    [RaisesException, MeasureAs=WebkitCSSMatrixSetMatrixValue] void setMatrixValue([Default=Undefined] optional DOMString string);
-
-    // Multiply this matrix by secondMatrix, on the right (result = this * secondMatrix)
-    [LegacyInterfaceTypeChecking] WebKitCSSMatrix multiply([Default=Undefined] optional WebKitCSSMatrix secondMatrix);
-
-    // Return the inverse of this matrix. Throw an exception if the matrix is not invertible
-    [RaisesException] WebKitCSSMatrix inverse();
-
-    // Return this matrix translated by the passed values.
-    // Passing a NaN will use a value of 0. This allows the 3D form to used for 2D operations
-    WebKitCSSMatrix translate([Default=Undefined] optional unrestricted double x,
-                              [Default=Undefined] optional unrestricted double y,
-                              [Default=Undefined] optional unrestricted double z);
-
-    // Returns this matrix scaled by the passed values.
-    // Passing scaleX or scaleZ as NaN uses a value of 1, but passing scaleY of NaN
-    // makes it the same as scaleX. This allows the 3D form to used for 2D operations
-    WebKitCSSMatrix scale([Default=Undefined] optional unrestricted double scaleX,
-                          [Default=Undefined] optional unrestricted double scaleY,
-                          [Default=Undefined] optional unrestricted double scaleZ);
-
-    // Returns this matrix rotated by the passed values.
-    // If rotY and rotZ are NaN, rotate about Z (rotX=0, rotateY=0, rotateZ=rotX).
-    // Otherwise use a rotation value of 0 for any passed NaN.
-    WebKitCSSMatrix rotate([Default=Undefined] optional unrestricted double rotX,
-                           [Default=Undefined] optional unrestricted double rotY,
-                           [Default=Undefined] optional unrestricted double rotZ);
-
-    // Returns this matrix rotated about the passed axis by the passed angle.
-    // Passing a NaN will use a value of 0. If the axis is (0,0,0) use a value
-    // of (0,0,1).
-    WebKitCSSMatrix rotateAxisAngle([Default=Undefined] optional unrestricted double x,
-                                    [Default=Undefined] optional unrestricted double y,
-                                    [Default=Undefined] optional unrestricted double z,
-                                    [Default=Undefined] optional unrestricted double angle);
-
-    // Returns this matrix skewed along the X axis by the passed values.
-    // Passing a NaN will use a value of 0.
-    WebKitCSSMatrix skewX([Default=Undefined] optional unrestricted double angle);
-
-    // Returns this matrix skewed along the Y axis by the passed values.
-    // Passing a NaN will use a value of 0.
-    WebKitCSSMatrix skewY([Default=Undefined] optional unrestricted double angle);
-
-    [NotEnumerable] stringifier;
-};
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index bcf1820..61e9ade 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -4930,20 +4930,27 @@
     return;
   }
 
-  OriginAccessEntry access_entry(GetSecurityOrigin()->Protocol(), new_domain,
-                                 OriginAccessEntry::kAllowSubdomains);
-  OriginAccessEntry::MatchResult result =
-      access_entry.MatchesOrigin(*GetSecurityOrigin());
-  if (result == OriginAccessEntry::kDoesNotMatchOrigin) {
-    exception_state.ThrowSecurityError(
-        "'" + new_domain + "' is not a suffix of '" + domain() + "'.");
-    return;
-  }
+  // TODO(mkwst): If we decide to ship this, change the IDL file to make the
+  // value nullable (via `TreatNullAs=NullString`, for example). For the moment,
+  // just rely on JavaScript's inherent nuttiness for implicit conversion to the
+  // string "null". https://crbug.com/733150
+  if (!RuntimeEnabledFeatures::NullableDocumentDomainEnabled() ||
+      new_domain != "null") {
+    OriginAccessEntry access_entry(GetSecurityOrigin()->Protocol(), new_domain,
+                                   OriginAccessEntry::kAllowSubdomains);
+    OriginAccessEntry::MatchResult result =
+        access_entry.MatchesOrigin(*GetSecurityOrigin());
+    if (result == OriginAccessEntry::kDoesNotMatchOrigin) {
+      exception_state.ThrowSecurityError(
+          "'" + new_domain + "' is not a suffix of '" + domain() + "'.");
+      return;
+    }
 
-  if (result == OriginAccessEntry::kMatchesOriginButIsPublicSuffix) {
-    exception_state.ThrowSecurityError("'" + new_domain +
-                                       "' is a top-level domain.");
-    return;
+    if (result == OriginAccessEntry::kMatchesOriginButIsPublicSuffix) {
+      exception_state.ThrowSecurityError("'" + new_domain +
+                                         "' is a top-level domain.");
+      return;
+    }
   }
 
   if (frame_) {
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.cpp b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
index 6e6ca09..214a622 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrame.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
@@ -458,6 +458,18 @@
   // Notify ScriptController that the frame is closing, since its cleanup ends
   // up calling back to LocalFrameClient via WindowProxy.
   GetScriptController().ClearForClose();
+
+  // TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout crashes.
+  // It seems to crash because Frame is detached before LocalFrameView.
+  // Verify here that any LocalFrameView has been detached by now.
+  CHECK(!view_->IsAttached());
+  if (HTMLFrameOwnerElement* owner = DeprecatedLocalOwner()) {
+    if (EmbeddedContentView* owner_view = owner->OwnedEmbeddedContentView()) {
+      CHECK(!owner_view->IsAttached());
+      CHECK_EQ(owner_view, view_);
+    }
+  }
+
   SetView(nullptr);
 
   page_->GetEventHandlerRegistry().DidRemoveAllEventHandlers(*DomWindow());
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
index bdbd0a6..2da8988 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
@@ -241,6 +241,7 @@
 
 DEFINE_TRACE(LocalFrameView) {
   visitor->Trace(frame_);
+  visitor->Trace(parent_);
   visitor->Trace(fragment_anchor_);
   visitor->Trace(scrollable_areas_);
   visitor->Trace(animating_scrollable_areas_);
@@ -3884,9 +3885,17 @@
 }
 
 void LocalFrameView::AttachToLayout() {
-  DCHECK(!is_attached_);
+  CHECK(!is_attached_);
   is_attached_ = true;
-  if (ParentFrameView()->IsVisible())
+  parent_ = ParentFrameView();
+  if (!parent_) {
+    Frame* parent_frame = frame_->Tree().Parent();
+    CHECK(parent_frame);
+    CHECK(parent_frame->IsLocalFrame());
+    CHECK(parent_frame->View());
+  }
+  CHECK(parent_);
+  if (parent_->IsVisible())
     SetParentVisible(true);
   UpdateParentScrollableAreaSet();
   SetupRenderThrottling();
@@ -3894,9 +3903,18 @@
 }
 
 void LocalFrameView::DetachFromLayout() {
-  DCHECK(is_attached_);
+  // TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout crashes.
+  CHECK(is_attached_);
+  LocalFrameView* parent = ParentFrameView();
+  if (!parent) {
+    Frame* parent_frame = frame_->Tree().Parent();
+    CHECK(parent_frame);
+    CHECK(parent_frame->IsLocalFrame());
+    CHECK(parent_frame->View());
+  }
+  CHECK(parent == parent_);
   if (!RuntimeEnabledFeatures::RootLayerScrollingEnabled())
-    ParentFrameView()->RemoveScrollableArea(this);
+    parent->RemoveScrollableArea(this);
   SetParentVisible(false);
   is_attached_ = false;
 }
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameView.h b/third_party/WebKit/Source/core/frame/LocalFrameView.h
index 9265043..fbc737c 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameView.h
+++ b/third_party/WebKit/Source/core/frame/LocalFrameView.h
@@ -1064,6 +1064,7 @@
   EmbeddedObjectSet part_update_set_;
 
   Member<LocalFrame> frame_;
+  Member<LocalFrameView> parent_;
 
   IntRect frame_rect_;
   bool is_attached_;
diff --git a/third_party/WebKit/Source/core/frame/Window.idl b/third_party/WebKit/Source/core/frame/Window.idl
index 3434eb3..f610832 100644
--- a/third_party/WebKit/Source/core/frame/Window.idl
+++ b/third_party/WebKit/Source/core/frame/Window.idl
@@ -204,6 +204,8 @@
 
     // https://w3c.github.io/webappsec/specs/powerfulfeatures/#monkey-patching-global-object
     readonly attribute boolean isSecureContext;
+
+    attribute DOMMatrixConstructor WebKitCSSMatrix;
 };
 
 // https://html.spec.whatwg.org/#transferable-objects
diff --git a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
index 7be507d..96ec2377 100644
--- a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
@@ -949,7 +949,11 @@
             GetSecurityOrigin())) {
       CountDeprecation(
           WebFeature::kRequestedSubresourceWithEmbeddedCredentials);
-      return true;
+
+      // TODO(mkwst): Remove the runtime check one way or the other once we're
+      // sure it's going to stick (or that it's not).
+      if (RuntimeEnabledFeatures::BlockCredentialedSubresourcesEnabled())
+        return true;
     }
   }
   return false;
diff --git a/third_party/WebKit/Source/core/loader/WorkerFetchContext.cpp b/third_party/WebKit/Source/core/loader/WorkerFetchContext.cpp
index f502d93..2faf3de 100644
--- a/third_party/WebKit/Source/core/loader/WorkerFetchContext.cpp
+++ b/third_party/WebKit/Source/core/loader/WorkerFetchContext.cpp
@@ -161,7 +161,11 @@
     if (Url().User() != url.User() || Url().Pass() != url.Pass()) {
       CountDeprecation(
           WebFeature::kRequestedSubresourceWithEmbeddedCredentials);
-      return true;
+
+      // TODO(mkwst): Remove the runtime check one way or the other once we're
+      // sure it's going to stick (or that it's not).
+      if (RuntimeEnabledFeatures::BlockCredentialedSubresourcesEnabled())
+        return true;
     }
   }
   return false;
diff --git a/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.cpp b/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.cpp
index 7f5f3a37..df25107b 100644
--- a/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.cpp
+++ b/third_party/WebKit/Source/modules/shapedetection/BarcodeDetector.cpp
@@ -54,15 +54,15 @@
     HeapVector<Point2D> corner_points;
     for (const auto& corner_point : barcode->corner_points) {
       Point2D point;
-      point.setX(corner_point->x);
-      point.setY(corner_point->y);
+      point.setX(corner_point.x);
+      point.setY(corner_point.y);
       corner_points.push_back(point);
     }
     detected_barcodes.push_back(DetectedBarcode::Create(
         barcode->raw_value,
-        DOMRect::Create(barcode->bounding_box->x, barcode->bounding_box->y,
-                        barcode->bounding_box->width,
-                        barcode->bounding_box->height),
+        DOMRect::Create(barcode->bounding_box.x, barcode->bounding_box.y,
+                        barcode->bounding_box.width,
+                        barcode->bounding_box.height),
         corner_points));
   }
 
diff --git a/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp b/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp
index 2f5c485..148fed7 100644
--- a/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp
+++ b/third_party/WebKit/Source/modules/shapedetection/FaceDetector.cpp
@@ -65,8 +65,8 @@
     HeapVector<Landmark> landmarks;
     for (const auto& landmark : face->landmarks) {
       Point2D location;
-      location.setX(landmark->location->x);
-      location.setY(landmark->location->y);
+      location.setX(landmark->location.x);
+      location.setY(landmark->location.y);
       Landmark web_landmark;
       web_landmark.setLocation(location);
       if (landmark->type == shape_detection::mojom::blink::LandmarkType::EYE) {
@@ -79,8 +79,8 @@
     }
 
     detected_faces.push_back(DetectedFace::Create(
-        DOMRect::Create(face->bounding_box->x, face->bounding_box->y,
-                        face->bounding_box->width, face->bounding_box->height),
+        DOMRect::Create(face->bounding_box.x, face->bounding_box.y,
+                        face->bounding_box.width, face->bounding_box.height),
         landmarks));
   }
 
diff --git a/third_party/WebKit/Source/modules/shapedetection/TextDetector.cpp b/third_party/WebKit/Source/modules/shapedetection/TextDetector.cpp
index 1d6ad27..45e49a0 100644
--- a/third_party/WebKit/Source/modules/shapedetection/TextDetector.cpp
+++ b/third_party/WebKit/Source/modules/shapedetection/TextDetector.cpp
@@ -51,9 +51,8 @@
   for (const auto& text : text_detection_results) {
     detected_text.push_back(DetectedText::Create(
         text->raw_value,
-        DOMRect::Create(text->bounding_box->x, text->bounding_box->y,
-                        text->bounding_box->width,
-                        text->bounding_box->height)));
+        DOMRect::Create(text->bounding_box.x, text->bounding_box.y,
+                        text->bounding_box.width, text->bounding_box.height)));
   }
 
   resolver->Resolve(detected_text);
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5 b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
index 820ce875..702918c 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
@@ -116,6 +116,10 @@
       status: "test",
     },
     {
+      name: "BlockCredentialedSubresources",
+      status: "stable",
+    },
+    {
       name: "BlockLegacySubresources",
       status: "stable",
     },
@@ -725,6 +729,10 @@
       status: "stable",
     },
     {
+      name: "NullableDocumentDomain",
+      status: "experimental",
+    },
+    {
       name: "OffMainThreadFetch",
     },
     {
diff --git a/third_party/WebKit/Source/platform/mojo/Geometry.typemap b/third_party/WebKit/Source/platform/mojo/Geometry.typemap
index 3e1e17d..4b35c2f 100644
--- a/third_party/WebKit/Source/platform/mojo/Geometry.typemap
+++ b/third_party/WebKit/Source/platform/mojo/Geometry.typemap
@@ -3,7 +3,11 @@
 # found in the LICENSE file.
 
 mojom = "//ui/gfx/geometry/mojo/geometry.mojom"
-public_headers = [ "//third_party/WebKit/public/platform/WebSize.h" ]
+public_headers = [
+  "//third_party/WebKit/public/platform/WebFloatRect.h",
+  "//third_party/WebKit/public/platform/WebFloatPoint.h",
+  "//third_party/WebKit/public/platform/WebSize.h",
+]
 traits_headers = [
   "//third_party/WebKit/Source/platform/mojo/GeometryStructTraits.h",
   "//ui/gfx/geometry/mojo/geometry_struct_traits.h",
@@ -17,4 +21,8 @@
 
 # TODO(zqzhang): ideally, gfx.mojom.Size should be mapped into ::blink::IntSize.
 # However that introduces an link issue on Windows. See https://crbug.com/653323
-type_mappings = [ "gfx.mojom.Size=::blink::WebSize" ]
+type_mappings = [
+  "gfx.mojom.PointF=::blink::WebFloatPoint",
+  "gfx.mojom.RectF=::blink::WebFloatRect",
+  "gfx.mojom.Size=::blink::WebSize",
+]
diff --git a/third_party/WebKit/Source/platform/mojo/GeometryStructTraits.cpp b/third_party/WebKit/Source/platform/mojo/GeometryStructTraits.cpp
index 4ec1c2a..fbc1a81 100644
--- a/third_party/WebKit/Source/platform/mojo/GeometryStructTraits.cpp
+++ b/third_party/WebKit/Source/platform/mojo/GeometryStructTraits.cpp
@@ -7,6 +7,28 @@
 namespace mojo {
 
 // static
+bool StructTraits<gfx::mojom::RectFDataView, ::blink::WebFloatRect>::Read(
+    gfx::mojom::RectFDataView data,
+    ::blink::WebFloatRect* out) {
+  if (data.width() < 0 || data.height() < 0)
+    return false;
+  out->x = data.x();
+  out->y = data.y();
+  out->width = data.width();
+  out->height = data.height();
+  return true;
+}
+
+// static
+bool StructTraits<gfx::mojom::PointFDataView, ::blink::WebFloatPoint>::Read(
+    gfx::mojom::PointFDataView data,
+    ::blink::WebFloatPoint* out) {
+  out->x = data.x();
+  out->y = data.y();
+  return true;
+}
+
+// static
 bool StructTraits<gfx::mojom::SizeDataView, ::blink::WebSize>::Read(
     gfx::mojom::SizeDataView data,
     ::blink::WebSize* out) {
diff --git a/third_party/WebKit/Source/platform/mojo/GeometryStructTraits.h b/third_party/WebKit/Source/platform/mojo/GeometryStructTraits.h
index b88c3b2..21d4a8f 100644
--- a/third_party/WebKit/Source/platform/mojo/GeometryStructTraits.h
+++ b/third_party/WebKit/Source/platform/mojo/GeometryStructTraits.h
@@ -5,12 +5,30 @@
 #ifndef GeometryStructTraits_h
 #define GeometryStructTraits_h
 
+#include "third_party/WebKit/public/platform/WebFloatPoint.h"
+#include "third_party/WebKit/public/platform/WebFloatRect.h"
 #include "third_party/WebKit/public/platform/WebSize.h"
 #include "ui/gfx/geometry/mojo/geometry.mojom-shared.h"
 
 namespace mojo {
 
 template <>
+struct StructTraits<gfx::mojom::PointFDataView, ::blink::WebFloatPoint> {
+  static float x(const ::blink::WebFloatPoint& point) { return point.x; }
+  static float y(const ::blink::WebFloatPoint& point) { return point.y; }
+  static bool Read(gfx::mojom::PointFDataView, ::blink::WebFloatPoint* out);
+};
+
+template <>
+struct StructTraits<gfx::mojom::RectFDataView, ::blink::WebFloatRect> {
+  static float x(const ::blink::WebFloatRect& rect) { return rect.x; }
+  static float y(const ::blink::WebFloatRect& rect) { return rect.y; }
+  static float width(const ::blink::WebFloatRect& rect) { return rect.width; }
+  static float height(const ::blink::WebFloatRect& rect) { return rect.height; }
+  static bool Read(gfx::mojom::RectFDataView, ::blink::WebFloatRect* out);
+};
+
+template <>
 struct StructTraits<gfx::mojom::SizeDataView, ::blink::WebSize> {
   static int width(const ::blink::WebSize& size) { return size.width; }
   static int height(const ::blink::WebSize& size) { return size.height; }
diff --git a/third_party/WebKit/Source/platform/mojo/GeometryStructTraitsTest.cpp b/third_party/WebKit/Source/platform/mojo/GeometryStructTraitsTest.cpp
index 9a48f27f..569c7c7 100644
--- a/third_party/WebKit/Source/platform/mojo/GeometryStructTraitsTest.cpp
+++ b/third_party/WebKit/Source/platform/mojo/GeometryStructTraitsTest.cpp
@@ -33,9 +33,9 @@
     NOTREACHED();
   }
 
-  void EchoPointF(gfx::mojom::blink::PointFPtr, EchoPointFCallback) override {
-    // The type map is not specified.
-    NOTREACHED();
+  void EchoPointF(const WebFloatPoint& p,
+                  EchoPointFCallback callback) override {
+    std::move(callback).Run(p);
   }
 
   void EchoSize(const WebSize& s, EchoSizeCallback callback) override {
@@ -52,9 +52,8 @@
     NOTREACHED();
   }
 
-  void EchoRectF(gfx::mojom::blink::RectFPtr, EchoRectFCallback) override {
-    // The type map is not specified.
-    NOTREACHED();
+  void EchoRectF(const WebFloatRect& r, EchoRectFCallback callback) override {
+    std::move(callback).Run(r);
   }
 
   void EchoInsets(gfx::mojom::blink::InsetsPtr, EchoInsetsCallback) override {
@@ -100,4 +99,26 @@
   EXPECT_EQ(input, output);
 }
 
+TEST_F(GeometryStructTraitsTest, PointF) {
+  const float kX = 1.234;
+  const float kY = 5.678;
+  WebFloatPoint input(kX, kY);
+  gfx::mojom::blink::GeometryTraitsTestServicePtr proxy = GetTraitsTestProxy();
+  WebFloatPoint output;
+  proxy->EchoPointF(input, &output);
+  EXPECT_EQ(input, output);
+}
+
+TEST_F(GeometryStructTraitsTest, RectF) {
+  const float kX = 1.234;
+  const float kY = 2.345;
+  const float kWidth = 3.456;
+  const float kHeight = 4.567;
+  WebFloatRect input(kX, kY, kWidth, kHeight);
+  gfx::mojom::blink::GeometryTraitsTestServicePtr proxy = GetTraitsTestProxy();
+  WebFloatRect output;
+  proxy->EchoRectF(input, &output);
+  EXPECT_EQ(input, output);
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/weborigin/SecurityOrigin.cpp b/third_party/WebKit/Source/platform/weborigin/SecurityOrigin.cpp
index 1f96326..3ff0ba0 100644
--- a/third_party/WebKit/Source/platform/weborigin/SecurityOrigin.cpp
+++ b/third_party/WebKit/Source/platform/weborigin/SecurityOrigin.cpp
@@ -260,8 +260,12 @@
       if (host_ == other->host_ && port_ == other->port_)
         can_access = true;
     } else if (domain_was_set_in_dom_ && other->domain_was_set_in_dom_) {
-      if (domain_ == other->domain_)
+      // TODO(mkwst): If/when we ship this behavior, change this to check
+      // IsNull() rather than relying on string comparison.
+      // https://crbug.com/733150
+      if (domain_ == other->domain_ && domain_ != "null") {
         can_access = true;
+      }
     }
   }
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/common.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/common.py
index d6cbf3a..2260237 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/common.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/common.py
@@ -16,6 +16,7 @@
 WPT_GH_URL = 'https://github.com/%s/%s/' % (WPT_GH_ORG, WPT_GH_REPO_NAME)
 WPT_GH_SSH_URL_TEMPLATE = 'https://{}@github.com/%s/%s.git' % (WPT_GH_ORG, WPT_GH_REPO_NAME)
 WPT_REVISION_FOOTER = 'WPT-Export-Revision:'
+DEFAULT_COMMIT_HISTORY_WINDOW = 5000
 
 # TODO(qyearsley): This directory should be able to be constructed with
 # PathFinder and WPT_DEST_NAME, plus the string "external".
@@ -28,7 +29,7 @@
 _log = logging.getLogger(__name__)
 
 
-def exportable_commits_since(chromium_commit_hash, host, local_wpt):
+def _exportable_commits_since(chromium_commit_hash, host, local_wpt):
     """Lists exportable commits after a certain point.
 
     Args:
@@ -55,7 +56,7 @@
     return [commit for commit in chromium_commits if is_exportable(commit, local_wpt)]
 
 
-def exportable_commits_over_last_n_commits(number, host, local_wpt):
+def exportable_commits_over_last_n_commits(host, local_wpt, number=DEFAULT_COMMIT_HISTORY_WINDOW):
     """Lists exportable commits after a certain point.
 
     Args:
@@ -70,7 +71,7 @@
         A list of ChromiumCommit objects for commits that are exportable after
         the given commit, in chronological order.
     """
-    return exportable_commits_since('HEAD~{}'.format(number + 1), host, local_wpt)
+    return _exportable_commits_since('HEAD~{}'.format(number + 1), host, local_wpt)
 
 
 def is_exportable(chromium_commit, local_wpt):
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/common_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/common_unittest.py
index e3bfa4e..b1fc2c0 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/common_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/common_unittest.py
@@ -7,7 +7,7 @@
 from webkitpy.common.host_mock import MockHost
 from webkitpy.common.system.executive_mock import mock_git_commands
 from webkitpy.w3c.chromium_commit import ChromiumCommit
-from webkitpy.w3c.common import exportable_commits_since
+from webkitpy.w3c.common import _exportable_commits_since
 
 
 class MockLocalWPT(object):
@@ -31,7 +31,7 @@
             'footers': 'cr-rev-position',
         })
 
-        commits = exportable_commits_since('beefcafe', host, MockLocalWPT())
+        commits = _exportable_commits_since('beefcafe', host, MockLocalWPT())
         self.assertEqual(len(commits), 1)
         self.assertIsInstance(commits[0], ChromiumCommit)
         self.assertEqual(host.executive.calls, [
@@ -55,7 +55,7 @@
             'footers': 'cr-rev-position',
         })
 
-        commits = exportable_commits_since('add087a97844f4b9e307d9a216940582d96db306', host, MockLocalWPT())
+        commits = _exportable_commits_since('add087a97844f4b9e307d9a216940582d96db306', host, MockLocalWPT())
         self.assertEqual(commits, [])
         self.assertEqual(host.executive.calls, [
             ['git', 'rev-parse', '--show-toplevel'],
@@ -77,7 +77,7 @@
             'diff-tree': '',
         }, strict=True)
 
-        commits = exportable_commits_since('add087a97844f4b9e307d9a216940582d96db306', host, MockLocalWPT())
+        commits = _exportable_commits_since('add087a97844f4b9e307d9a216940582d96db306', host, MockLocalWPT())
         self.assertEqual(len(commits), 0)
         self.assertEqual(host.executive.calls, [
             ['git', 'rev-parse', '--show-toplevel'],
@@ -98,7 +98,7 @@
             'footers': 'cr-rev-position',
         })
 
-        commits = exportable_commits_since('beefcafe', host, MockLocalWPT())
+        commits = _exportable_commits_since('beefcafe', host, MockLocalWPT())
         self.assertEqual(commits, [])
         self.assertEqual(host.executive.calls, [
             ['git', 'rev-parse', '--show-toplevel'],
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier.py
index a9d84ee..3cf8b98 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_copier.py
@@ -92,9 +92,7 @@
             cur_dir = root.replace(self.dir_above_repo + '/', '') + '/'
             _log.debug('Scanning %s...', cur_dir)
 
-            # Files in 'tools' are not for browser testing, so we skip them.
-            # See: http://web-platform-tests.org/writing-tests/general-guidelines.html#tools
-            dirs_to_skip = ('.git', 'test-plan', 'tools')
+            dirs_to_skip = ('.git', 'test-plan')
 
             if dirs:
                 for name in dirs_to_skip:
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter.py
index be0d919..03764d6e 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter.py
@@ -16,7 +16,6 @@
 _log = logging.getLogger(__name__)
 
 PR_HISTORY_WINDOW = 100
-COMMIT_HISTORY_WINDOW = 5000
 
 
 class TestExporter(object):
@@ -39,7 +38,7 @@
         open_gerrit_cls = self.gerrit.query_exportable_open_cls()
         self.process_gerrit_cls(open_gerrit_cls)
 
-        exportable_commits = self.get_exportable_commits(limit=COMMIT_HISTORY_WINDOW)
+        exportable_commits = self.get_exportable_commits()
         for exportable_commit in exportable_commits:
             pull_request = self.corresponding_pull_request_for_commit(exportable_commit)
 
@@ -78,8 +77,8 @@
                 _log.info('No in-flight PR found for CL. Creating...')
                 self.create_or_update_pull_request_from_cl(cl)
 
-    def get_exportable_commits(self, limit):
-        return exportable_commits_over_last_n_commits(limit, self.host, self.local_wpt)
+    def get_exportable_commits(self):
+        return exportable_commits_over_last_n_commits(self.host, self.local_wpt)
 
     def merge_pull_request(self, pull_request):
         _log.info('In-flight PR found: %s', pull_request.title)
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter_unittest.py
index dbf6059..ff0a0cc 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_exporter_unittest.py
@@ -31,7 +31,7 @@
             PullRequest(title='title1', number=1234, body='', state='open'),
         ])
         test_exporter.gerrit = MockGerritAPI(host, 'gerrit-username', 'gerrit-token')
-        test_exporter.get_exportable_commits = lambda limit: [
+        test_exporter.get_exportable_commits = lambda: [
             ChromiumCommit(host, position='refs/heads/master@{#458475}'),
             ChromiumCommit(host, position='refs/heads/master@{#458476}'),
             ChromiumCommit(host, position='refs/heads/master@{#458477}'),
@@ -128,7 +128,7 @@
             ),
         ], unsuccessful_merge_index=0)
         test_exporter.gerrit = MockGerritAPI(host, 'gerrit-username', 'gerrit-token')
-        test_exporter.get_exportable_commits = lambda limit: [
+        test_exporter.get_exportable_commits = lambda: [
             ChromiumCommit(host, position='refs/heads/master@{#458475}'),
             ChromiumCommit(host, position='refs/heads/master@{#458476}'),
             ChromiumCommit(host, position='refs/heads/master@{#458477}'),
@@ -159,7 +159,7 @@
         test_exporter = TestExporter(host, 'gh-username', 'gh-token', gerrit_user=None,
                                      gerrit_token=None, dry_run=False)
         test_exporter.wpt_github = MockWPTGitHub(pull_requests=[])
-        test_exporter.get_exportable_commits = lambda limit: []
+        test_exporter.get_exportable_commits = lambda: []
         test_exporter.gerrit = MockGerritAPI(host, 'gerrit-username', 'gerrit-token')
         test_exporter.gerrit.get = lambda path, raw: base64.b64encode('sample diff')  # pylint: disable=unused-argument
         test_exporter.gerrit.query_exportable_open_cls = lambda: [
@@ -196,7 +196,7 @@
             PullRequest(title='title1', number=1234,
                         body='description\nWPT-Export-Revision: 1', state='open'),
         ])
-        test_exporter.get_exportable_commits = lambda limit: []
+        test_exporter.get_exportable_commits = lambda: []
         test_exporter.gerrit = MockGerritAPI(host, 'gerrit-username', 'gerrit-token')
         test_exporter.gerrit.query_exportable_open_cls = lambda: [
             GerritCL(data={
@@ -225,7 +225,7 @@
             PullRequest(title='title1', number=1234,
                         body='description\nWPT-Export-Revision: 1', state='open'),
         ])
-        test_exporter.get_exportable_commits = lambda limit: []
+        test_exporter.get_exportable_commits = lambda: []
         test_exporter.gerrit = MockGerritAPI(host, 'gerrit-username', 'gerrit-token')
         test_exporter.gerrit.get = lambda path, raw: base64.b64encode('sample diff')  # pylint: disable=unused-argument
         test_exporter.gerrit.query_exportable_open_cls = lambda: [
@@ -274,7 +274,7 @@
                         body='description\nWPT-Export-Revision: 9\nChange-Id: decafbad',
                         state='open'),
         ])
-        test_exporter.get_exportable_commits = lambda limit: [
+        test_exporter.get_exportable_commits = lambda: [
             ChromiumCommit(host, sha='c881563d734a86f7d9cd57ac509653a61c45c240'),
         ]
         test_exporter.gerrit = MockGerritAPI(host, 'gerrit-username', 'gerrit-token')
@@ -296,7 +296,7 @@
         test_exporter = TestExporter(host, 'gh-username', 'gh-token', gerrit_user=None,
                                      gerrit_token=None, dry_run=False)
         test_exporter.wpt_github = MockWPTGitHub(pull_requests=[])
-        test_exporter.get_exportable_commits = lambda limit: []
+        test_exporter.get_exportable_commits = lambda: []
         test_exporter.gerrit = MockGerritAPI(host, 'gerrit-username', 'gerrit-token')
         test_exporter.gerrit.get = lambda path, raw: base64.b64encode('sample diff')  # pylint: disable=unused-argument
         test_exporter.gerrit.query_exportable_open_cls = lambda: [
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py
index 07ac7033..0c546a17 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py
@@ -19,7 +19,7 @@
 from webkitpy.common.path_finder import PathFinder
 from webkitpy.layout_tests.models.test_expectations import TestExpectations, TestExpectationParser
 from webkitpy.layout_tests.port.base import Port
-from webkitpy.w3c.common import WPT_REPO_URL, WPT_DEST_NAME, exportable_commits_since
+from webkitpy.w3c.common import WPT_REPO_URL, WPT_DEST_NAME, exportable_commits_over_last_n_commits
 from webkitpy.w3c.directory_owners_extractor import DirectoryOwnersExtractor
 from webkitpy.w3c.local_wpt import LocalWPT
 from webkitpy.w3c.test_copier import TestCopier
@@ -156,10 +156,9 @@
             A list of commits in the Chromium repo that are exportable
             but not yet exported to the web-platform-tests repo.
         """
-        local_wpt = LocalWPT(self.host, path=wpt_path)
         assert self.host.filesystem.exists(wpt_path)
-        _, chromium_commit = local_wpt.most_recent_chromium_commit()
-        return exportable_commits_since(chromium_commit.sha, self.host, local_wpt)
+        local_wpt = LocalWPT(self.host, path=wpt_path)
+        return exportable_commits_over_last_n_commits(self.host, local_wpt)
 
     def clean_up_temp_repo(self, temp_repo_path):
         """Removes the temporary copy of the wpt repo that was downloaded."""
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index 88049bc..9618664 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crashpad
 URL: https://crashpad.chromium.org/
 Version: unknown
-Revision: 7d56fd2386067a26878324ab48314e58e2b242ea
+Revision: 63ccbd0e4c2fa9e1f69c0b1e5f4627ad66dcc4f1
 License: Apache 2.0
 License File: crashpad/LICENSE
 Security Critical: yes
diff --git a/third_party/crashpad/crashpad/DEPS b/third_party/crashpad/crashpad/DEPS
index 0c7f66b..742430d 100644
--- a/third_party/crashpad/crashpad/DEPS
+++ b/third_party/crashpad/crashpad/DEPS
@@ -38,7 +38,7 @@
 
   'crashpad/third_party/mini_chromium/mini_chromium':
       Var('chromium_git') + '/chromium/mini_chromium@' +
-      'ef0ded8717340c9fe48e8e0f34f3e0e74d10a392',
+      '723e840a2f100a525f7feaad2e93df31d701780a',
   'crashpad/third_party/zlib/zlib':
       Var('chromium_git') + '/chromium/src/third_party/zlib@' +
       '13dc246a58e4b72104d35f9b1809af95221ebda7',
diff --git a/third_party/crashpad/crashpad/client/capture_context_mac_test.cc b/third_party/crashpad/crashpad/client/capture_context_mac_test.cc
index ca52961e..15640210 100644
--- a/third_party/crashpad/crashpad/client/capture_context_mac_test.cc
+++ b/third_party/crashpad/crashpad/client/capture_context_mac_test.cc
@@ -35,11 +35,15 @@
 // gtest assertions.
 void SanityCheckContext(const NativeCPUContext& context) {
 #if defined(ARCH_CPU_X86)
-  ASSERT_EQ(context.tsh.flavor, x86_THREAD_STATE32);
-  ASSERT_EQ(context.tsh.count, implicit_cast<int>(x86_THREAD_STATE32_COUNT));
+  ASSERT_EQ(implicit_cast<thread_state_flavor_t>(context.tsh.flavor),
+            implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE32));
+  ASSERT_EQ(implicit_cast<uint32_t>(context.tsh.count),
+            implicit_cast<uint32_t>(x86_THREAD_STATE32_COUNT));
 #elif defined(ARCH_CPU_X86_64)
-  ASSERT_EQ(context.tsh.flavor, x86_THREAD_STATE64);
-  ASSERT_EQ(context.tsh.count, implicit_cast<int>(x86_THREAD_STATE64_COUNT));
+  ASSERT_EQ(implicit_cast<thread_state_flavor_t>(context.tsh.flavor),
+            implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE64));
+  ASSERT_EQ(implicit_cast<uint32_t>(context.tsh.count),
+            implicit_cast<uint32_t>(x86_THREAD_STATE64_COUNT));
 #endif
 
 #if defined(ARCH_CPU_X86_FAMILY)
diff --git a/third_party/crashpad/crashpad/client/simulate_crash_mac.cc b/third_party/crashpad/crashpad/client/simulate_crash_mac.cc
index 7e279015..2786438 100644
--- a/third_party/crashpad/crashpad/client/simulate_crash_mac.cc
+++ b/third_party/crashpad/crashpad/client/simulate_crash_mac.cc
@@ -177,12 +177,12 @@
 
 void SimulateCrash(const NativeCPUContext& cpu_context) {
 #if defined(ARCH_CPU_X86)
-  DCHECK_EQ(cpu_context.tsh.flavor,
+  DCHECK_EQ(implicit_cast<thread_state_flavor_t>(cpu_context.tsh.flavor),
             implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE32));
   DCHECK_EQ(implicit_cast<mach_msg_type_number_t>(cpu_context.tsh.count),
             x86_THREAD_STATE32_COUNT);
 #elif defined(ARCH_CPU_X86_64)
-  DCHECK_EQ(cpu_context.tsh.flavor,
+  DCHECK_EQ(implicit_cast<thread_state_flavor_t>(cpu_context.tsh.flavor),
             implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE64));
   DCHECK_EQ(implicit_cast<mach_msg_type_number_t>(cpu_context.tsh.count),
             x86_THREAD_STATE64_COUNT);
diff --git a/third_party/crashpad/crashpad/client/simulate_crash_mac_test.cc b/third_party/crashpad/crashpad/client/simulate_crash_mac_test.cc
index de91ebb..c195352 100644
--- a/third_party/crashpad/crashpad/client/simulate_crash_mac_test.cc
+++ b/third_party/crashpad/crashpad/client/simulate_crash_mac_test.cc
@@ -130,12 +130,12 @@
               reinterpret_cast<const x86_thread_state*>(old_state);
           switch (state->tsh.flavor) {
             case x86_THREAD_STATE32:
-              EXPECT_EQ(state->tsh.count,
-                        implicit_cast<int>(x86_THREAD_STATE32_COUNT));
+              EXPECT_EQ(implicit_cast<uint32_t>(state->tsh.count),
+                        implicit_cast<uint32_t>(x86_THREAD_STATE32_COUNT));
               break;
             case x86_THREAD_STATE64:
-              EXPECT_EQ(state->tsh.count,
-                        implicit_cast<int>(x86_THREAD_STATE64_COUNT));
+              EXPECT_EQ(implicit_cast<uint32_t>(state->tsh.count),
+                        implicit_cast<uint32_t>(x86_THREAD_STATE64_COUNT));
               break;
             default:
               ADD_FAILURE() << "unexpected tsh.flavor " << state->tsh.flavor;
@@ -149,12 +149,12 @@
               reinterpret_cast<const x86_float_state*>(old_state);
           switch (state->fsh.flavor) {
             case x86_FLOAT_STATE32:
-              EXPECT_EQ(state->fsh.count,
-                        implicit_cast<int>(x86_FLOAT_STATE32_COUNT));
+              EXPECT_EQ(implicit_cast<uint32_t>(state->fsh.count),
+                        implicit_cast<uint32_t>(x86_FLOAT_STATE32_COUNT));
               break;
             case x86_FLOAT_STATE64:
-              EXPECT_EQ(state->fsh.count,
-                        implicit_cast<int>(x86_FLOAT_STATE64_COUNT));
+              EXPECT_EQ(implicit_cast<uint32_t>(state->fsh.count),
+                        implicit_cast<uint32_t>(x86_FLOAT_STATE64_COUNT));
               break;
             default:
               ADD_FAILURE() << "unexpected fsh.flavor " << state->fsh.flavor;
@@ -168,12 +168,12 @@
               reinterpret_cast<const x86_debug_state*>(old_state);
           switch (state->dsh.flavor) {
             case x86_DEBUG_STATE32:
-              EXPECT_EQ(state->dsh.count,
-                        implicit_cast<int>(x86_DEBUG_STATE32_COUNT));
+              EXPECT_EQ(implicit_cast<uint32_t>(state->dsh.count),
+                        implicit_cast<uint32_t>(x86_DEBUG_STATE32_COUNT));
               break;
             case x86_DEBUG_STATE64:
-              EXPECT_EQ(state->dsh.count,
-                        implicit_cast<int>(x86_DEBUG_STATE64_COUNT));
+              EXPECT_EQ(implicit_cast<uint32_t>(state->dsh.count),
+                        implicit_cast<uint32_t>(x86_DEBUG_STATE64_COUNT));
               break;
             default:
               ADD_FAILURE() << "unexpected dsh.flavor " << state->dsh.flavor;
diff --git a/third_party/crashpad/crashpad/compat/android/elf.h b/third_party/crashpad/crashpad/compat/android/elf.h
deleted file mode 100644
index a3fc2a9a..0000000
--- a/third_party/crashpad/crashpad/compat/android/elf.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef CRASHPAD_COMPAT_ANDROID_ELF_H_
-#define CRASHPAD_COMPAT_ANDROID_ELF_H_
-
-#include_next <elf.h>
-
-// Android 5.0.0 (API 21) NDK
-#if !defined(NT_PRSTATUS)
-#define NT_PRSTATUS 1
-#endif
-
-#endif  // CRASHPAD_COMPAT_ANDROID_ELF_H_
diff --git a/third_party/crashpad/crashpad/compat/android/linux/elf.h b/third_party/crashpad/crashpad/compat/android/linux/elf.h
new file mode 100644
index 0000000..e65d153
--- /dev/null
+++ b/third_party/crashpad/crashpad/compat/android/linux/elf.h
@@ -0,0 +1,38 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_ANDROID_LINUX_ELF_H_
+#define CRASHPAD_COMPAT_ANDROID_LINUX_ELF_H_
+
+#include_next <linux/elf.h>
+
+// Android 5.0.0 (API 21) NDK
+
+#if defined(__i386__) || defined(__x86_64__)
+#if !defined(NT_386_TLS)
+#define NT_386_TLS 0x200
+#endif
+#endif  // __i386__ || __x86_64__
+
+#if defined(__ARMEL__) || defined(__aarch64__)
+#if !defined(NT_ARM_VFP)
+#define NT_ARM_VFP 0x400
+#endif
+
+#if !defined(NT_ARM_TLS)
+#define NT_ARM_TLS 0x401
+#endif
+#endif  // __ARMEL__ || __aarch64__
+
+#endif  // CRASHPAD_COMPAT_ANDROID_LINUX_ELF_H_
diff --git a/third_party/crashpad/crashpad/compat/mac/AvailabilityMacros.h b/third_party/crashpad/crashpad/compat/mac/AvailabilityMacros.h
index 4037eca6..f105f94 100644
--- a/third_party/crashpad/crashpad/compat/mac/AvailabilityMacros.h
+++ b/third_party/crashpad/crashpad/compat/mac/AvailabilityMacros.h
@@ -53,4 +53,10 @@
 #define MAC_OS_X_VERSION_10_12 101200
 #endif
 
+// 10.13 SDK
+
+#ifndef MAC_OS_X_VERSION_10_13
+#define MAC_OS_X_VERSION_10_13 101300
+#endif
+
 #endif  // CRASHPAD_COMPAT_MAC_AVAILABILITYMACROS_H_
diff --git a/third_party/crashpad/crashpad/compat/non_win/dbghelp.h b/third_party/crashpad/crashpad/compat/non_win/dbghelp.h
index dedd8ad..a8d5a0f3 100644
--- a/third_party/crashpad/crashpad/compat/non_win/dbghelp.h
+++ b/third_party/crashpad/crashpad/compat/non_win/dbghelp.h
@@ -533,11 +533,11 @@
   uint32_t CheckSum;
 
   //! \brief The module’s timestamp, in `time_t` units, seconds since the POSIX
-  //!     epoch.
+  //!     epoch, or `0` if unknown.
   //!
   //! On Windows, this field comes from the `TimeDateStamp` field of the
-  //! module’s `IMAGE_HEADER` structure. It reflects the timestamp at the time
-  //! the module was linked.
+  //! module’s `IMAGE_FILE_HEADER` structure. It reflects the timestamp at the
+  //! time the module was linked.
   uint32_t TimeDateStamp;
 
   //! \brief ::RVA of a MINIDUMP_STRING containing the module’s path or file
@@ -723,11 +723,11 @@
   uint32_t CheckSum;
 
   //! \brief The module’s timestamp, in `time_t` units, seconds since the POSIX
-  //!     epoch.
+  //!     epoch, or `0` if unknown.
   //!
   //! On Windows, this field comes from the `TimeDateStamp` field of the
-  //! module’s `IMAGE_HEADER` structure. It reflects the timestamp at the time
-  //! the module was linked.
+  //! module’s `IMAGE_FILE_HEADER` structure. It reflects the timestamp at the
+  //! time the module was linked.
   uint32_t TimeDateStamp;
 
   //! \brief ::RVA of a MINIDUMP_STRING containing the module’s path or file
diff --git a/third_party/crashpad/crashpad/minidump/minidump_context.h b/third_party/crashpad/crashpad/minidump/minidump_context.h
index b71b57c..f558832f 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_context.h
+++ b/third_party/crashpad/crashpad/minidump/minidump_context.h
@@ -252,7 +252,7 @@
 //!     normally alias `dr6` and `dr7`, respectively. See Intel Software
 //!     Developer’s Manual, Volume 3B: System Programming, Part 2 (253669-052),
 //!     17.2.2 “Debug Registers DR4 and DR5”.
-struct ALIGNAS(16) MinidumpContextAMD64 {
+struct alignas(16) MinidumpContextAMD64 {
   //! \brief Register parameter home address.
   //!
   //! On Windows, this field may contain the “home” address (on-stack, in the
diff --git a/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options_test.cc b/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options_test.cc
index 53d4f62..697c14e 100644
--- a/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/crashpad_info_client_options_test.cc
@@ -21,6 +21,7 @@
 #include "client/crashpad_info.h"
 #include "gtest/gtest.h"
 #include "test/errors.h"
+#include "test/scoped_module_handle.h"
 #include "test/test_paths.h"
 
 #if defined(OS_MACOSX)
@@ -138,48 +139,6 @@
   }
 }
 
-#if defined(OS_POSIX)
-using DlHandle = void*;
-#elif defined(OS_WIN)
-using DlHandle = HMODULE;
-#endif  // OS_POSIX
-
-class ScopedDlHandle {
- public:
-  explicit ScopedDlHandle(DlHandle dl_handle)
-      : dl_handle_(dl_handle) {
-  }
-
-  ~ScopedDlHandle() {
-    if (dl_handle_) {
-#if defined(OS_POSIX)
-      if (dlclose(dl_handle_) != 0) {
-        LOG(ERROR) << "dlclose: " << dlerror();
-      }
-#elif defined(OS_WIN)
-      if (!FreeLibrary(dl_handle_))
-        PLOG(ERROR) << "FreeLibrary";
-#endif  // OS_POSIX
-    }
-  }
-
-  bool valid() const { return dl_handle_ != nullptr; }
-
-  template <typename T>
-  T LookUpSymbol(const char* symbol_name) {
-#if defined(OS_POSIX)
-    return reinterpret_cast<T>(dlsym(dl_handle_, symbol_name));
-#elif defined(OS_WIN)
-    return reinterpret_cast<T>(GetProcAddress(dl_handle_, symbol_name));
-#endif  // OS_POSIX
-  }
-
- private:
-  DlHandle dl_handle_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedDlHandle);
-};
-
 TEST(CrashpadInfoClientOptions, TwoModules) {
   // Open the module, which has its own CrashpadInfo structure.
 #if defined(OS_MACOSX)
@@ -190,15 +149,15 @@
   base::FilePath module_path = TestPaths::Executable().DirName().Append(
       FILE_PATH_LITERAL("crashpad_snapshot_test_module") + kDlExtension);
 #if defined(OS_MACOSX)
-  ScopedDlHandle dl_handle(
+  ScopedModuleHandle module(
       dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL));
-  ASSERT_TRUE(dl_handle.valid()) << "dlopen " << module_path.value() << ": "
-                                 << dlerror();
+  ASSERT_TRUE(module.valid()) << "dlopen " << module_path.value() << ": "
+                              << dlerror();
 #elif defined(OS_WIN)
-  ScopedDlHandle dl_handle(LoadLibrary(module_path.value().c_str()));
-  ASSERT_TRUE(dl_handle.valid())
-      << "LoadLibrary " << base::UTF16ToUTF8(module_path.value()) << ": "
-      << ErrorMessage();
+  ScopedModuleHandle module(LoadLibrary(module_path.value().c_str()));
+  ASSERT_TRUE(module.valid()) << "LoadLibrary "
+                              << base::UTF16ToUTF8(module_path.value()) << ": "
+                              << ErrorMessage();
 #else
 #error Port.
 #endif  // OS_MACOSX
@@ -207,7 +166,7 @@
   // because it runs in the module, it returns the remote module’s CrashpadInfo
   // structure.
   CrashpadInfo* (*TestModule_GetCrashpadInfo)() =
-      dl_handle.LookUpSymbol<CrashpadInfo* (*)()>("TestModule_GetCrashpadInfo");
+      module.LookUpSymbol<CrashpadInfo* (*)()>("TestModule_GetCrashpadInfo");
   ASSERT_TRUE(TestModule_GetCrashpadInfo);
 
   auto options = SelfProcessSnapshotAndGetCrashpadOptions();
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc
index d0b7a08..f16aa55 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader.cc
@@ -555,22 +555,14 @@
     return false;
   }
 
-  mach_vm_size_t vmsize = segment->vmsize();
-
   if (segment_name == SEG_TEXT) {
+    mach_vm_size_t vmsize = segment->vmsize();
+
     if (vmsize == 0) {
       LOG(WARNING) << "zero-sized " SEG_TEXT " segment" << load_command_info;
       return false;
     }
 
-    mach_vm_size_t fileoff = segment->fileoff();
-    if (fileoff != 0) {
-      LOG(WARNING) << base::StringPrintf(
-                          SEG_TEXT " segment has unexpected fileoff 0x%llx",
-                          fileoff) << load_command_info;
-      return false;
-    }
-
     size_ = vmsize;
 
     // The slide is computed as the difference between the __TEXT segment’s
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader_test.cc b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader_test.cc
index 6c545bc..6d6e49d 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_reader_test.cc
@@ -577,8 +577,7 @@
 
   // Now that all of the modules have been verified, make sure that dyld itself
   // can be read properly too.
-  const struct dyld_all_image_infos* dyld_image_infos =
-      _dyld_get_all_image_infos();
+  const dyld_all_image_infos* dyld_image_infos = DyldGetAllImageInfos();
   ASSERT_GE(dyld_image_infos->version, 1u);
   EXPECT_EQ(dyld_image_infos->infoArrayCount, count);
 
diff --git a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader.cc b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader.cc
index 9837a9c..06e1daf4 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/mach_o_image_segment_reader.cc
@@ -120,36 +120,29 @@
                                                   sections_.size(),
                                                   load_command_info.c_str());
 
-    if (section_segment_name != segment_name) {
-      // cl_kernels modules (for OpenCL) aren’t ld output, and they’re formatted
-      // incorrectly on OS X 10.10 and later. They have a single __TEXT segment,
-      // but one of the sections within it claims to belong to the __LD segment.
-      // This mismatch shouldn’t happen. This errant section also has the
-      // S_ATTR_DEBUG flag set, which shouldn’t happen unless all of the other
-      // sections in the segment also have this bit set (they don’t). These odd
-      // sections are reminiscent of unwind information stored in MH_OBJECT
-      // images, although cl_kernels images claim to be MH_BUNDLE. Because at
-      // least one cl_kernels module will commonly be found in a process, and
-      // sometimes more will be, tolerate this quirk.
-      //
-      // https://openradar.appspot.com/20239912
-      bool ok = false;
-      if (file_type == MH_BUNDLE && module_name == "cl_kernels") {
-        int mac_os_x_minor_version = MacOSXMinorVersion();
-        if ((mac_os_x_minor_version >= 10 && mac_os_x_minor_version <= 12) &&
-            segment_name == SEG_TEXT &&
-            section_segment_name == "__LD" &&
-            section_name == "__compact_unwind" &&
-            (section.flags & S_ATTR_DEBUG)) {
-          ok = true;
-        }
-      }
-
-      if (!ok) {
-        LOG(WARNING) << "section.segname incorrect in segment " << segment_name
-                     << section_info;
-        return false;
-      }
+    // cl_kernels modules (for OpenCL) aren’t ld output, and they’re formatted
+    // incorrectly on OS X 10.10 and later. They have a single __TEXT segment,
+    // but one of the sections within it claims to belong to the __LD segment.
+    // This mismatch shouldn’t happen. This errant section also has the
+    // S_ATTR_DEBUG flag set, which shouldn’t happen unless all of the other
+    // sections in the segment also have this bit set (they don’t). These odd
+    // sections are reminiscent of unwind information stored in MH_OBJECT
+    // images, although cl_kernels images claim to be MH_BUNDLE. Because at
+    // least one cl_kernels module will commonly be found in a process, and
+    // sometimes more will be, tolerate this quirk.
+    //
+    // https://openradar.appspot.com/20239912
+    if (section_segment_name != segment_name &&
+        !(file_type == MH_BUNDLE &&
+          module_name == "cl_kernels" &&
+          MacOSXMinorVersion() >= 10 &&
+          segment_name == SEG_TEXT &&
+          section_segment_name == "__LD" &&
+          section_name == "__compact_unwind" &&
+          (section.flags & S_ATTR_DEBUG))) {
+      LOG(WARNING) << "section.segname incorrect in segment " << segment_name
+                   << section_info;
+      return false;
     }
 
     CheckedMachAddressRange section_range(
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_reader.cc b/third_party/crashpad/crashpad/snapshot/mac/process_reader.cc
index 46083ab3fa..5e1f1c7 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_reader.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_reader.cc
@@ -198,6 +198,46 @@
   return modules_;
 }
 
+mach_vm_address_t ProcessReader::DyldAllImageInfo(
+    mach_vm_size_t* all_image_info_size) {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+  task_dyld_info_data_t dyld_info;
+  mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+  kern_return_t kr = task_info(
+      task_, TASK_DYLD_INFO, reinterpret_cast<task_info_t>(&dyld_info), &count);
+  if (kr != KERN_SUCCESS) {
+    MACH_LOG(WARNING, kr) << "task_info";
+    return 0;
+  }
+
+  // TODO(mark): Deal with statically linked executables which don’t use dyld.
+  // This may look for the module that matches the executable path in the same
+  // data set that vmmap uses.
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
+  // The task_dyld_info_data_t struct grew in 10.7, adding the format field.
+  // Don’t check this field if it’s not present, which can happen when either
+  // the SDK used at compile time or the kernel at run time are too old and
+  // don’t know about it.
+  if (count >= TASK_DYLD_INFO_COUNT) {
+    const integer_t kExpectedFormat =
+        !Is64Bit() ? TASK_DYLD_ALL_IMAGE_INFO_32 : TASK_DYLD_ALL_IMAGE_INFO_64;
+    if (dyld_info.all_image_info_format != kExpectedFormat) {
+      LOG(WARNING) << "unexpected task_dyld_info_data_t::all_image_info_format "
+                   << dyld_info.all_image_info_format;
+      DCHECK_EQ(dyld_info.all_image_info_format, kExpectedFormat);
+      return 0;
+    }
+  }
+#endif
+
+  if (all_image_info_size) {
+    *all_image_info_size = dyld_info.all_image_info_size;
+  }
+  return dyld_info.all_image_info_addr;
+}
+
 void ProcessReader::InitializeThreads() {
   DCHECK(!initialized_threads_);
   DCHECK(threads_.empty());
@@ -345,38 +385,12 @@
 
   initialized_modules_ = true;
 
-  task_dyld_info_data_t dyld_info;
-  mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
-  kern_return_t kr = task_info(
-      task_, TASK_DYLD_INFO, reinterpret_cast<task_info_t>(&dyld_info), &count);
-  if (kr != KERN_SUCCESS) {
-    MACH_LOG(WARNING, kr) << "task_info";
-    return;
-  }
-
-  // TODO(mark): Deal with statically linked executables which don’t use dyld.
-  // This may look for the module that matches the executable path in the same
-  // data set that vmmap uses.
-
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
-  // The task_dyld_info_data_t struct grew in 10.7, adding the format field.
-  // Don’t check this field if it’s not present, which can happen when either
-  // the SDK used at compile time or the kernel at run time are too old and
-  // don’t know about it.
-  if (count >= TASK_DYLD_INFO_COUNT) {
-    const integer_t kExpectedFormat =
-        !Is64Bit() ? TASK_DYLD_ALL_IMAGE_INFO_32 : TASK_DYLD_ALL_IMAGE_INFO_64;
-    if (dyld_info.all_image_info_format != kExpectedFormat) {
-      LOG(WARNING) << "unexpected task_dyld_info_data_t::all_image_info_format "
-                   << dyld_info.all_image_info_format;
-      DCHECK_EQ(dyld_info.all_image_info_format, kExpectedFormat);
-      return;
-    }
-  }
-#endif
+  mach_vm_size_t all_image_info_size;
+  mach_vm_address_t all_image_info_addr =
+      DyldAllImageInfo(&all_image_info_size);
 
   process_types::dyld_all_image_infos all_image_infos;
-  if (!all_image_infos.Read(this, dyld_info.all_image_info_addr)) {
+  if (!all_image_infos.Read(this, all_image_info_addr)) {
     LOG(WARNING) << "could not read dyld_all_image_infos";
     return;
   }
@@ -390,10 +404,10 @@
   size_t expected_size =
       process_types::dyld_all_image_infos::ExpectedSizeForVersion(
           this, all_image_infos.version);
-  if (dyld_info.all_image_info_size < expected_size) {
-    LOG(WARNING) << "small dyld_all_image_infos size "
-                 << dyld_info.all_image_info_size << " < " << expected_size
-                 << " for version " << all_image_infos.version;
+  if (all_image_info_size < expected_size) {
+    LOG(WARNING) << "small dyld_all_image_infos size " << all_image_info_size
+                 << " < " << expected_size << " for version "
+                 << all_image_infos.version;
     return;
   }
 
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_reader.h b/third_party/crashpad/crashpad/snapshot/mac/process_reader.h
index 72bd1dc..ddb49cb6 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_reader.h
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_reader.h
@@ -150,6 +150,21 @@
   //!     corresponds to the dynamic loader, dyld.
   const std::vector<Module>& Modules();
 
+  //! \brief Determines the location of the `dyld_all_image_infos` structure in
+  //!     the process’ address space.
+  //!
+  //! This function is an internal implementation detail of Modules(), and
+  //! should not normally be used directly. It is exposed solely for use by test
+  //! code.
+  //!
+  //! \param[out] all_image_info_size The size of the `dyld_all_image_infos`
+  //!     structure. Optional, may be `nullptr` if not required.
+  //!
+  //! \return The address of the `dyld_all_image_infos` structure in the
+  //!     process’ address space, with \a all_image_info_size set appropriately.
+  //!     On failure, returns `0` with a message logged.
+  mach_vm_address_t DyldAllImageInfo(mach_vm_size_t* all_image_info_size);
+
  private:
   //! Performs lazy initialization of the \a threads_ vector on behalf of
   //! Threads().
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_reader_test.cc b/third_party/crashpad/crashpad/snapshot/mac/process_reader_test.cc
index 3554326..3a6a1c8 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_reader_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_reader_test.cc
@@ -44,12 +44,13 @@
 #include "util/stdlib/pointer_container.h"
 #include "util/synchronization/semaphore.h"
 
-#if !defined(MAC_OS_X_VERSION_10_10) || \
-    MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
 extern "C" {
-// Redeclare a typedef whose availability (OSX 10.10) is newer than the
+
+// Redeclare a typedef whose availability (OS X 10.10) is newer than the
 // deployment target.
 typedef struct _cl_device_id* cl_device_id;
+
 }  // extern "C"
 #endif
 
@@ -698,8 +699,7 @@
   // is also reported as 0.
   EXPECT_EQ(modules[index].timestamp, 0);
 
-  const struct dyld_all_image_infos* dyld_image_infos =
-      _dyld_get_all_image_infos();
+  const dyld_all_image_infos* dyld_image_infos = DyldGetAllImageInfos();
   if (dyld_image_infos->version >= 2) {
     ASSERT_TRUE(modules[index].reader);
     EXPECT_EQ(modules[index].reader->Address(),
@@ -781,8 +781,7 @@
     FileHandle write_handle = WritePipeHandle();
 
     uint32_t dyld_image_count = _dyld_image_count();
-    const struct dyld_all_image_infos* dyld_image_infos =
-        _dyld_get_all_image_infos();
+    const dyld_all_image_infos* dyld_image_infos = DyldGetAllImageInfos();
 
     uint32_t write_image_count = dyld_image_count;
     if (dyld_image_infos->version >= 2) {
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types.cc b/third_party/crashpad/crashpad/snapshot/mac/process_types.cc
index 53d8903..aeff88bb 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_types.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_types.cc
@@ -42,6 +42,15 @@
 }
 
 template <>
+inline void Assign<process_types::internal::Reserved32_32Only64,
+                   process_types::internal::Reserved32_32Only32>(
+    process_types::internal::Reserved32_32Only64* destination,
+    const process_types::internal::Reserved32_32Only32& source) {
+  // Reserved32_32Only32 carries no data and has no storage in the 64-bit
+  // structure.
+}
+
+template <>
 inline void Assign<process_types::internal::Reserved32_64Only64,
                    process_types::internal::Reserved32_64Only32>(
     process_types::internal::Reserved32_64Only64* destination,
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types.h b/third_party/crashpad/crashpad/snapshot/mac/process_types.h
index 7274515..eb397b9 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_types.h
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_types.h
@@ -37,8 +37,10 @@
 
 // Some structure definitions differ in 32-bit and 64-bit environments by having
 // additional “reserved” padding fields present only in the 64-bit environment.
-// These Reserved*_64Only* types allow the process_types system to replicate
+// These Reserved*_*Only* types allow the process_types system to replicate
 // these structures more precisely.
+using Reserved32_32Only32 = uint32_t;
+using Reserved32_32Only64 = Nothing;
 using Reserved32_64Only32 = Nothing;
 using Reserved32_64Only64 = uint32_t;
 using Reserved64_64Only32 = Nothing;
@@ -71,6 +73,7 @@
     using Pointer = internal::TraitsGeneric::Pointer;                          \
     using IntPtr = internal::TraitsGeneric::IntPtr;                            \
     using UIntPtr = internal::TraitsGeneric::UIntPtr;                          \
+    using Reserved32_32Only = internal::TraitsGeneric::Reserved32_32Only;      \
     using Reserved32_64Only = internal::TraitsGeneric::Reserved32_64Only;      \
     using Reserved64_64Only = internal::TraitsGeneric::Reserved64_64Only;      \
     using Nothing = internal::TraitsGeneric::Nothing;                          \
@@ -162,6 +165,7 @@
     using Pointer = typename Traits::Pointer;                                  \
     using IntPtr = typename Traits::IntPtr;                                    \
     using UIntPtr = typename Traits::UIntPtr;                                  \
+    using Reserved32_32Only = typename Traits::Reserved32_32Only;              \
     using Reserved32_64Only = typename Traits::Reserved32_64Only;              \
     using Reserved64_64Only = typename Traits::Reserved64_64Only;              \
     using Nothing = typename Traits::Nothing;                                  \
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types/custom.cc b/third_party/crashpad/crashpad/snapshot/mac/process_types/custom.cc
index c896ebf..7839b40 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_types/custom.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_types/custom.cc
@@ -80,7 +80,9 @@
       offsetof(dyld_all_image_infos<Traits>, sharedCacheSlide),  // 11
       offsetof(dyld_all_image_infos<Traits>, sharedCacheUUID),  // 12
       offsetof(dyld_all_image_infos<Traits>, infoArrayChangeTimestamp),  // 13
-      offsetof(dyld_all_image_infos<Traits>, end),  // 14
+      offsetof(dyld_all_image_infos<Traits>, end_14_15),  // 14
+      offsetof(dyld_all_image_infos<Traits>, end_14_15),  // 15
+      sizeof(dyld_all_image_infos<Traits>),  // 16
   };
 
   if (version >= arraysize(kSizeForVersion)) {
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types/dyld_images.proctype b/third_party/crashpad/crashpad/snapshot/mac/process_types/dyld_images.proctype
index 3470ee8..f92a800 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_types/dyld_images.proctype
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_types/dyld_images.proctype
@@ -116,20 +116,28 @@
 
   // Version 14 (OS X 10.9)
   // As of the 10.12 SDK, this is declared as reserved[9] for 64-bit platforms
-  // and reserved[4] for 32-bit platforms.
+  // and reserved[4] for 32-bit platforms. It was expanded to reserved[5] for
+  // 32-bit platforms in the 10.13 SDK to provide proper padding, but because
+  // the runtimes that use versions 14 and 15 were built with SDKs that did not
+  // have this extra padding, it’s necessary to treat the element at index 4 on
+  // 32-bit systems as outside of the version 14 and 15 structure. This is why
+  // |reserved| is only declared a 4-element array, with a special end_14_15
+  // member (not present in the native definition) available to indicate the
+  // end of the native version 14 and 15 structure, preceding the padding in the
+  // 32-bit structure that would natively be addressed at index 4 of |reserved|.
+  // Treat reserved_4_32 as only available in version 16 of the structure.
   PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, reserved, [4])
-  PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_4)
+  PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_4_64)
   PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_5)
   PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_6)
   PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_7)
   PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_8)
+  PROCESS_TYPE_STRUCT_MEMBER(Nothing, end_14_15)
+  PROCESS_TYPE_STRUCT_MEMBER(Reserved32_32Only, reserved_4_32)
 
-  // The 32-bit version of the structure will have four extra bytes of tail
-  // padding when built for 64-bit systems than it does natively and when built
-  // for 32-bit systems. Instead of using sizeof(dyld_all_image_infos), use
-  // offsetof(dyld_all_image_infos, end) to avoid taking this tail padding into
-  // account.
-  PROCESS_TYPE_STRUCT_MEMBER(Nothing, end)
+  // Version 16 (macOS 10.13)
+  PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, compact_dyld_image_info_addr)
+  PROCESS_TYPE_STRUCT_MEMBER(ULong, compact_dyld_image_info_size)  // size_t
 PROCESS_TYPE_STRUCT_END(dyld_all_image_infos)
 
 #endif  // ! PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO &&
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types/traits.h b/third_party/crashpad/crashpad/snapshot/mac/process_types/traits.h
index 396799be..613f4f57 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_types/traits.h
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_types/traits.h
@@ -36,6 +36,7 @@
     using Pointer = uint##lp_bits##_t __VA_ARGS__;                   \
     using IntPtr = int##lp_bits##_t __VA_ARGS__;                     \
     using UIntPtr = uint##lp_bits##_t __VA_ARGS__;                   \
+    using Reserved32_32Only = Reserved32_32Only##lp_bits;            \
     using Reserved32_64Only = Reserved32_64Only##lp_bits;            \
     using Reserved64_64Only = Reserved64_64Only##lp_bits;            \
     using Nothing = Nothing;                                         \
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types_test.cc b/third_party/crashpad/crashpad/snapshot/mac/process_types_test.cc
index 18595f0..8ab15c8 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_types_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_types_test.cc
@@ -46,9 +46,13 @@
 
 TEST(ProcessTypes, DyldImagesSelf) {
   // Get the in-process view of dyld_all_image_infos, and check it for sanity.
-  const struct dyld_all_image_infos* self_image_infos =
-      _dyld_get_all_image_infos();
+  const dyld_all_image_infos* self_image_infos = DyldGetAllImageInfos();
   int mac_os_x_minor_version = MacOSXMinorVersion();
+
+  // The 10.13 SDK defines dyld_all_image_infos version 16 and says that it’s
+  // used on 10.13, but 10.13db1 17A264c uses version 15.
+  //
+  // TODO(mark): Recheck later in the beta period, up to the 10.13 release.
   if (mac_os_x_minor_version >= 12) {
     EXPECT_GE(self_image_infos->version, 15u);
   } else if (mac_os_x_minor_version >= 9) {
@@ -100,16 +104,18 @@
   ProcessReader process_reader;
   ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
 
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12
-  const uint32_t kDyldAllImageInfosVersionInSDK = 15;
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13
+  constexpr uint32_t kDyldAllImageInfosVersionInSDK = 16;
+#elif MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12
+  constexpr uint32_t kDyldAllImageInfosVersionInSDK = 15;
 #elif MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
-  const uint32_t kDyldAllImageInfosVersionInSDK = 14;
+  constexpr uint32_t kDyldAllImageInfosVersionInSDK = 14;
 #elif MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
-  const uint32_t kDyldAllImageInfosVersionInSDK = 12;
+  constexpr uint32_t kDyldAllImageInfosVersionInSDK = 12;
 #elif MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
-  const uint32_t kDyldAllImageInfosVersionInSDK = 7;
+  constexpr uint32_t kDyldAllImageInfosVersionInSDK = 7;
 #else
-  const uint32_t kDyldAllImageInfosVersionInSDK = 1;
+  constexpr uint32_t kDyldAllImageInfosVersionInSDK = 1;
 #endif
 
   // Make sure that the size of the structure as declared in the SDK matches the
@@ -120,7 +126,7 @@
 
   // Make sure that the computed sizes of various versions of this structure are
   // correct at different bitnessses.
-  const struct {
+  constexpr struct {
     uint32_t version;
     size_t size_32;
     size_t size_64;
@@ -139,6 +145,7 @@
       {13, 104, 184},
       {14, 164, 304},
       {15, 164, 304},
+      {16, 176, 320},
   };
   for (size_t index = 0; index < arraysize(kVersionsAndSizes); ++index) {
     uint32_t version = kVersionsAndSizes[index].version;
@@ -290,7 +297,8 @@
           << "index " << index;
     }
 #if defined(ARCH_CPU_64_BITS)
-    EXPECT_EQ(proctype_image_infos.reserved_4, self_image_infos->reserved[4]);
+    EXPECT_EQ(proctype_image_infos.reserved_4_64,
+              self_image_infos->reserved[4]);
     EXPECT_EQ(proctype_image_infos.reserved_5, self_image_infos->reserved[5]);
     EXPECT_EQ(proctype_image_infos.reserved_6, self_image_infos->reserved[6]);
     EXPECT_EQ(proctype_image_infos.reserved_7, self_image_infos->reserved[7]);
@@ -299,6 +307,15 @@
   }
 #endif
 
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13
+  if (proctype_image_infos.version >= 16) {
+    EXPECT_EQ(proctype_image_infos.compact_dyld_image_info_addr,
+              self_image_infos->compact_dyld_image_info_addr);
+    EXPECT_EQ(proctype_image_infos.compact_dyld_image_info_size,
+              self_image_infos->compact_dyld_image_info_size);
+  }
+#endif
+
   if (proctype_image_infos.version >= 1) {
     std::vector<process_types::dyld_image_info> proctype_image_info_vector(
         proctype_image_infos.infoArrayCount);
diff --git a/third_party/crashpad/crashpad/test/mac/dyld.cc b/third_party/crashpad/crashpad/test/mac/dyld.cc
new file mode 100644
index 0000000..6fa2176
--- /dev/null
+++ b/third_party/crashpad/crashpad/test/mac/dyld.cc
@@ -0,0 +1,99 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "test/mac/dyld.h"
+
+#include <AvailabilityMacros.h>
+#include <dlfcn.h>
+#include <mach/mach.h>
+#include <mach-o/dyld.h>
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "snapshot/mac/process_reader.h"
+#include "test/scoped_module_handle.h"
+#include "util/numeric/safe_assignment.h"
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13
+extern "C" {
+
+// A non-public dyld API, declared in 10.12.4
+// dyld-433.5/include/mach-o/dyld_priv.h. The code still exists in 10.13, but
+// its symbol is no longer public, so it can’t be used there.
+const dyld_all_image_infos* _dyld_get_all_image_infos()
+    __attribute__((weak_import));
+
+}  // extern "C"
+#endif
+
+namespace crashpad {
+namespace test {
+
+const dyld_all_image_infos* DyldGetAllImageInfos() {
+#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13
+  // When building with the pre-10.13 SDK, the weak_import declaration above is
+  // available and a symbol will be present in the SDK to link against. If the
+  // old interface is also available at run time (running on pre-10.13), use it.
+  if (_dyld_get_all_image_infos) {
+    return _dyld_get_all_image_infos();
+  }
+#elif MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_13
+  // When building with the 10.13 SDK or later, but able to run on pre-10.13,
+  // look for _dyld_get_all_image_infos in the same module that provides
+  // _dyld_image_count. There’s no symbol in the SDK to link against, so this is
+  // a little more involved than the pre-10.13 SDK case above.
+  Dl_info dli;
+  if (!dladdr(reinterpret_cast<void*>(_dyld_image_count), &dli)) {
+    LOG(WARNING) << "dladdr: failed";
+  } else {
+    ScopedModuleHandle module(
+        dlopen(dli.dli_fname, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD));
+    if (!module.valid()) {
+      LOG(WARNING) << "dlopen: " << dlerror();
+    } else {
+      using DyldGetAllImageInfosType = const dyld_all_image_infos*(*)();
+      const auto _dyld_get_all_image_infos =
+          module.LookUpSymbol<DyldGetAllImageInfosType>(
+              "_dyld_get_all_image_infos");
+      if (_dyld_get_all_image_infos) {
+        return _dyld_get_all_image_infos();
+      }
+    }
+  }
+#endif
+
+  // On 10.13 and later, do it the hard way.
+  ProcessReader process_reader;
+  if (!process_reader.Initialize(mach_task_self())) {
+    return nullptr;
+  }
+
+  mach_vm_address_t all_image_info_addr_m =
+      process_reader.DyldAllImageInfo(nullptr);
+  if (!all_image_info_addr_m) {
+    return nullptr;
+  }
+
+  uintptr_t all_image_info_addr_u;
+  if (!AssignIfInRange(&all_image_info_addr_u, all_image_info_addr_m)) {
+    LOG(ERROR) << "all_image_info_addr_m " << all_image_info_addr_m
+               << " out of range";
+    return nullptr;
+  }
+
+  return reinterpret_cast<const dyld_all_image_infos*>(all_image_info_addr_u);
+}
+
+}  // namespace test
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/mac/dyld.h b/third_party/crashpad/crashpad/test/mac/dyld.h
index e436c5d1..0a688f3e 100644
--- a/third_party/crashpad/crashpad/test/mac/dyld.h
+++ b/third_party/crashpad/crashpad/test/mac/dyld.h
@@ -17,13 +17,17 @@
 
 #include <mach-o/dyld_images.h>
 
-extern "C" {
+namespace crashpad {
+namespace test {
 
-// Returns a pointer to this process’ dyld_all_image_infos structure. This is
-// implemented as a non-public dyld API, declared in 10.9.2
-// dyld-239.4/include/mach-o/dyld_priv.h.
-const struct dyld_all_image_infos* _dyld_get_all_image_infos();
+//! \brief Calls or emulates the `_dyld_get_all_image_infos()` private/internal
+//!     function.
+//!
+//! \return A pointer to this process’ dyld_all_image_infos structure, or
+//!     `nullptr` on failure with a message logged.
+const dyld_all_image_infos* DyldGetAllImageInfos();
 
-}  // extern "C"
+}  // namespace test
+}  // namespace crashpad
 
 #endif  // CRASHPAD_TEST_MAC_DYLD_H_
diff --git a/third_party/crashpad/crashpad/test/scoped_module_handle.cc b/third_party/crashpad/crashpad/test/scoped_module_handle.cc
new file mode 100644
index 0000000..3854222
--- /dev/null
+++ b/third_party/crashpad/crashpad/test/scoped_module_handle.cc
@@ -0,0 +1,46 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "test/scoped_module_handle.h"
+
+#include "base/logging.h"
+
+namespace crashpad {
+namespace test {
+
+// static
+void ScopedModuleHandle::Impl::Close(ModuleHandle handle) {
+#if defined(OS_POSIX)
+  if (dlclose(handle) != 0) {
+    LOG(ERROR) << "dlclose: " << dlerror();
+  }
+#elif defined(OS_WIN)
+  if (!FreeLibrary(handle)) {
+    PLOG(ERROR) << "FreeLibrary";
+  }
+#else
+#error Port
+#endif
+}
+
+ScopedModuleHandle::ScopedModuleHandle(ModuleHandle handle) : handle_(handle) {}
+
+ScopedModuleHandle::~ScopedModuleHandle() {
+  if (valid()) {
+    Impl::Close(handle_);
+  }
+}
+
+}  // namespace test
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/scoped_module_handle.h b/third_party/crashpad/crashpad/test/scoped_module_handle.h
new file mode 100644
index 0000000..3863cad
--- /dev/null
+++ b/third_party/crashpad/crashpad/test/scoped_module_handle.h
@@ -0,0 +1,81 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_TEST_SCOPED_MODULE_HANDLE_H_
+#define CRASHPAD_TEST_SCOPED_MODULE_HANDLE_H_
+
+#include "base/macros.h"
+#include "build/build_config.h"
+
+#if defined(OS_POSIX)
+#include <dlfcn.h>
+#elif defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace crashpad {
+namespace test {
+
+//! \brief Maintains ownership of a loadable module handle, releasing it as
+//!     appropriate on destruction.
+class ScopedModuleHandle {
+ private:
+  class Impl {
+   public:
+#if defined(OS_POSIX)
+    using ModuleHandle = void*;
+
+    static void* LookUpSymbol(ModuleHandle handle, const char* symbol_name) {
+      return dlsym(handle, symbol_name);
+    }
+#elif defined(OS_WIN)
+    using ModuleHandle = HMODULE;
+
+    static void* LookUpSymbol(ModuleHandle handle, const char* symbol_name) {
+      return GetProcAddress(handle, symbol_name);
+    }
+#endif
+
+    static void Close(ModuleHandle handle);
+
+   private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(Impl);
+  };
+
+ public:
+  using ModuleHandle = Impl::ModuleHandle;
+
+  explicit ScopedModuleHandle(ModuleHandle handle);
+  ~ScopedModuleHandle();
+
+  //! \return `true` if this object manages a valid loadable module handle.
+  bool valid() const { return handle_ != nullptr; }
+
+  //! \return The value of the symbol named by \a symbol_name, or `nullptr` on
+  //!     failure.
+  template <typename T>
+  T LookUpSymbol(const char* symbol_name) const {
+    return reinterpret_cast<T>(Impl::LookUpSymbol(handle_, symbol_name));
+  }
+
+ private:
+  ModuleHandle handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedModuleHandle);
+};
+
+}  // namespace test
+}  // namespace crashpad
+
+#endif  // CRASHPAD_TEST_SCOPED_MODULE_HANDLE_H_
diff --git a/third_party/crashpad/crashpad/test/test.gyp b/third_party/crashpad/crashpad/test/test.gyp
index 94b198c..f2682c0 100644
--- a/third_party/crashpad/crashpad/test/test.gyp
+++ b/third_party/crashpad/crashpad/test/test.gyp
@@ -22,6 +22,7 @@
       'type': 'static_library',
       'dependencies': [
         '../compat/compat.gyp:crashpad_compat',
+        '../snapshot/snapshot.gyp:crashpad_snapshot',
         '../third_party/gtest/gtest.gyp:gtest',
         '../third_party/mini_chromium/mini_chromium.gyp:base',
         '../util/util.gyp:crashpad_util',
@@ -37,6 +38,7 @@
         'gtest_death_check.h',
         'hex_string.cc',
         'hex_string.h',
+        'mac/dyld.cc',
         'mac/dyld.h',
         'mac/mach_errors.cc',
         'mac/mach_errors.h',
@@ -49,6 +51,8 @@
         'multiprocess_exec_posix.cc',
         'multiprocess_exec_win.cc',
         'multiprocess_posix.cc',
+        'scoped_module_handle.cc',
+        'scoped_module_handle.h',
         'scoped_temp_dir.cc',
         'scoped_temp_dir.h',
         'scoped_temp_dir_posix.cc',
diff --git a/third_party/crashpad/crashpad/util/linux/auxiliary_vector.cc b/third_party/crashpad/crashpad/util/linux/auxiliary_vector.cc
new file mode 100644
index 0000000..8852e614
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/linux/auxiliary_vector.cc
@@ -0,0 +1,104 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/linux/auxiliary_vector.h"
+
+#include <linux/auxvec.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <algorithm>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "util/file/file_reader.h"
+#include "util/stdlib/map_insert.h"
+
+namespace crashpad {
+
+AuxiliaryVector::AuxiliaryVector() : values_() {}
+
+AuxiliaryVector::~AuxiliaryVector() {}
+
+bool AuxiliaryVector::Initialize(pid_t pid, bool is_64_bit) {
+  return is_64_bit ? Read<uint64_t>(pid) : Read<uint32_t>(pid);
+}
+
+template <typename ULong>
+bool AuxiliaryVector::Read(pid_t pid) {
+  char path[32];
+  snprintf(path, sizeof(path), "/proc/%d/auxv", pid);
+  FileReader aux_file;
+  if (!aux_file.Open(base::FilePath(path))) {
+    return false;
+  }
+
+  ULong type;
+  ULong value;
+  while (aux_file.ReadExactly(&type, sizeof(type)) &&
+         aux_file.ReadExactly(&value, sizeof(value))) {
+    if (type == AT_NULL && value == 0) {
+      return true;
+    }
+    if (type == AT_IGNORE) {
+      continue;
+    }
+    if (!MapInsertOrReplace(&values_, type, value, nullptr)) {
+      LOG(ERROR) << "duplicate auxv entry";
+      return false;
+    }
+  }
+  return false;
+}
+
+// static
+bool AuxiliaryVector::VariableSizeBitCast(uint64_t data,
+                                          char* dest,
+                                          size_t dest_size) {
+  auto data_p = reinterpret_cast<const char*>(&data);
+  constexpr size_t data_size = sizeof(uint64_t);
+
+  // Verify that any unused bytes from data are zero.
+  // The unused bytes are at the start of the data buffer for big-endian and the
+  // end of the buffer for little-endian.
+  if (dest_size < data_size) {
+    uint64_t zero = 0;
+    auto extra_bytes = data_p;
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+    extra_bytes += dest_size;
+#endif  // ARCH_CPU_LITTLE_ENDIAN
+    if (memcmp(extra_bytes, &zero, data_size - dest_size) != 0) {
+      LOG(ERROR) << "information loss";
+      return false;
+    }
+  }
+
+  // Zero out the destination, in case it is larger than data.
+  memset(dest, 0, dest_size);
+
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+  // Copy a prefix of data to a prefix of dest for little-endian
+  memcpy(dest, data_p, std::min(dest_size, data_size));
+#else
+  // or the suffix of data to the suffix of dest for big-endian
+  if (data_size >= dest_size) {
+    memcpy(dest, data_p + data_size - dest_size, dest_size);
+  } else {
+    memcpy(dest + dest_size - data_size, data_p, data_size);
+  }
+#endif  // ARCH_CPU_LITTLE_ENDIAN
+  return true;
+}
+
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/linux/auxiliary_vector.h b/third_party/crashpad/crashpad/util/linux/auxiliary_vector.h
new file mode 100644
index 0000000..904eada
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/linux/auxiliary_vector.h
@@ -0,0 +1,76 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_LINUX_AUXILIARY_VECTOR_H_
+#define CRASHPAD_UTIL_LINUX_AUXILIARY_VECTOR_H_
+
+#include <sys/types.h>
+
+#include <map>
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace crashpad {
+
+//! \brief Read the auxiliary vector for a target process.
+class AuxiliaryVector {
+ public:
+  AuxiliaryVector();
+  ~AuxiliaryVector();
+
+  //! \brief Initializes this object with the auxiliary vector for the process
+  //!     with process ID \a pid.
+  //!
+  //! This method must be called successfully prior to calling any other method
+  //! in this class.
+  //!
+  //! \param[in] pid The process ID of a target process.
+  //! \param[in] is_64_bit Whether the target process is 64-bit.
+  //!
+  //! \return `true` on success, `false` on failure with a message logged.
+  bool Initialize(pid_t pid, bool is_64_bit);
+
+  //! \brief Retrieve a value from the vector.
+  //!
+  //! \param[in] type Specifies which value should be retrieved. The possible
+  //!     values for this parameter are defined by `<linux/auxvec.h>`.
+  //! \param[out] value The value, casted to an appropriate type, if found.
+  //! \return `true` if the value is found.
+  template <typename V>
+  bool GetValue(uint64_t type, V* value) const {
+    auto iter = values_.find(type);
+    if (iter == values_.end()) {
+      LOG(ERROR) << "value not found";
+      return false;
+    }
+    return VariableSizeBitCast(
+        iter->second, reinterpret_cast<char*>(value), sizeof(V));
+  }
+
+ protected:
+  std::map<uint64_t, uint64_t> values_;
+
+ private:
+  template <typename ULong>
+  bool Read(pid_t pid);
+
+  static bool VariableSizeBitCast(uint64_t data, char* dest, size_t dest_size);
+
+  DISALLOW_COPY_AND_ASSIGN(AuxiliaryVector);
+};
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_LINUX_AUXILIARY_VECTOR_H_
diff --git a/third_party/crashpad/crashpad/util/linux/auxiliary_vector_test.cc b/third_party/crashpad/crashpad/util/linux/auxiliary_vector_test.cc
new file mode 100644
index 0000000..89bc3c7
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/linux/auxiliary_vector_test.cc
@@ -0,0 +1,209 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/linux/auxiliary_vector.h"
+
+#include <linux/auxvec.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include <limits>
+
+#include "base/macros.h"
+#include "gtest/gtest.h"
+#include "test/errors.h"
+#include "test/multiprocess.h"
+#include "util/linux/address_types.h"
+#include "util/linux/memory_map.h"
+#include "util/linux/process_memory.h"
+#include "util/misc/from_pointer_cast.h"
+#include "util/numeric/int128.h"
+
+extern "C" {
+extern void _start();
+}  // extern "C"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+void TestAgainstCloneOrSelf(pid_t pid) {
+#if defined(ARCH_CPU_64_BITS)
+  constexpr bool am_64_bit = true;
+#else
+  constexpr bool am_64_bit = false;
+#endif
+  AuxiliaryVector aux;
+  ASSERT_TRUE(aux.Initialize(pid, am_64_bit));
+
+  MemoryMap mappings;
+  ASSERT_TRUE(mappings.Initialize(pid));
+
+  LinuxVMAddress phdrs;
+  ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs));
+  EXPECT_TRUE(mappings.FindMapping(phdrs));
+
+  int pagesize;
+  ASSERT_TRUE(aux.GetValue(AT_PAGESZ, &pagesize));
+  EXPECT_EQ(pagesize, getpagesize());
+
+  LinuxVMAddress interp_base;
+  ASSERT_TRUE(aux.GetValue(AT_BASE, &interp_base));
+  EXPECT_TRUE(mappings.FindMapping(interp_base));
+
+  LinuxVMAddress entry_addr;
+  ASSERT_TRUE(aux.GetValue(AT_ENTRY, &entry_addr));
+  EXPECT_EQ(entry_addr, FromPointerCast<LinuxVMAddress>(_start));
+
+  uid_t uid;
+  ASSERT_TRUE(aux.GetValue(AT_UID, &uid));
+  EXPECT_EQ(uid, getuid());
+
+  uid_t euid;
+  ASSERT_TRUE(aux.GetValue(AT_EUID, &euid));
+  EXPECT_EQ(euid, geteuid());
+
+  gid_t gid;
+  ASSERT_TRUE(aux.GetValue(AT_GID, &gid));
+  EXPECT_EQ(gid, getgid());
+
+  gid_t egid;
+  ASSERT_TRUE(aux.GetValue(AT_EGID, &egid));
+  EXPECT_EQ(egid, getegid());
+
+  ProcessMemory memory;
+  ASSERT_TRUE(memory.Initialize(pid));
+
+  LinuxVMAddress platform_addr;
+  ASSERT_TRUE(aux.GetValue(AT_PLATFORM, &platform_addr));
+  std::string platform;
+  ASSERT_TRUE(memory.ReadCStringSizeLimited(platform_addr, 10, &platform));
+#if defined(ARCH_CPU_X86)
+  EXPECT_STREQ(platform.c_str(), "i686");
+#elif defined(ARCH_CPU_X86_64)
+  EXPECT_STREQ(platform.c_str(), "x86_64");
+#elif defined(ARCH_CPU_ARMEL)
+  // Machine name and platform are set in Linux:/arch/arm/kernel/setup.c
+  // Machine typically looks like "armv7l".
+  // Platform typically looks like "v7l".
+  utsname sys_names;
+  ASSERT_EQ(uname(&sys_names), 0);
+  std::string machine_name(sys_names.machine);
+  EXPECT_NE(machine_name.find(platform), std::string::npos);
+#elif defined(ARCH_CPU_ARM64)
+  EXPECT_STREQ(platform.c_str(), "aarch64");
+#endif  // ARCH_CPU_X86
+
+#if defined(AT_SYSINFO_EHDR)
+  LinuxVMAddress vdso_addr;
+  ASSERT_TRUE(aux.GetValue(AT_SYSINFO_EHDR, &vdso_addr));
+  EXPECT_TRUE(mappings.FindMapping(vdso_addr));
+#endif  // AT_SYSINFO_EHDR
+
+#if defined(AT_EXECFN)
+  LinuxVMAddress filename_addr;
+  ASSERT_TRUE(aux.GetValue(AT_EXECFN, &filename_addr));
+  std::string filename;
+  ASSERT_TRUE(memory.ReadCStringSizeLimited(filename_addr, 4096, &filename));
+  EXPECT_TRUE(filename.find("crashpad_util_test") != std::string::npos);
+#endif  // AT_EXECFN
+
+  int ignore;
+  EXPECT_FALSE(aux.GetValue(AT_NULL, &ignore));
+
+  char too_small;
+  EXPECT_FALSE(aux.GetValue(AT_PAGESZ, &too_small));
+
+  uint128_struct big_dest;
+  memset(&big_dest, 0xf, sizeof(big_dest));
+  ASSERT_TRUE(aux.GetValue(AT_PHDR, &big_dest));
+  EXPECT_EQ(big_dest.lo, phdrs);
+}
+
+TEST(AuxiliaryVector, ReadSelf) {
+  TestAgainstCloneOrSelf(getpid());
+}
+
+class ReadChildTest : public Multiprocess {
+ public:
+  ReadChildTest() : Multiprocess() {}
+  ~ReadChildTest() {}
+
+ private:
+  void MultiprocessParent() override { TestAgainstCloneOrSelf(ChildPID()); }
+
+  void MultiprocessChild() override { CheckedReadFileAtEOF(ReadPipeHandle()); }
+
+  DISALLOW_COPY_AND_ASSIGN(ReadChildTest);
+};
+
+TEST(AuxiliaryVector, ReadChild) {
+  ReadChildTest test;
+  test.Run();
+}
+
+class AuxVecTester : public AuxiliaryVector {
+ public:
+  AuxVecTester() : AuxiliaryVector() {}
+  void Insert(uint64_t type, uint64_t value) { values_[type] = value; }
+};
+
+TEST(AuxiliaryVector, SignedBit) {
+#if defined(ARCH_CPU_64_BITS)
+  constexpr bool am_64_bit = true;
+#else
+  constexpr bool am_64_bit = false;
+#endif
+  AuxVecTester aux;
+  ASSERT_TRUE(aux.Initialize(getpid(), am_64_bit));
+  constexpr uint64_t type = 0x0000000012345678;
+
+  constexpr int32_t neg1_32 = -1;
+  aux.Insert(type, bit_cast<uint32_t>(neg1_32));
+  int32_t outval32s;
+  ASSERT_TRUE(aux.GetValue(type, &outval32s));
+  EXPECT_EQ(outval32s, neg1_32);
+
+  constexpr int32_t int32_max = std::numeric_limits<int32_t>::max();
+  aux.Insert(type, bit_cast<uint32_t>(int32_max));
+  ASSERT_TRUE(aux.GetValue(type, &outval32s));
+  EXPECT_EQ(outval32s, int32_max);
+
+  constexpr uint32_t uint32_max = std::numeric_limits<uint32_t>::max();
+  aux.Insert(type, uint32_max);
+  uint32_t outval32u;
+  ASSERT_TRUE(aux.GetValue(type, &outval32u));
+  EXPECT_EQ(outval32u, uint32_max);
+
+  constexpr int64_t neg1_64 = -1;
+  aux.Insert(type, bit_cast<uint64_t>(neg1_64));
+  int64_t outval64s;
+  ASSERT_TRUE(aux.GetValue(type, &outval64s));
+  EXPECT_EQ(outval64s, neg1_64);
+
+  constexpr int64_t int64_max = std::numeric_limits<int64_t>::max();
+  aux.Insert(type, bit_cast<uint64_t>(int64_max));
+  ASSERT_TRUE(aux.GetValue(type, &outval64s));
+  EXPECT_EQ(outval64s, int64_max);
+
+  constexpr uint64_t uint64_max = std::numeric_limits<uint64_t>::max();
+  aux.Insert(type, uint64_max);
+  uint64_t outval64u;
+  ASSERT_TRUE(aux.GetValue(type, &outval64u));
+  EXPECT_EQ(outval64u, uint64_max);
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/linux/memory_map.cc b/third_party/crashpad/crashpad/util/linux/memory_map.cc
index d1ef6c0..4283cb8 100644
--- a/third_party/crashpad/crashpad/util/linux/memory_map.cc
+++ b/third_party/crashpad/crashpad/util/linux/memory_map.cc
@@ -255,4 +255,16 @@
   return nullptr;
 }
 
+const MemoryMap::Mapping* MemoryMap::FindMappingWithName(
+    const std::string& name) const {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+  for (const auto& mapping : mappings_) {
+    if (mapping.name == name) {
+      return &mapping;
+    }
+  }
+  return nullptr;
+}
+
 }  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/linux/memory_map.h b/third_party/crashpad/crashpad/util/linux/memory_map.h
index 2d7a01a1..22f4448b 100644
--- a/third_party/crashpad/crashpad/util/linux/memory_map.h
+++ b/third_party/crashpad/crashpad/util/linux/memory_map.h
@@ -63,11 +63,17 @@
   //! \return `true` on success, `false` on failure with a message logged.
   bool Initialize(pid_t pid);
 
-  //! \return The Mapping containing \a address. The caller does not take
-  //!     ownership of this object. It is scoped to the lifetime of the
-  //!     MemoryMap object that it was obtained from.
+  //! \return The Mapping containing \a address or `nullptr` if no match is
+  //!     found. The caller does not take ownership of this object. It is scoped
+  //!     to the lifetime of the MemoryMap object that it was obtained from.
   const Mapping* FindMapping(LinuxVMAddress address) const;
 
+  //! \return The Mapping with the lowest base address whose name is \a name or
+  //!     `nullptr` if no match is found. The caller does not take ownership of
+  //!     this object. It is scoped to the lifetime of the MemoryMap object that
+  //!     it was obtained from.
+  const Mapping* FindMappingWithName(const std::string& name) const;
+
  private:
   std::vector<Mapping> mappings_;
   InitializationStateDcheck initialized_;
diff --git a/third_party/crashpad/crashpad/util/linux/memory_map_test.cc b/third_party/crashpad/crashpad/util/linux/memory_map_test.cc
index cf5c9e2..aab5c4ac 100644
--- a/third_party/crashpad/crashpad/util/linux/memory_map_test.cc
+++ b/third_party/crashpad/crashpad/util/linux/memory_map_test.cc
@@ -149,6 +149,7 @@
         << ErrnoMessage("stat");
     EXPECT_EQ(mapping->device, file_stat.st_dev);
     EXPECT_EQ(mapping->inode, file_stat.st_ino);
+    EXPECT_EQ(map.FindMappingWithName(mapping->name), mapping);
   }
 
   void MultiprocessChild() override {
diff --git a/third_party/crashpad/crashpad/util/linux/thread_info.cc b/third_party/crashpad/crashpad/util/linux/thread_info.cc
new file mode 100644
index 0000000..d389763
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/linux/thread_info.cc
@@ -0,0 +1,332 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/linux/thread_info.h"
+
+#include <linux/elf.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/uio.h>
+
+#include "base/logging.h"
+#include "util/misc/from_pointer_cast.h"
+
+#if defined(ARCH_CPU_X86_FAMILY)
+#include <asm/ldt.h>
+#endif
+
+namespace crashpad {
+
+namespace {
+
+#if defined(ARCH_CPU_ARMEL)
+// PTRACE_GETREGSET, introduced in Linux 2.6.34 (2225a122ae26), requires kernel
+// support enabled by HAVE_ARCH_TRACEHOOK. This has been set for x86 (including
+// x86_64) since Linux 2.6.28 (99bbc4b1e677a), but for ARM only since
+// Linux 3.5.0 (0693bf68148c4). Older Linux kernels support PTRACE_GETREGS,
+// PTRACE_GETFPREGS, and PTRACE_GETVFPREGS instead, which don't allow checking
+// the size of data copied.
+//
+// Fortunately, 64-bit ARM support only appeared in Linux 3.7.0, so if
+// PTRACE_GETREGSET fails on ARM with EIO, indicating that the request is not
+// supported, the kernel must be old enough that 64-bit ARM isn’t supported
+// either.
+//
+// TODO(mark): Once helpers to interpret the kernel version are available, add
+// a DCHECK to ensure that the kernel is older than 3.5.
+
+bool GetGeneralPurposeRegistersLegacy(pid_t tid, ThreadContext* context) {
+  if (ptrace(PTRACE_GETREGS, tid, nullptr, &context->t32) != 0) {
+    PLOG(ERROR) << "ptrace";
+    return false;
+  }
+  return true;
+}
+
+bool GetFloatingPointRegistersLegacy(pid_t tid, FloatContext* context) {
+  if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f32.fpregs) != 0) {
+    PLOG(ERROR) << "ptrace";
+    return false;
+  }
+  context->f32.have_fpregs = true;
+
+  if (ptrace(PTRACE_GETVFPREGS, tid, nullptr, &context->f32.vfp) != 0) {
+    switch (errno) {
+      case EINVAL:
+        // These registers are optional on 32-bit ARM cpus
+        break;
+      default:
+        PLOG(ERROR) << "ptrace";
+        return false;
+    }
+  } else {
+    context->f32.have_vfp = true;
+  }
+  return true;
+}
+#endif  // ARCH_CPU_ARMEL
+
+#if defined(ARCH_CPU_ARM_FAMILY)
+// Normally, the Linux kernel will copy out register sets according to the size
+// of the struct that contains them. Tracing a 32-bit ARM process running in
+// compatibility mode on a 64-bit ARM cpu will only copy data for the number of
+// registers times the size of the register, which won't include any possible
+// trailing padding in the struct. These are the sizes of the register data, not
+// including any possible padding.
+constexpr size_t kArmVfpSize = 32 * 8 + 4;
+
+// Target is 32-bit
+bool GetFloatingPointRegisters32(pid_t tid, FloatContext* context) {
+  context->f32.have_fpregs = false;
+  context->f32.have_vfp = false;
+
+  iovec iov;
+  iov.iov_base = &context->f32.fpregs;
+  iov.iov_len = sizeof(context->f32.fpregs);
+  if (ptrace(
+          PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRFPREG), &iov) !=
+      0) {
+    switch (errno) {
+#if defined(ARCH_CPU_ARMEL)
+      case EIO:
+        return GetFloatingPointRegistersLegacy(tid, context);
+#endif  // ARCH_CPU_ARMEL
+      case EINVAL:
+        // A 32-bit process running on a 64-bit CPU doesn't have this register
+        // set. It should have a VFP register set instead.
+        break;
+      default:
+        PLOG(ERROR) << "ptrace";
+        return false;
+    }
+  } else {
+    if (iov.iov_len != sizeof(context->f32.fpregs)) {
+      LOG(ERROR) << "Unexpected registers size";
+      return false;
+    }
+    context->f32.have_fpregs = true;
+  }
+
+  iov.iov_base = &context->f32.vfp;
+  iov.iov_len = sizeof(context->f32.vfp);
+  if (ptrace(
+          PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_ARM_VFP), &iov) !=
+      0) {
+    switch (errno) {
+      case EINVAL:
+        // VFP may not be present on 32-bit ARM cpus.
+        break;
+      default:
+        PLOG(ERROR) << "ptrace";
+        return false;
+    }
+  } else {
+    if (iov.iov_len != kArmVfpSize && iov.iov_len != sizeof(context->f32.vfp)) {
+      LOG(ERROR) << "Unexpected registers size";
+      return false;
+    }
+    context->f32.have_vfp = true;
+  }
+
+  if (!(context->f32.have_fpregs || context->f32.have_vfp)) {
+    LOG(ERROR) << "Unable to collect registers";
+    return false;
+  }
+  return true;
+}
+
+// Target is 64-bit
+bool GetFloatingPointRegisters64(pid_t tid, FloatContext* context) {
+  iovec iov;
+  iov.iov_base = context;
+  iov.iov_len = sizeof(*context);
+  if (ptrace(
+          PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRFPREG), &iov) !=
+      0) {
+    PLOG(ERROR) << "ptrace";
+    return false;
+  }
+  if (iov.iov_len != sizeof(context->f64)) {
+    LOG(ERROR) << "Unexpected registers size";
+    return false;
+  }
+  return true;
+}
+#endif  // ARCH_CPU_ARM_FAMILY
+
+}  // namespace
+
+ThreadContext::ThreadContext() {
+  memset(this, 0, sizeof(*this));
+}
+
+ThreadContext::~ThreadContext() {}
+
+FloatContext::FloatContext() {
+  memset(this, 0, sizeof(*this));
+}
+
+FloatContext::~FloatContext() {}
+
+ThreadInfo::ThreadInfo()
+    : context_(), attachment_(), tid_(-1), initialized_(), is_64_bit_(false) {}
+
+ThreadInfo::~ThreadInfo() {}
+
+bool ThreadInfo::Initialize(pid_t tid) {
+  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+  if (!attachment_.ResetAttach(tid)) {
+    return false;
+  }
+  tid_ = tid;
+
+  size_t length = GetGeneralPurposeRegistersAndLength(&context_);
+  if (length == sizeof(context_.t64)) {
+    is_64_bit_ = true;
+  } else if (length == sizeof(context_.t32)) {
+    is_64_bit_ = false;
+  } else {
+    LOG(ERROR) << "Unexpected registers size";
+    return false;
+  }
+
+  INITIALIZATION_STATE_SET_VALID(initialized_);
+  return true;
+}
+
+bool ThreadInfo::Is64Bit() {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  return is_64_bit_;
+}
+
+void ThreadInfo::GetGeneralPurposeRegisters(ThreadContext* context) {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+  *context = context_;
+}
+
+size_t ThreadInfo::GetGeneralPurposeRegistersAndLength(ThreadContext* context) {
+  iovec iov;
+  iov.iov_base = context;
+  iov.iov_len = sizeof(*context);
+  if (ptrace(
+          PTRACE_GETREGSET, tid_, reinterpret_cast<void*>(NT_PRSTATUS), &iov) !=
+      0) {
+    switch (errno) {
+#if defined(ARCH_CPU_ARMEL)
+      case EIO:
+        if (GetGeneralPurposeRegistersLegacy(tid_, context)) {
+          return sizeof(context->t32);
+        }
+#endif  // ARCH_CPU_ARMEL
+      default:
+        PLOG(ERROR) << "ptrace";
+        return 0;
+    }
+  }
+  return iov.iov_len;
+}
+
+bool ThreadInfo::GetFloatingPointRegisters(FloatContext* context) {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+#if defined(ARCH_CPU_X86_FAMILY)
+  iovec iov;
+  iov.iov_base = context;
+  iov.iov_len = sizeof(*context);
+  if (ptrace(
+          PTRACE_GETREGSET, tid_, reinterpret_cast<void*>(NT_PRFPREG), &iov) !=
+      0) {
+    PLOG(ERROR) << "ptrace";
+    return false;
+  }
+  if (is_64_bit_ && iov.iov_len == sizeof(context->f64)) {
+    return true;
+  }
+  if (!is_64_bit_ && iov.iov_len == sizeof(context->f32)) {
+    return true;
+  }
+  LOG(ERROR) << "Unexpected registers size";
+  return false;
+
+#elif defined(ARCH_CPU_ARM_FAMILY)
+  return is_64_bit_ ? GetFloatingPointRegisters64(tid_, context)
+                    : GetFloatingPointRegisters32(tid_, context);
+#else
+#error Port.
+#endif  // ARCH_CPU_X86_FAMILY
+}
+
+bool ThreadInfo::GetThreadArea(LinuxVMAddress* address) {
+  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+#if defined(ARCH_CPU_X86_FAMILY)
+  if (is_64_bit_) {
+    *address = context_.t64.fs_base;
+    return true;
+  }
+
+  user_desc desc;
+  iovec iov;
+  iov.iov_base = &desc;
+  iov.iov_len = sizeof(desc);
+  *address = 0;
+  if (ptrace(
+          PTRACE_GETREGSET, tid_, reinterpret_cast<void*>(NT_386_TLS), &iov) !=
+      0) {
+    PLOG(ERROR) << "ptrace";
+    return false;
+  }
+  *address = desc.base_addr;
+  return true;
+
+#elif defined(ARCH_CPU_ARM_FAMILY)
+  if (is_64_bit_) {
+    iovec iov;
+    iov.iov_base = address;
+    iov.iov_len = sizeof(*address);
+    if (ptrace(PTRACE_GETREGSET,
+               tid_,
+               reinterpret_cast<void*>(NT_ARM_TLS),
+               &iov) != 0) {
+      PLOG(ERROR) << "ptrace";
+      return false;
+    }
+    if (iov.iov_len != 8) {
+      LOG(ERROR) << "address size mismatch";
+      return false;
+    }
+    return true;
+  }
+
+#if defined(ARCH_CPU_ARMEL)
+  void* result;
+  if (ptrace(PTRACE_GET_THREAD_AREA, tid_, nullptr, &result) != 0) {
+    PLOG(ERROR) << "ptrace";
+    return false;
+  }
+  *address = FromPointerCast<LinuxVMAddress>(result);
+  return true;
+#else
+  // TODO(jperaza): it doesn't look like there is a way for a 64-bit ARM process
+  // to get the thread area for a 32-bit ARM process with ptrace.
+  LOG(WARNING) << "64-bit ARM cannot trace TLS area for a 32-bit process";
+  return false;
+#endif  // ARCH_CPU_ARMEL
+#else
+#error Port.
+#endif  // ARCH_CPU_X86_FAMILY
+}
+
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/linux/thread_info.h b/third_party/crashpad/crashpad/util/linux/thread_info.h
new file mode 100644
index 0000000..6fb2621d
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/linux/thread_info.h
@@ -0,0 +1,284 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_LINUX_THREAD_INFO_H_
+#define CRASHPAD_UTIL_LINUX_THREAD_INFO_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/user.h>
+
+#include <type_traits>
+
+#include "build/build_config.h"
+#include "util/linux/address_types.h"
+#include "util/linux/scoped_ptrace_attach.h"
+#include "util/misc/initialization_state_dcheck.h"
+#include "util/numeric/int128.h"
+
+namespace crashpad {
+
+//! \brief The set of general purpose registers for an architecture family.
+union ThreadContext {
+  ThreadContext();
+  ~ThreadContext();
+
+  //! \brief The general purpose registers used by the 32-bit variant of the
+  //!     architecture.
+  struct t32 {
+#if defined(ARCH_CPU_X86_FAMILY)
+    // Reflects user_regs_struct in sys/user.h.
+    uint32_t ebx;
+    uint32_t ecx;
+    uint32_t edx;
+    uint32_t esi;
+    uint32_t edi;
+    uint32_t ebp;
+    uint32_t eax;
+    uint32_t xds;
+    uint32_t xes;
+    uint32_t xfs;
+    uint32_t xgs;
+    uint32_t orig_eax;
+    uint32_t eip;
+    uint32_t xcs;
+    uint32_t eflags;
+    uint32_t esp;
+    uint32_t xss;
+#elif defined(ARCH_CPU_ARM_FAMILY)
+    // Reflects user_regs in sys/user.h.
+    uint32_t regs[11];
+    uint32_t fp;
+    uint32_t ip;
+    uint32_t sp;
+    uint32_t lr;
+    uint32_t pc;
+    uint32_t cpsr;
+    uint32_t orig_r0;
+#else
+#error Port.
+#endif  // ARCH_CPU_X86_FAMILY
+  } t32;
+
+  //! \brief The general purpose registers used by the 64-bit variant of the
+  //!     architecture.
+  struct t64 {
+#if defined(ARCH_CPU_X86_FAMILY)
+    // Reflects user_regs_struct in sys/user.h.
+    uint64_t r15;
+    uint64_t r14;
+    uint64_t r13;
+    uint64_t r12;
+    uint64_t rbp;
+    uint64_t rbx;
+    uint64_t r11;
+    uint64_t r10;
+    uint64_t r9;
+    uint64_t r8;
+    uint64_t rax;
+    uint64_t rcx;
+    uint64_t rdx;
+    uint64_t rsi;
+    uint64_t rdi;
+    uint64_t orig_rax;
+    uint64_t rip;
+    uint64_t cs;
+    uint64_t eflags;
+    uint64_t rsp;
+    uint64_t ss;
+    uint64_t fs_base;
+    uint64_t gs_base;
+    uint64_t ds;
+    uint64_t es;
+    uint64_t fs;
+    uint64_t gs;
+#elif defined(ARCH_CPU_ARM_FAMILY)
+    // Reflects user_regs_struct in sys/user.h.
+    uint64_t regs[31];
+    uint64_t sp;
+    uint64_t pc;
+    uint64_t pstate;
+#else
+#error Port.
+#endif  // ARCH_CPU_X86_FAMILY
+  } t64;
+
+#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
+  using NativeThreadContext = user_regs_struct;
+#elif defined(ARCH_CPU_ARMEL)
+  using NativeThreadContext = user_regs;
+#else
+#error Port.
+#endif  // ARCH_CPU_X86_FAMILY || ARCH_CPU_ARM64
+
+#if defined(ARCH_CPU_32_BITS)
+  static_assert(sizeof(t32) == sizeof(NativeThreadContext), "Size mismatch");
+#else  // ARCH_CPU_64_BITS
+  static_assert(sizeof(t64) == sizeof(NativeThreadContext), "Size mismatch");
+#endif  // ARCH_CPU_32_BITS
+};
+static_assert(std::is_standard_layout<ThreadContext>::value,
+              "Not standard layout");
+
+//! \brief The floating point registers used for an architecture family.
+union FloatContext {
+  FloatContext();
+  ~FloatContext();
+
+  //! \brief The floating point registers used by the 32-bit variant of the
+  //!     architecture.
+  struct f32 {
+#if defined(ARCH_CPU_X86_FAMILY)
+    // Reflects user_fpregs_struct in sys/user.h.
+    uint32_t cwd;
+    uint32_t swd;
+    uint32_t twd;
+    uint32_t fip;
+    uint32_t fcs;
+    uint32_t foo;
+    uint32_t fos;
+    uint32_t st_space[20];
+#elif defined(ARCH_CPU_ARM_FAMILY)
+    // Reflects user_fpregs in sys/user.h.
+    struct fpregs {
+      struct fp_reg {
+        uint32_t sign1 : 1;
+        uint32_t unused : 15;
+        uint32_t sign2 : 1;
+        uint32_t exponent : 14;
+        uint32_t j : 1;
+        uint32_t mantissa1 : 31;
+        uint32_t mantisss0 : 32;
+      } fpregs[8];
+      uint32_t fpsr : 32;
+      uint32_t fpcr : 32;
+      uint8_t type[8];
+      uint32_t init_flag;
+    } fpregs;
+
+    // Reflects user_vfp in sys/user.h.
+    struct vfp {
+      uint64_t fpregs[32];
+      uint32_t fpscr;
+    } vfp;
+
+    bool have_fpregs;
+    bool have_vfp;
+#else
+#error Port.
+#endif  // ARCH_CPU_X86_FAMILY
+  } f32;
+
+  //! \brief The floating point registers used by the 64-bit variant of the
+  //!     architecture.
+  struct f64 {
+#if defined(ARCH_CPU_X86_FAMILY)
+    uint16_t cwd;
+    uint16_t swd;
+    uint16_t ftw;
+    uint16_t fop;
+    uint64_t rip;
+    uint64_t rdp;
+    uint32_t mxcsr;
+    uint32_t mxcr_mask;
+    uint32_t st_space[32];
+    uint32_t xmm_space[64];
+    uint32_t padding[24];
+#elif defined(ARCH_CPU_ARM_FAMILY)
+    uint128_struct vregs[32];
+    uint32_t fpsr;
+    uint32_t fpcr;
+    uint8_t padding[8];
+#else
+#error Port.
+#endif  // ARCH_CPU_X86_FAMILY
+  } f64;
+
+#if defined(ARCH_CPU_X86)
+  static_assert(sizeof(f32) == sizeof(user_fpregs_struct), "Size mismatch");
+#elif defined(ARCH_CPU_X86_64)
+  static_assert(sizeof(f64) == sizeof(user_fpregs_struct), "Size mismatch");
+#elif defined(ARCH_CPU_ARMEL)
+  static_assert(sizeof(f32::fpregs) == sizeof(user_fpregs), "Size mismatch");
+  static_assert(sizeof(f32::vfp) == sizeof(user_vfp), "Size mismatch");
+#elif defined(ARCH_CPU_ARM64)
+  static_assert(sizeof(f64) == sizeof(user_fpsimd_struct), "Size mismatch");
+#else
+#error Port.
+#endif  // ARCH_CPU_X86
+};
+static_assert(std::is_standard_layout<FloatContext>::value,
+              "Not standard layout");
+
+class ThreadInfo {
+ public:
+  ThreadInfo();
+  ~ThreadInfo();
+
+  //! \brief Initializes this object with information about the thread whose ID
+  //!     is \a tid.
+  //!
+  //! This method must be called successfully prior to calling any other method
+  //! in this class. This method may only be called once.
+  //!
+  //! It is unspecified whether the information that an object of this class
+  //! returns is loaded at the time Initialize() is called or subsequently, and
+  //! whether this information is cached in the object or not.
+  //!
+  //! \param[in] tid The thread ID to obtain information for.
+  //!
+  //! \return `true` on success, `false` on failure with a message logged.
+  bool Initialize(pid_t tid);
+
+  //! \brief Determines the target thread’s bitness.
+  //!
+  //! \return `true` if the target is 64-bit.
+  bool Is64Bit();
+
+  //! \brief Uses `ptrace` to collect general purpose registers from the target
+  //!     thread and places the result in \a context.
+  //!
+  //! \param[out] context The registers read from the target thread.
+  void GetGeneralPurposeRegisters(ThreadContext* context);
+
+  //! \brief Uses `ptrace` to collect floating point registers from the target
+  //!     thread and places the result in \a context.
+  //!
+  //! \param[out] context The registers read from the target thread.
+  //!
+  //! \return `true` on success, with \a context set. Otherwise, `false` with a
+  //!     message logged.
+  bool GetFloatingPointRegisters(FloatContext* context);
+
+  //! \brief Uses `ptrace` to determine the thread-local storage address for the
+  //!     target thread and places the result in \a address.
+  //!
+  //! \param[out] address The address of the TLS area.
+  //!
+  //! \return `true` on success. `false` on failure with a message logged.
+  bool GetThreadArea(LinuxVMAddress* address);
+
+ private:
+  size_t GetGeneralPurposeRegistersAndLength(ThreadContext* context);
+
+  ThreadContext context_;
+  ScopedPtraceAttach attachment_;
+  pid_t tid_;
+  InitializationStateDcheck initialized_;
+  bool is_64_bit_;
+};
+
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_LINUX_THREAD_INFO_H_
diff --git a/third_party/crashpad/crashpad/util/linux/thread_info_test.cc b/third_party/crashpad/crashpad/util/linux/thread_info_test.cc
new file mode 100644
index 0000000..fe05b4a
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/linux/thread_info_test.cc
@@ -0,0 +1,100 @@
+// Copyright 2017 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/linux/thread_info.h"
+
+#include "build/build_config.h"
+#include "gtest/gtest.h"
+#include "test/multiprocess.h"
+#include "util/file/file_io.h"
+#include "util/misc/from_pointer_cast.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+class SameBitnessTest : public Multiprocess {
+ public:
+  SameBitnessTest() : Multiprocess() {}
+  ~SameBitnessTest() {}
+
+ private:
+  void MultiprocessParent() override {
+    LinuxVMAddress expected_tls;
+    CheckedReadFileExactly(
+        ReadPipeHandle(), &expected_tls, sizeof(expected_tls));
+
+    ThreadInfo thread_info;
+    ASSERT_TRUE(thread_info.Initialize(ChildPID()));
+
+#if defined(ARCH_CPU_64_BITS)
+    const bool am_64_bit = true;
+#else
+    const bool am_64_bit = false;
+#endif  // ARCH_CPU_64_BITS
+
+    EXPECT_EQ(thread_info.Is64Bit(), am_64_bit);
+
+    ThreadContext thread_context;
+    thread_info.GetGeneralPurposeRegisters(&thread_context);
+
+#if defined(ARCH_CPU_X86_64)
+    EXPECT_EQ(thread_context.t64.fs_base, expected_tls);
+#endif  // ARCH_CPU_X86_64
+
+    FloatContext float_context;
+    EXPECT_TRUE(thread_info.GetFloatingPointRegisters(&float_context));
+
+    LinuxVMAddress tls_address;
+    ASSERT_TRUE(thread_info.GetThreadArea(&tls_address));
+    EXPECT_EQ(tls_address, expected_tls);
+  }
+
+  void MultiprocessChild() override {
+    LinuxVMAddress expected_tls;
+#if defined(ARCH_CPU_ARMEL)
+    // 0xffff0fe0 is the address of the kernel user helper __kuser_get_tls().
+    auto kuser_get_tls = reinterpret_cast<void* (*)()>(0xffff0fe0);
+    expected_tls = FromPointerCast<LinuxVMAddress>(kuser_get_tls());
+#elif defined(ARCH_CPU_ARM64)
+    // Linux/aarch64 places the tls address in system register tpidr_el0.
+    asm("mrs %0, tpidr_el0" : "=r"(expected_tls));
+#elif defined(ARCH_CPU_X86)
+    uint32_t expected_tls_32;
+    asm("movl %%gs:0x0, %0" : "=r"(expected_tls_32));
+    expected_tls = expected_tls_32;
+#elif defined(ARCH_CPU_X86_64)
+    asm("movq %%fs:0x0, %0" : "=r"(expected_tls));
+#else
+#error Port.
+#endif  // ARCH_CPU_ARMEL
+
+    CheckedWriteFile(WritePipeHandle(), &expected_tls, sizeof(expected_tls));
+
+    CheckedReadFileAtEOF(ReadPipeHandle());
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(SameBitnessTest);
+};
+
+TEST(ThreadInfo, SameBitness) {
+  SameBitnessTest test;
+  test.Run();
+}
+
+// TODO(jperaza): Test against a process with different bitness.
+
+}  // namespace
+}  // namespace test
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/posix/process_info_linux.cc b/third_party/crashpad/crashpad/util/posix/process_info_linux.cc
index 65d9644..ac3a2ae 100644
--- a/third_party/crashpad/crashpad/util/posix/process_info_linux.cc
+++ b/third_party/crashpad/crashpad/util/posix/process_info_linux.cc
@@ -15,12 +15,8 @@
 #include "util/posix/process_info.h"
 
 #include <ctype.h>
-#include <elf.h>
 #include <errno.h>
 #include <string.h>
-#include <sys/ptrace.h>
-#include <sys/uio.h>
-#include <sys/user.h>
 #include <time.h>
 #include <unistd.h>
 
@@ -33,7 +29,7 @@
 #include "util/file/delimited_file_reader.h"
 #include "util/file/file_io.h"
 #include "util/file/file_reader.h"
-#include "util/linux/scoped_ptrace_attach.h"
+#include "util/linux/thread_info.h"
 
 namespace crashpad {
 
@@ -305,57 +301,11 @@
     if (pid_ == getpid()) {
       is_64_bit_ = am_64_bit;
     } else {
-      ScopedPtraceAttach ptrace_attach;
-      if (!ptrace_attach.ResetAttach(pid_)) {
+      ThreadInfo thread_info;
+      if (!thread_info.Initialize(pid_)) {
         return false;
       }
-
-      // Allocate more buffer space than is required to hold registers for this
-      // process. If the kernel fills the extra space, the target process uses
-      // more/larger registers than this process. If the kernel fills less space
-      // than sizeof(regs) then the target process uses smaller/fewer registers.
-      struct {
-#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
-        using PrStatusType = user_regs_struct;
-#elif defined(ARCH_CPU_ARMEL)
-        using PrStatusType = user_regs;
-#endif
-        PrStatusType regs;
-        char extra;
-      } regbuf;
-
-      iovec iov;
-      iov.iov_base = &regbuf;
-      iov.iov_len = sizeof(regbuf);
-      if (ptrace(PTRACE_GETREGSET,
-                 pid_,
-                 reinterpret_cast<void*>(NT_PRSTATUS),
-                 &iov) != 0) {
-        switch (errno) {
-#if defined(ARCH_CPU_ARMEL)
-          case EIO:
-            // PTRACE_GETREGSET, introduced in Linux 2.6.34 (2225a122ae26),
-            // requires kernel support enabled by HAVE_ARCH_TRACEHOOK. This has
-            // been set for x86 (including x86_64) since Linux 2.6.28
-            // (99bbc4b1e677a), but for ARM only since Linux 3.5.0
-            // (0693bf68148c4). Fortunately, 64-bit ARM support only appeared in
-            // Linux 3.7.0, so if PTRACE_GETREGSET fails on ARM with EIO,
-            // indicating that the request is not supported, the kernel must be
-            // old enough that 64-bit ARM isn’t supported either.
-            //
-            // TODO(mark): Once helpers to interpret the kernel version are
-            // available, add a DCHECK to ensure that the kernel is older than
-            // 3.5.
-            is_64_bit_ = false;
-            break;
-#endif
-          default:
-            PLOG(ERROR) << "ptrace";
-            return false;
-        }
-      } else {
-        is_64_bit_ = am_64_bit == (iov.iov_len == sizeof(regbuf.regs));
-      }
+      is_64_bit_ = thread_info.Is64Bit();
     }
 
     is_64_bit_initialized_.set_valid();
diff --git a/third_party/crashpad/crashpad/util/stdlib/aligned_allocator_test.cc b/third_party/crashpad/crashpad/util/stdlib/aligned_allocator_test.cc
index 2799558e..4a018f5 100644
--- a/third_party/crashpad/crashpad/util/stdlib/aligned_allocator_test.cc
+++ b/third_party/crashpad/crashpad/util/stdlib/aligned_allocator_test.cc
@@ -16,6 +16,7 @@
 
 #include <stdint.h>
 
+#include "base/compiler_specific.h"
 #include "gtest/gtest.h"
 
 #if defined(OS_WIN)
diff --git a/third_party/crashpad/crashpad/util/util.gyp b/third_party/crashpad/crashpad/util/util.gyp
index e3074e9a..8e5c7cfe 100644
--- a/third_party/crashpad/crashpad/util/util.gyp
+++ b/third_party/crashpad/crashpad/util/util.gyp
@@ -45,11 +45,15 @@
         'file/string_file.cc',
         'file/string_file.h',
         'linux/address_types.h',
+        'linux/auxiliary_vector.cc',
+        'linux/auxiliary_vector.h',
         'linux/checked_address_range.h',
         'linux/memory_map.cc',
         'linux/memory_map.h',
         'linux/process_memory.cc',
         'linux/process_memory.h',
+        'linux/thread_info.cc',
+        'linux/thread_info.h',
         'linux/scoped_ptrace_attach.cc',
         'linux/scoped_ptrace_attach.h',
         'mac/checked_mach_address_range.h',
diff --git a/third_party/crashpad/crashpad/util/util_test.gyp b/third_party/crashpad/crashpad/util/util_test.gyp
index db54a9b0..ef5a681 100644
--- a/third_party/crashpad/crashpad/util/util_test.gyp
+++ b/third_party/crashpad/crashpad/util/util_test.gyp
@@ -39,8 +39,10 @@
         'file/file_io_test.cc',
         'file/file_reader_test.cc',
         'file/string_file_test.cc',
+        'linux/auxiliary_vector_test.cc',
         'linux/memory_map_test.cc',
         'linux/process_memory_test.cc',
+        'linux/thread_info_test.cc',
         'linux/scoped_ptrace_attach_test.cc',
         'mac/launchd_test.mm',
         'mac/mac_util_test.mm',
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index cbf76fc1..662f9a8 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -269,6 +269,40 @@
   </metric>
 </event>
 
+<event name="Event.ScrollBegin.Touch">
+  <owner>nzolghadr@chromium.org</owner>
+  <summary>
+    Metrics related to first scroll action caused by the generated ScrollUpdate
+    gesture event.
+  </summary>
+  <metric name="TimeToScrollUpdateSwapBegin">
+    <summary>
+      The time in microseconds between initial creation of a touch event and the
+      start of the frame swap on the GPU service caused by the generated
+      ScrollUpdate gesture event if that ScrollUpdate is the first such event in
+      a given scroll gesture event sequence. If no swap was induced by the
+      event, no recording is made.
+    </summary>
+  </metric>
+</event>
+
+<event name="Event.ScrollBegin.Wheel">
+  <owner>nzolghadr@chromium.org</owner>
+  <summary>
+    Metrics related to first scroll action caused by the generated ScrollUpdate
+    gesture event.
+  </summary>
+  <metric name="TimeToScrollUpdateSwapBegin">
+    <summary>
+      The time in microseconds between the initial creation of a wheel event and
+      the start of the frame swap on the GPU service caused by the generated
+      ScrollUpdate gesture event if that ScrollUpdate is the first such event in
+      a given scroll gesture event sequence. If no swap was induced by the
+      event, no recording is made.
+    </summary>
+  </metric>
+</event>
+
 <event name="Media.WatchTime">
   <owner>dalecurtis@chromium.org</owner>
   <summary>
diff --git a/ui/app_list/views/page_switcher_vertical.cc b/ui/app_list/views/page_switcher_vertical.cc
index 3155bf7d..a22d1d9 100644
--- a/ui/app_list/views/page_switcher_vertical.cc
+++ b/ui/app_list/views/page_switcher_vertical.cc
@@ -14,6 +14,11 @@
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/skia_util.h"
+#include "ui/views/animation/flood_fill_ink_drop_ripple.h"
+#include "ui/views/animation/ink_drop_highlight.h"
+#include "ui/views/animation/ink_drop_impl.h"
+#include "ui/views/animation/ink_drop_mask.h"
+#include "ui/views/animation/ink_drop_painted_layer_delegates.h"
 #include "ui/views/controls/button/custom_button.h"
 #include "ui/views/layout/box_layout.h"
 
@@ -21,103 +26,137 @@
 
 namespace {
 
-const int kPreferredWidth = 58;
+constexpr int kNormalButtonRadius = 3;
+constexpr int kSelectedButtonRadius = 4;
+constexpr int kInkDropRadius = 8;
+// The padding on top/bottom side of each button.
+constexpr int kButtonPadding = 12;
+// The padding of a list of buttons as a button strip.
+constexpr int kButtonStripPadding = 24;
+constexpr int kMaxButtonRadius = 8;
+constexpr int kPreferredButtonStripWidth =
+    kMaxButtonRadius * 2 + kButtonStripPadding * 2;
 
-const int kMaxButtonSpacing = 18;
-const int kMinButtonSpacing = 4;
-const int kMaxButtonHeight = 68;
-const int kMinButtonHeight = 28;
-const int kButtonWidth = 6;
-const int kButtonCornerRadius = 2;
-const int kButtonStripPadding = 20;
+// The selected button color.
+constexpr SkColor kSelectedButtonColor = SK_ColorWHITE;
+// The normal button color (54% white).
+constexpr SkColor kNormalColor = SkColorSetA(SK_ColorWHITE, 138);
+constexpr SkColor kInkDropBaseColor = SK_ColorWHITE;
+constexpr SkColor kInkDropRippleColor = SkColorSetA(kInkDropBaseColor, 20);
+constexpr SkColor kInkDropHighlightColor = SkColorSetA(kInkDropBaseColor, 15);
+
+constexpr SkScalar kStrokeWidth = SkIntToScalar(1);
 
 class PageSwitcherButton : public views::CustomButton {
  public:
   explicit PageSwitcherButton(views::ButtonListener* listener)
-      : views::CustomButton(listener),
-        button_height_(kMaxButtonHeight),
-        selected_range_(0) {}
-  ~PageSwitcherButton() override {}
-
-  void SetSelectedRange(double selected_range) {
-    if (selected_range_ == selected_range)
-      return;
-
-    selected_range_ = selected_range;
-    SchedulePaint();
+      : views::CustomButton(listener) {
+    SetInkDropMode(InkDropMode::ON);
   }
 
-  void set_button_height(int button_height) { button_height_ = button_height; }
+  ~PageSwitcherButton() override {}
+
+  void SetSelected(bool selected) {
+    if (selected == selected_)
+      return;
+
+    selected_ = selected;
+    SchedulePaint();
+  }
 
   // Overridden from views::View:
   gfx::Size CalculatePreferredSize() const override {
-    return gfx::Size(kButtonWidth, button_height_);
+    return gfx::Size(kMaxButtonRadius * 2, kMaxButtonRadius * 2);
   }
 
   void PaintButtonContents(gfx::Canvas* canvas) override {
-    if (state() == STATE_HOVERED)
-      PaintButton(canvas, kPagerHoverColor);
-    else
-      PaintButton(canvas, kPagerNormalColor);
+    PaintButton(canvas, BuildPaintButtonInfo());
   }
 
-  void OnGestureEvent(ui::GestureEvent* event) override {
-    CustomButton::OnGestureEvent(event);
+ protected:
+  bool IsTriggerableEvent(const ui::Event& event) override {
+    return event.IsMouseEvent() &&
+           (triggerable_event_flags() & event.flags()) != 0;
+  }
 
-    if (event->type() == ui::ET_GESTURE_TAP_DOWN)
-      SetState(views::CustomButton::STATE_HOVERED);
-    else if (event->type() == ui::ET_GESTURE_TAP_CANCEL ||
-             event->type() == ui::ET_GESTURE_TAP)
-      SetState(views::CustomButton::STATE_NORMAL);
-    SchedulePaint();
+  std::unique_ptr<views::InkDrop> CreateInkDrop() override {
+    std::unique_ptr<views::InkDropImpl> ink_drop =
+        CustomButton::CreateDefaultInkDropImpl();
+    ink_drop->SetShowHighlightOnHover(false);
+    ink_drop->SetAutoHighlightMode(
+        views::InkDropImpl::AutoHighlightMode::SHOW_ON_RIPPLE);
+    return std::move(ink_drop);
+  }
+
+  std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override {
+    return base::MakeUnique<views::CircleInkDropMask>(
+        size(), GetLocalBounds().CenterPoint(), kMaxButtonRadius);
+  }
+
+  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override {
+    gfx::Point center = GetLocalBounds().CenterPoint();
+    gfx::Rect bounds(center.x() - kMaxButtonRadius,
+                     center.y() - kMaxButtonRadius, 2 * kMaxButtonRadius,
+                     2 * kMaxButtonRadius);
+    return base::MakeUnique<views::FloodFillInkDropRipple>(
+        size(), GetLocalBounds().InsetsFrom(bounds),
+        GetInkDropCenterBasedOnLastEvent(), kInkDropRippleColor, 1.0f);
+  }
+
+  std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight()
+      const override {
+    return base::MakeUnique<views::InkDropHighlight>(
+        gfx::PointF(GetLocalBounds().CenterPoint()),
+        base::MakeUnique<views::CircleLayerDelegate>(kInkDropHighlightColor,
+                                                     kInkDropRadius));
+  }
+
+  void NotifyClick(const ui::Event& event) override {
+    CustomButton::NotifyClick(event);
+    GetInkDrop()->AnimateToState(views::InkDropState::ACTION_TRIGGERED);
   }
 
  private:
-  // Paints a button that has two rounded corner at bottom.
-  void PaintButton(gfx::Canvas* canvas, SkColor base_color) {
-    gfx::Rect rect(GetContentsBounds());
-    rect.ClampToCenteredSize(gfx::Size(kButtonWidth, button_height_));
+  // Stores the information of how to paint the button.
+  struct PaintButtonInfo {
+    SkColor color;
+    cc::PaintFlags::Style style;
+    SkScalar radius;
+    SkScalar stroke_width;
+  };
 
+  // Returns the information of how to paint selected/normal button.
+  PaintButtonInfo BuildPaintButtonInfo() {
+    PaintButtonInfo info;
+    if (selected_) {
+      info.color = kSelectedButtonColor;
+      info.style = cc::PaintFlags::kFill_Style;
+      info.radius = SkIntToScalar(kSelectedButtonRadius);
+      info.stroke_width = SkIntToScalar(0);
+    } else {
+      info.color = kNormalColor;
+      info.style = cc::PaintFlags::kStroke_Style;
+      info.radius = SkIntToScalar(kNormalButtonRadius);
+      info.stroke_width = kStrokeWidth;
+    }
+    return info;
+  }
+
+  // Paints a button based on the |info|.
+  void PaintButton(gfx::Canvas* canvas, PaintButtonInfo info) {
+    gfx::Rect rect(GetContentsBounds());
     SkPath path;
-    path.addRoundRect(gfx::RectToSkRect(rect),
-                      SkIntToScalar(kButtonCornerRadius),
-                      SkIntToScalar(kButtonCornerRadius));
+    path.addCircle(rect.CenterPoint().x(), rect.CenterPoint().y(), info.radius);
 
     cc::PaintFlags flags;
     flags.setAntiAlias(true);
-    flags.setStyle(cc::PaintFlags::kFill_Style);
-    flags.setColor(base_color);
+    flags.setStyle(info.style);
+    flags.setColor(info.color);
     canvas->DrawPath(path, flags);
-
-    int selected_start_y = 0;
-    int selected_height = 0;
-    if (selected_range_ > 0) {
-      selected_height = selected_range_ * rect.height();
-    } else if (selected_range_ < 0) {
-      selected_height = -selected_range_ * rect.height();
-      selected_start_y = rect.bottom() - selected_height;
-    }
-
-    if (selected_height) {
-      gfx::Rect selected_rect(rect);
-      selected_rect.set_y(selected_start_y);
-      selected_rect.set_height(selected_height);
-
-      SkPath selected_path;
-      selected_path.addRoundRect(gfx::RectToSkRect(selected_rect),
-                                 SkIntToScalar(kButtonCornerRadius),
-                                 SkIntToScalar(kButtonCornerRadius));
-      flags.setColor(kPagerSelectedColor);
-      canvas->DrawPath(selected_path, flags);
-    }
   }
 
-  int button_height_;
-
-  // [-1, 1] range that represents the portion of the button that should be
-  // painted with kSelectedColor. Positive range starts from top side and
-  // negative range starts from the bottom side.
-  double selected_range_;
+  // If this button is selected, set to true. By default, set to false;
+  bool selected_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(PageSwitcherButton);
 };
@@ -131,6 +170,10 @@
 
 PageSwitcherVertical::PageSwitcherVertical(PaginationModel* model)
     : model_(model), buttons_(new views::View) {
+  buttons_->SetLayoutManager(new views::BoxLayout(
+      views::BoxLayout::kVertical,
+      gfx::Insets(kButtonStripPadding, kButtonStripPadding), kButtonPadding));
+
   AddChildView(buttons_);
 
   TotalPagesChanged();
@@ -179,14 +222,13 @@
 gfx::Size PageSwitcherVertical::CalculatePreferredSize() const {
   // Always return a size with correct width so that container resize is not
   // needed when more pages are added.
-  return gfx::Size(kPreferredWidth, buttons_->GetPreferredSize().height());
+  return gfx::Size(kPreferredButtonStripWidth,
+                   buttons_->GetPreferredSize().height());
 }
 
 void PageSwitcherVertical::Layout() {
   gfx::Rect rect(GetContentsBounds());
 
-  CalculateButtonHeightAndSpacing(rect.height());
-
   // Makes |buttons_| vertically center and horizontally fill.
   gfx::Size buttons_size(buttons_->GetPreferredSize());
   gfx::Rect buttons_bounds(rect.x(),
@@ -195,38 +237,6 @@
   buttons_->SetBoundsRect(gfx::IntersectRects(rect, buttons_bounds));
 }
 
-void PageSwitcherVertical::CalculateButtonHeightAndSpacing(
-    int contents_height) {
-  const int button_count = buttons_->child_count();
-  if (!button_count)
-    return;
-
-  contents_height -= 2 * kButtonStripPadding;
-
-  int button_height = kMinButtonHeight;
-  int button_spacing = kMinButtonSpacing;
-  if (button_count > 1) {
-    button_spacing =
-        (contents_height - button_height * button_count) / (button_count - 1);
-    button_spacing = std::min(kMaxButtonSpacing,
-                              std::max(kMinButtonSpacing, button_spacing));
-  }
-
-  button_height =
-      (contents_height - (button_count - 1) * button_spacing) / button_count;
-  button_height =
-      std::min(kMaxButtonHeight, std::max(kMinButtonHeight, button_height));
-
-  buttons_->SetLayoutManager(new views::BoxLayout(
-      views::BoxLayout::kVertical, gfx::Insets(kButtonStripPadding, 0),
-      button_spacing));
-  for (int i = 0; i < button_count; ++i) {
-    PageSwitcherButton* button =
-        static_cast<PageSwitcherButton*>(buttons_->child_at(i));
-    button->set_button_height(button_height);
-  }
-}
-
 void PageSwitcherVertical::ButtonPressed(views::Button* sender,
                                          const ui::Event& event) {
   for (int i = 0; i < buttons_->child_count(); ++i) {
@@ -241,7 +251,7 @@
   buttons_->RemoveAllChildViews(true);
   for (int i = 0; i < model_->total_pages(); ++i) {
     PageSwitcherButton* button = new PageSwitcherButton(this);
-    button->SetSelectedRange(i == model_->selected_page() ? 1 : 0);
+    button->SetSelected(i == model_->selected_page() ? true : false);
     buttons_->AddChildView(button);
   }
   buttons_->SetVisible(model_->total_pages() > 1);
@@ -251,28 +261,13 @@
 void PageSwitcherVertical::SelectedPageChanged(int old_selected,
                                                int new_selected) {
   if (old_selected >= 0 && old_selected < buttons_->child_count())
-    GetButtonByIndex(buttons_, old_selected)->SetSelectedRange(0);
+    GetButtonByIndex(buttons_, old_selected)->SetSelected(false);
   if (new_selected >= 0 && new_selected < buttons_->child_count())
-    GetButtonByIndex(buttons_, new_selected)->SetSelectedRange(1);
+    GetButtonByIndex(buttons_, new_selected)->SetSelected(true);
 }
 
 void PageSwitcherVertical::TransitionStarted() {}
 
-void PageSwitcherVertical::TransitionChanged() {
-  const int current_page = model_->selected_page();
-  const int target_page = model_->transition().target_page;
-
-  double progress = model_->transition().progress;
-  double remaining = progress - 1;
-
-  if (current_page > target_page) {
-    remaining = -remaining;
-    progress = -progress;
-  }
-
-  GetButtonByIndex(buttons_, current_page)->SetSelectedRange(remaining);
-  if (model_->is_valid_page(target_page))
-    GetButtonByIndex(buttons_, target_page)->SetSelectedRange(progress);
-}
+void PageSwitcherVertical::TransitionChanged() {}
 
 }  // namespace app_list
diff --git a/ui/app_list/views/page_switcher_vertical.h b/ui/app_list/views/page_switcher_vertical.h
index 0f2198ad..39547a4 100644
--- a/ui/app_list/views/page_switcher_vertical.h
+++ b/ui/app_list/views/page_switcher_vertical.h
@@ -33,8 +33,6 @@
   void Layout() override;
 
  private:
-  void CalculateButtonHeightAndSpacing(int contents_height);
-
   // Overridden from views::ButtonListener:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
 
diff --git a/ui/latency/latency_tracker.cc b/ui/latency/latency_tracker.cc
index 494a89c..5be7a26c 100644
--- a/ui/latency/latency_tracker.cc
+++ b/ui/latency/latency_tracker.cc
@@ -159,6 +159,10 @@
                                   ".TimeToScrollUpdateSwapBegin2",
                               original_component, gpu_swap_begin_component);
 
+    ReportUkmScrollLatency("Event.ScrollBegin." + input_modality,
+                           "TimeToScrollUpdateSwapBegin", original_component,
+                           gpu_swap_begin_component);
+
     // TODO(lanwei): Will remove them when M56 is stable, see
     // https://crbug.com/669618.
     UMA_HISTOGRAM_INPUT_LATENCY_HIGH_RESOLUTION_MICROSECONDS(
@@ -179,6 +183,7 @@
       ReportRapporScrollLatency(
           "Event.Latency.ScrollUpdate.Touch.TimeToScrollUpdateSwapBegin2",
           original_component, gpu_swap_begin_component);
+
       ReportUkmScrollLatency("Event.ScrollUpdate.Touch",
                              "TimeToScrollUpdateSwapBegin", original_component,
                              gpu_swap_begin_component);
diff --git a/ui/views/bubble/bubble_dialog_delegate.h b/ui/views/bubble/bubble_dialog_delegate.h
index e027988d..76d0235 100644
--- a/ui/views/bubble/bubble_dialog_delegate.h
+++ b/ui/views/bubble/bubble_dialog_delegate.h
@@ -148,7 +148,7 @@
   void SetAnchorRect(const gfx::Rect& rect);
 
   // Resize and potentially move the bubble to fit the content's preferred size.
-  void SizeToContents();
+  virtual void SizeToContents();
 
   BubbleFrameView* GetBubbleFrameView() const;
 
diff --git a/ui/views/bubble/tray_bubble_view.cc b/ui/views/bubble/tray_bubble_view.cc
index 9f106c1..816b701 100644
--- a/ui/views/bubble/tray_bubble_view.cc
+++ b/ui/views/bubble/tray_bubble_view.cc
@@ -265,6 +265,11 @@
   return ui::DIALOG_BUTTON_NONE;
 }
 
+void TrayBubbleView::SizeToContents() {
+  BubbleDialogDelegateView::SizeToContents();
+  bubble_content_mask_->layer()->SetBounds(layer()->bounds());
+}
+
 void TrayBubbleView::OnBeforeBubbleWidgetInit(Widget::InitParams* params,
                                               Widget* bubble_widget) const {
   // Apply a WM-provided shadow (see ui/wm/core/).
diff --git a/ui/views/bubble/tray_bubble_view.h b/ui/views/bubble/tray_bubble_view.h
index 81042a8..0a11f8a 100644
--- a/ui/views/bubble/tray_bubble_view.h
+++ b/ui/views/bubble/tray_bubble_view.h
@@ -145,6 +145,7 @@
  protected:
   // Overridden from views::BubbleDialogDelegateView.
   int GetDialogButtons() const override;
+  void SizeToContents() override;
 
   // Overridden from views::View.
   void ChildPreferredSizeChanged(View* child) override;