diff --git a/DEPS b/DEPS
index 48b461da1..0b902f3 100644
--- a/DEPS
+++ b/DEPS
@@ -74,7 +74,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '06ab3836f779dbcbcd067a9261300616ff7cc594',
+  'skia_revision': 'a3e9271ec41db6c3b6886e50053f37d345ab1d5c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -130,7 +130,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': '37c17ee783f358fa01ae994ed89162722b961616',
+  'catapult_revision': '09fc536c66c644ad0ec55b8048d5efe11e4af7c7',
   # 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/ash/wm/overview/window_selector_controller.cc b/ash/wm/overview/window_selector_controller.cc
index d95d165..e6d3e3a 100644
--- a/ash/wm/overview/window_selector_controller.cc
+++ b/ash/wm/overview/window_selector_controller.cc
@@ -20,6 +20,7 @@
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
 
 namespace ash {
 
@@ -165,6 +166,8 @@
     // in overview mode. To work around this set |active_window| before exiting
     // split view.
     wm::ActivateWindow(active_window);
+    base::RecordAction(
+        base::UserMetricsAction("Tablet_LongPressOverviewButtonExitSplitView"));
     return;
   }
 
@@ -187,6 +190,8 @@
     // mode.
     split_view_controller->SnapWindow(active_window, SplitViewController::LEFT);
     ToggleOverview();
+    base::RecordAction(base::UserMetricsAction(
+        "Tablet_LongPressOverviewButtonEnterSplitView"));
     return;
   }
 
@@ -219,6 +224,8 @@
   window_selector_->SetBoundsForWindowGridsInScreen(
       split_view_controller->GetSnappedWindowBoundsInScreen(
           window, SplitViewController::RIGHT));
+  base::RecordAction(
+      base::UserMetricsAction("Tablet_LongPressOverviewButtonEnterSplitView"));
 }
 
 std::vector<aura::Window*>
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index addf46b..5bbff42 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -23,6 +23,8 @@
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
 #include "base/command_line.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
 #include "base/optional.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_delegate.h"
@@ -151,6 +153,7 @@
     default_snap_position_ = snap_position;
     split_view_divider_ =
         std::make_unique<SplitViewDivider>(this, window->GetRootWindow());
+    splitview_start_time_ = base::Time::Now();
   }
 
   State previous_state = state_;
@@ -189,6 +192,7 @@
     window->parent()->StackChildBelow(stacking_target, window);
 
   NotifySplitViewStateChanged(previous_state, state_);
+  base::RecordAction(base::UserMetricsAction("SplitView_SnapWindow"));
 }
 
 void SplitViewController::SwapWindows() {
@@ -202,6 +206,9 @@
 
   SnapWindow(new_left_window, LEFT);
   SnapWindow(new_right_window, RIGHT);
+
+  base::RecordAction(
+      base::UserMetricsAction("SplitView_DoubleTapDividerSwapWindows"));
 }
 
 aura::Window* SplitViewController::GetDefaultSnappedWindow() {
@@ -271,6 +278,7 @@
   is_resizing_ = true;
   split_view_divider_->UpdateDividerBounds(is_resizing_);
   previous_event_location_ = location_in_screen;
+  base::RecordAction(base::UserMetricsAction("SplitView_ResizeWindows"));
 }
 
 void SplitViewController::Resize(const gfx::Point& location_in_screen) {
@@ -358,6 +366,9 @@
   NotifySplitViewStateChanged(previous_state, state_);
 
   Shell::Get()->NotifySplitViewModeEnded();
+  base::RecordAction(base::UserMetricsAction("SplitView_EndSplitView"));
+  UMA_HISTOGRAM_LONG_TIMES("Ash.SplitView.TimeInSplitView",
+                           base::Time::Now() - splitview_start_time_);
 }
 
 void SplitViewController::AddObserver(Observer* observer) {
diff --git a/ash/wm/splitview/split_view_controller.h b/ash/wm/splitview/split_view_controller.h
index 515b70fde..84fc18d4 100644
--- a/ash/wm/splitview/split_view_controller.h
+++ b/ash/wm/splitview/split_view_controller.h
@@ -11,6 +11,7 @@
 #include "ash/wm/window_state_observer.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
+#include "base/time/time.h"
 #include "third_party/WebKit/public/platform/modules/screen_orientation/WebScreenOrientationLockType.h"
 #include "ui/aura/window_observer.h"
 #include "ui/display/display.h"
@@ -280,6 +281,9 @@
   // If the divider is currently being dragging.
   bool is_resizing_ = false;
 
+  // The time when splitview starts. Used for metric collection purpose.
+  base::Time splitview_start_time_;
+
   base::ObserverList<Observer> observers_;
 
   DISALLOW_COPY_AND_ASSIGN(SplitViewController);
diff --git a/build/secondary/third_party/android_platform/development/scripts/BUILD.gn b/build/secondary/third_party/android_platform/development/scripts/BUILD.gn
index d808824..2ac12d7 100644
--- a/build/secondary/third_party/android_platform/development/scripts/BUILD.gn
+++ b/build/secondary/third_party/android_platform/development/scripts/BUILD.gn
@@ -13,5 +13,9 @@
   sources = _py_files
   data = sources
 
-  data += [ "//third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer" ]
+  data += [
+    "${android_tool_prefix}addr2line",
+    "${android_tool_prefix}objdump",
+    "${android_tool_prefix}c++filt",
+  ]
 }
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 5026d38..00b78eac 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -1878,6 +1878,7 @@
                                      LayerImpl* root_layer,
                                      const Functor& func,
                                      FindClosestMatchingLayerState* state) {
+  base::ElapsedTimer timer;
   // We want to iterate from front to back when hit testing.
   for (auto* layer : base::Reversed(*root_layer->layer_tree_impl())) {
     if (!func(layer))
@@ -1906,6 +1907,12 @@
       state->closest_match = layer;
     }
   }
+  if (const char* client_name = GetClientNameForMetrics()) {
+    UMA_HISTOGRAM_COUNTS_1M(
+        base::StringPrintf("Compositing.%s.HitTestTimeToFindClosestLayer",
+                           client_name),
+        timer.Elapsed().InMicroseconds());
+  }
 }
 
 struct FindScrollingLayerOrDrawnScrollbarFunctor {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApi.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApi.java
index 0dd76d3..97df7f36 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApi.java
@@ -63,4 +63,10 @@
      * Launch the stereoscopic, 3D VR launcher homescreen.
      */
     boolean launchVrHomescreen();
+
+    /**
+     * @return Whether this device boots directly into VR mode. May be used to detect standalone VR
+     * devices.
+     */
+    boolean bootsToVr();
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApiImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApiImpl.java
index 2df7ff8..891cfa6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApiImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrDaydreamApiImpl.java
@@ -14,8 +14,10 @@
 import com.google.vr.ndk.base.DaydreamApi;
 import com.google.vr.ndk.base.GvrApi;
 
+import org.chromium.base.Log;
 import org.chromium.ui.base.WindowAndroid;
 
+import java.lang.reflect.Method;
 
 /**
  * A wrapper for DaydreamApi. Note that we have to recreate the DaydreamApi instance each time we
@@ -24,6 +26,10 @@
 public class VrDaydreamApiImpl implements VrDaydreamApi {
     private final Context mContext;
 
+    private Boolean mBootsToVr = null;
+
+    public static final String VR_BOOT_SYSTEM_PROPERTY = "ro.boot.vr";
+
     public VrDaydreamApiImpl(Context context) {
         mContext = context;
     }
@@ -112,4 +118,28 @@
         daydreamApi.close();
         return true;
     }
+
+    @Override
+    public boolean bootsToVr() {
+        if (mBootsToVr == null) {
+            // TODO(mthiesse): Replace this with a Daydream API call when supported.
+            // Note that System.GetProperty is unable to read system ro properties, so we have to
+            // resort to reflection as seen below. This method of reading system properties has been
+            // available since API level 1.
+            mBootsToVr = getIntSystemProperty(VR_BOOT_SYSTEM_PROPERTY, 0) == 1;
+        }
+        return mBootsToVr;
+    }
+
+    private int getIntSystemProperty(String key, int defaultValue) {
+        try {
+            final Class<?> systemProperties = Class.forName("android.os.SystemProperties");
+            final Method getInt = systemProperties.getMethod("getInt", String.class, int.class);
+            return (Integer) getInt.invoke(null, key, defaultValue);
+        } catch (Exception e) {
+            Log.e("Exception while getting system property %s. Using default %s.", key,
+                    defaultValue, e);
+            return defaultValue;
+        }
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
index 884fd25b..3b4f8f6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
@@ -318,7 +318,9 @@
      */
     public static int getVrSupportLevel(VrDaydreamApi daydreamApi,
             VrCoreVersionChecker versionChecker, Tab tabToShowInfobarIn) {
-        if (versionChecker == null || daydreamApi == null
+        // TODO(mthiesse, crbug.com/791090): Re-enable VR mode for devices that boot to VR once we
+        // support those devices.
+        if (versionChecker == null || daydreamApi == null || daydreamApi.bootsToVr()
                 || !isVrCoreCompatible(versionChecker, tabToShowInfobarIn)) {
             return VR_NOT_AVAILABLE;
         }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellControllerInputTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellControllerInputTest.java
index a064c65..369d743 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellControllerInputTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellControllerInputTest.java
@@ -19,7 +19,6 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -60,7 +59,6 @@
      * Verifies that swiping up/down/left/right on the Daydream controller's
      * touchpad scrolls the webpage while in the VR browser.
      */
-    @DisabledTest(message = "crbug.com/786200")
     @Test
     @MediumTest
     public void testControllerScrolling() throws InterruptedException {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/mock/MockVrDaydreamApi.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/mock/MockVrDaydreamApi.java
index b90cb1b1..345070d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/mock/MockVrDaydreamApi.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/mock/MockVrDaydreamApi.java
@@ -65,4 +65,9 @@
     public boolean getLaunchInVrCalled() {
         return mLaunchInVrCalled;
     }
+
+    @Override
+    public boolean bootsToVr() {
+        return false;
+    }
 }
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 7b7c832..f423a57b 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3205,6 +3205,10 @@
       "printing/print_view_manager_base.h",
       "printing/print_view_manager_common.cc",
       "printing/print_view_manager_common.h",
+      "printing/printer_manager_dialog.h",
+      "printing/printer_manager_dialog_linux.cc",
+      "printing/printer_manager_dialog_mac.mm",
+      "printing/printer_manager_dialog_win.cc",
       "printing/printer_query.cc",
       "printing/printer_query.h",
       "printing/printing_init.cc",
@@ -3246,10 +3250,6 @@
         "printing/print_preview_message_handler.h",
         "printing/print_view_manager.cc",
         "printing/print_view_manager.h",
-        "printing/printer_manager_dialog.h",
-        "printing/printer_manager_dialog_linux.cc",
-        "printing/printer_manager_dialog_mac.mm",
-        "printing/printer_manager_dialog_win.cc",
         "printing/pwg_raster_converter.cc",
         "printing/pwg_raster_converter.h",
       ]
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 39e13da..a297c35 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1401,6 +1401,8 @@
     "printing/cups_print_job_notification_manager.h",
     "printing/cups_printers_manager.cc",
     "printing/cups_printers_manager.h",
+    "printing/external_printers.cc",
+    "printing/external_printers.h",
     "printing/ppd_provider_factory.cc",
     "printing/ppd_provider_factory.h",
     "printing/printer_configurer.cc",
@@ -1927,6 +1929,7 @@
     "power/renderer_freezer_unittest.cc",
     "preferences_unittest.cc",
     "printing/cups_printers_manager_unittest.cc",
+    "printing/external_printers_unittest.cc",
     "printing/printer_detector_test_util.h",
     "printing/printer_event_tracker_unittest.cc",
     "printing/printers_sync_bridge_unittest.cc",
diff --git a/chrome/browser/chromeos/printing/external_printers.cc b/chrome/browser/chromeos/printing/external_printers.cc
new file mode 100644
index 0000000..1e1b325
--- /dev/null
+++ b/chrome/browser/chromeos/printing/external_printers.cc
@@ -0,0 +1,233 @@
+// 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/chromeos/printing/external_printers.h"
+
+#include <set>
+#include <utility>
+
+#include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/sequence_checker.h"
+#include "base/stl_util.h"
+#include "base/values.h"
+#include "chromeos/printing/printer_translator.h"
+
+namespace chromeos {
+namespace {
+
+constexpr int kMaxRecords = 20000;
+
+class ExternalPrintersImpl : public ExternalPrinters {
+ public:
+  ExternalPrintersImpl() = default;
+  ~ExternalPrintersImpl() override = default;
+
+  // Resets the printer state fields.
+  void ClearData() override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    bool notify = !printers_.empty();
+    policy_retrieved_ = false;
+    printers_ready_ = false;
+
+    // Swap with empty vectors to release the allocated memory.
+    std::vector<Printer> empty;
+    printers_.swap(empty);
+    std::vector<std::unique_ptr<Printer>> empty_ptrs;
+    all_printers_.swap(empty_ptrs);
+
+    if (notify) {
+      Notify();
+    }
+  }
+
+  void SetData(std::unique_ptr<std::string> data) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    int error_code;
+    int error_line;
+    std::unique_ptr<base::Value> json_blob =
+        base::JSONReader::ReadAndReturnError(
+            *data, base::JSONParserOptions::JSON_PARSE_RFC, &error_code,
+            nullptr /* error_msg_out */, &error_line);
+    // It's not valid JSON.  Invalidate config.
+    if (!json_blob || !json_blob->is_list()) {
+      LOG(WARNING) << "Failed to parse external policy (" << error_code
+                   << ") on line " << error_line;
+      ClearData();
+      return;
+    }
+
+    const base::Value::ListStorage& printer_list = json_blob->GetList();
+    if (printer_list.size() > kMaxRecords) {
+      LOG(ERROR) << "Too many records: " << printer_list.size();
+      ClearData();
+      return;
+    }
+
+    for (const base::Value& val : printer_list) {
+      // TODO(skau): Convert to the new Value APIs.
+      const base::DictionaryValue* printer_dict;
+      if (!val.GetAsDictionary(&printer_dict)) {
+        LOG(WARNING) << "Entry is not a dictionary.";
+        continue;
+      }
+
+      auto printer = RecommendedPrinterToPrinter(*printer_dict);
+      if (!printer) {
+        LOG(WARNING) << "Failed to parse printer configuration.";
+        continue;
+      }
+      all_printers_.push_back(std::move(printer));
+    }
+
+    policy_retrieved_ = true;
+    RecomputePrinters();
+  }
+
+  void AddObserver(Observer* observer) override {
+    observers_.AddObserver(observer);
+  }
+
+  void RemoveObserver(Observer* observer) override {
+    observers_.RemoveObserver(observer);
+  }
+
+  void SetAccessMode(AccessMode mode) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    mode_ = mode;
+    RecomputePrinters();
+  }
+
+  void SetBlacklist(const std::vector<std::string>& blacklist) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    blacklist_.clear();
+    blacklist_.insert(blacklist.begin(), blacklist.end());
+    has_blacklist_ = true;
+    RecomputePrinters();
+  }
+
+  void SetWhitelist(const std::vector<std::string>& whitelist) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    whitelist_.clear();
+    whitelist_.insert(whitelist.begin(), whitelist.end());
+    has_whitelist_ = true;
+    RecomputePrinters();
+  }
+
+  // Returns true if the printer configuration has been downloaded and parsed.
+  bool IsPolicySet() const override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    return printers_ready_;
+  }
+
+  // Returns all the printers available from the policy.
+  const std::vector<Printer>& GetPrinters() const override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    return printers_;
+  }
+
+ private:
+  // Returns true if all required attributes have been set to compute the list
+  // of printers.
+  bool IsReady() const {
+    if (!policy_retrieved_) {
+      return false;
+    }
+
+    switch (mode_) {
+      case AccessMode::ALL_ACCESS:
+        return true;
+      case AccessMode::BLACKLIST_ONLY:
+        return has_blacklist_;
+      case AccessMode::WHITELIST_ONLY:
+        return has_whitelist_;
+      case AccessMode::UNSET:
+        return false;
+    }
+    NOTREACHED();
+    return false;
+  }
+
+  void RecomputePrinters() {
+    // Assume we're not ready.
+    printers_ready_ = false;
+    if (!IsReady()) {
+      return;
+    }
+
+    // Drop all printers, we're recomputing.
+    printers_.clear();
+    switch (mode_) {
+      case UNSET:
+        NOTREACHED();
+        break;
+      case WHITELIST_ONLY:
+        for (const auto& printer : all_printers_) {
+          if (base::ContainsKey(whitelist_, printer->id())) {
+            printers_.push_back(*printer);
+          }
+        }
+        break;
+      case BLACKLIST_ONLY:
+        for (const auto& printer : all_printers_) {
+          if (!base::ContainsKey(blacklist_, printer->id())) {
+            printers_.push_back(*printer);
+          }
+        }
+        break;
+      case ALL_ACCESS:
+        for (const auto& printer : all_printers_) {
+          printers_.push_back(*printer);
+        }
+        break;
+    }
+
+    // Everything has been computed.  Results are ready.
+    printers_ready_ = true;
+
+    // We assume something changed.  Notify now.
+    Notify();
+  }
+
+  void Notify() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    for (auto& observer : observers_) {
+      observer.OnPrintersChanged();
+    }
+  }
+
+  // True if all necessary information has been set to compute the set of
+  // printers.
+  bool printers_ready_ = false;
+  // Only true after the external policy has been downloaded.
+  bool policy_retrieved_ = false;
+
+  AccessMode mode_ = UNSET;
+  bool has_blacklist_ = false;
+  std::set<std::string> blacklist_;
+  bool has_whitelist_ = false;
+  std::set<std::string> whitelist_;
+
+  // Cache of the parsed printer configuration file.
+  std::vector<std::unique_ptr<Printer>> all_printers_;
+  // The computed set of printers.
+  std::vector<Printer> printers_;
+
+  base::ObserverList<ExternalPrinters::Observer> observers_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(ExternalPrintersImpl);
+};
+}  // namespace
+
+// static
+std::unique_ptr<ExternalPrinters> ExternalPrinters::Create() {
+  return base::MakeUnique<ExternalPrintersImpl>();
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/external_printers.h b/chrome/browser/chromeos/printing/external_printers.h
new file mode 100644
index 0000000..e7a6033
--- /dev/null
+++ b/chrome/browser/chromeos/printing/external_printers.h
@@ -0,0 +1,70 @@
+// 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_CHROMEOS_PRINTING_EXTERNAL_PRINTERS_H_
+#define CHROME_BROWSER_CHROMEOS_PRINTING_EXTERNAL_PRINTERS_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/policy/cloud_external_data_policy_observer.h"
+#include "chromeos/printing/printer_configuration.h"
+
+namespace chromeos {
+
+// Manages download and parsing of the external policy printer configuration and
+// enforces restrictions.
+class ExternalPrinters {
+ public:
+  // Choose the policy for printer access.
+  enum AccessMode {
+    UNSET,
+    // Printers in the blacklist are disallowed.  Others are allowed.
+    BLACKLIST_ONLY,
+    // Only printers in the whitelist are allowed.
+    WHITELIST_ONLY,
+    // All printers in the policy are allowed.
+    ALL_ACCESS
+  };
+
+  // Observer is notified when the computed set of printers change.  It is
+  // assumed that the observer is on the same sequence as the object it is
+  // observing.
+  class Observer {
+   public:
+    // Called when the computed set of printers changes.
+    virtual void OnPrintersChanged() = 0;
+  };
+
+  // Creates a handler for the external printer policies.
+  static std::unique_ptr<ExternalPrinters> Create();
+
+  virtual ~ExternalPrinters() = default;
+
+  // Parses |data| which is the contents of the bulk printes file and extracts
+  // printer information.  The file format is assumed to be JSON.
+  virtual void SetData(std::unique_ptr<std::string> data) = 0;
+  // Removes all printer data and invalidates the configuration.
+  virtual void ClearData() = 0;
+
+  virtual void AddObserver(Observer* observer) = 0;
+  virtual void RemoveObserver(Observer* observer) = 0;
+
+  virtual void SetAccessMode(AccessMode mode) = 0;
+  virtual void SetBlacklist(const std::vector<std::string>& blacklist) = 0;
+  virtual void SetWhitelist(const std::vector<std::string>& whitelist) = 0;
+
+  // Returns true if the printer configuration has been downloaded and parsed.
+  virtual bool IsPolicySet() const = 0;
+
+  // Returns all the printers available from the policy.
+  virtual const std::vector<Printer>& GetPrinters() const = 0;
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_EXTERNAL_PRINTERS_H_
diff --git a/chrome/browser/chromeos/printing/external_printers_unittest.cc b/chrome/browser/chromeos/printing/external_printers_unittest.cc
new file mode 100644
index 0000000..9ce3f10
--- /dev/null
+++ b/chrome/browser/chromeos/printing/external_printers_unittest.cc
@@ -0,0 +1,173 @@
+// 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/chromeos/printing/external_printers.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/scoped_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+namespace {
+
+// The number of printers in BulkPolicyContentsJson.
+constexpr size_t kNumPrinters = 3;
+
+// An example bulk printer configuration file.
+constexpr char kBulkPolicyContentsJson[] = R"json(
+[
+  {
+    "id": "First",
+    "display_name": "LexaPrint",
+    "description": "Laser on the test shelf",
+    "manufacturer": "LexaPrint, Inc.",
+    "model": "MS610de",
+    "uri": "ipp://192.168.1.5",
+    "ppd_resource": {
+      "effective_model": "MS610de"
+    }
+  }, {
+    "id": "Second",
+    "display_name": "Color Laser",
+    "description": "The printer next to the water cooler.",
+    "manufacturer": "Printer Manufacturer",
+    "model":"Color Laser 2004",
+    "uri":"ipps://print-server.intranet.example.com:443/ipp/cl2k4",
+    "uuid":"1c395fdb-5d93-4904-b246-b2c046e79d12",
+    "ppd_resource":{
+      "effective_manufacturer": "MakesPrinters",
+      "effective_model": "ColorLaser2k4"
+    }
+  }, {
+    "id": "Third",
+    "display_name": "YaLP",
+    "description": "Fancy Fancy Fancy",
+    "manufacturer": "LexaPrint, Inc.",
+    "model": "MS610de",
+    "uri": "ipp://192.168.1.8",
+    "ppd_resource": {
+      "effective_manufacturer": "LexaPrint",
+      "effective_model": "MS610de"
+    }
+  }
+])json";
+
+class ExternalPrintersTest : public testing::Test {
+ public:
+  ExternalPrintersTest() : scoped_task_environment_() {
+    external_printers_ = ExternalPrinters::Create();
+  }
+
+ protected:
+  std::unique_ptr<ExternalPrinters> external_printers_;
+
+ private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+};
+
+// Verify that we're initiall unset and empty.
+TEST_F(ExternalPrintersTest, InitialConditions) {
+  EXPECT_FALSE(external_printers_->IsPolicySet());
+  EXPECT_TRUE(external_printers_->GetPrinters().empty());
+}
+
+// Verifies that all IsPolicySet returns false until all necessary data is set.
+TEST_F(ExternalPrintersTest, PolicyUnsetWithMissingData) {
+  auto data = base::MakeUnique<std::string>(kBulkPolicyContentsJson);
+  external_printers_->ClearData();
+  external_printers_->SetData(std::move(data));
+
+  // Waiting for AccessMode.
+  EXPECT_FALSE(external_printers_->IsPolicySet());
+
+  external_printers_->SetAccessMode(ExternalPrinters::AccessMode::ALL_ACCESS);
+  EXPECT_TRUE(external_printers_->IsPolicySet());
+
+  external_printers_->SetAccessMode(
+      ExternalPrinters::AccessMode::WHITELIST_ONLY);
+  EXPECT_FALSE(external_printers_->IsPolicySet());  // Waiting for Whitelist.
+
+  std::vector<std::string> whitelist = {"First", "Third"};
+  external_printers_->SetWhitelist(whitelist);
+  EXPECT_TRUE(external_printers_->IsPolicySet());  // Everything is set.
+
+  external_printers_->SetAccessMode(
+      ExternalPrinters::AccessMode::BLACKLIST_ONLY);
+  EXPECT_FALSE(external_printers_->IsPolicySet());  // Blacklist needed now.
+
+  std::vector<std::string> blacklist = {"Second"};
+  external_printers_->SetBlacklist(blacklist);
+  EXPECT_TRUE(
+      external_printers_->IsPolicySet());  // Blacklist was set.  Ready again.
+}
+
+// Verify printer list after all attributes have been set.
+TEST_F(ExternalPrintersTest, AllPoliciesResultInPrinters) {
+  auto data = base::MakeUnique<std::string>(kBulkPolicyContentsJson);
+  external_printers_->SetAccessMode(ExternalPrinters::AccessMode::ALL_ACCESS);
+  external_printers_->ClearData();
+  external_printers_->SetData(std::move(data));
+
+  EXPECT_TRUE(external_printers_->IsPolicySet());
+  const std::vector<Printer>& printers = external_printers_->GetPrinters();
+  EXPECT_EQ(kNumPrinters, printers.size());
+  EXPECT_EQ("First", printers[0].id());
+  EXPECT_EQ("Second", printers[1].id());
+  EXPECT_EQ("Third", printers[2].id());
+}
+
+// The external policy was cleared, results should be invalidated.
+TEST_F(ExternalPrintersTest, PolicyClearedNowUnset) {
+  auto data = base::MakeUnique<std::string>(kBulkPolicyContentsJson);
+  external_printers_->SetAccessMode(ExternalPrinters::AccessMode::ALL_ACCESS);
+  external_printers_->ClearData();
+  external_printers_->SetData(std::move(data));
+
+  ASSERT_TRUE(external_printers_->IsPolicySet());
+
+  external_printers_->ClearData();
+  EXPECT_FALSE(external_printers_->IsPolicySet());
+  EXPECT_TRUE(external_printers_->GetPrinters().empty());
+}
+
+// Verify that the blacklist policy is applied correctly.  Printers in the
+// blacklist policy should not be available.  Printers not in the blackslist
+// should be available.
+TEST_F(ExternalPrintersTest, BlacklistPolicySet) {
+  auto data = base::MakeUnique<std::string>(kBulkPolicyContentsJson);
+  external_printers_->ClearData();
+  external_printers_->SetData(std::move(data));
+  external_printers_->SetAccessMode(ExternalPrinters::BLACKLIST_ONLY);
+  EXPECT_FALSE(external_printers_->IsPolicySet());
+  external_printers_->SetBlacklist({"Second", "Third"});
+  EXPECT_TRUE(external_printers_->IsPolicySet());
+
+  auto printers = external_printers_->GetPrinters();
+  ASSERT_EQ(1U, printers.size());
+  EXPECT_EQ(printers[0].id(), "First");
+}
+
+// Verify that the whitelist policy is correctly applied.  Only printers
+// available in the whitelist are available.
+TEST_F(ExternalPrintersTest, WhitelistPolicySet) {
+  auto data = base::MakeUnique<std::string>(kBulkPolicyContentsJson);
+  external_printers_->ClearData();
+  external_printers_->SetData(std::move(data));
+  external_printers_->SetAccessMode(ExternalPrinters::WHITELIST_ONLY);
+  EXPECT_FALSE(external_printers_->IsPolicySet());
+  external_printers_->SetWhitelist({"First"});
+  EXPECT_TRUE(external_printers_->IsPolicySet());
+
+  auto printers = external_printers_->GetPrinters();
+  ASSERT_EQ(1U, printers.size());
+  EXPECT_EQ(printers[0].id(), "First");
+}
+
+}  // namespace
+}  // namespace chromeos
diff --git a/chrome/browser/extensions/api/feedback_private/feedback_browsertest.cc b/chrome/browser/extensions/api/feedback_private/feedback_browsertest.cc
index eb9cb6e..46690d85 100644
--- a/chrome/browser/extensions/api/feedback_private/feedback_browsertest.cc
+++ b/chrome/browser/extensions/api/feedback_private/feedback_browsertest.cc
@@ -100,7 +100,14 @@
   VerifyFeedbackAppLaunch();
 }
 
-IN_PROC_BROWSER_TEST_F(FeedbackTest, ShowLoginFeedback) {
+// Disabled for ASan due to flakiness on Mac ASan 64 Tests (1).
+// See crbug.com/757243.
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_ShowLoginFeedback DISABLED_ShowLoginFeedback
+#else
+#define MAYBE_ShowLoginFeedback ShowLoginFeedback
+#endif
+IN_PROC_BROWSER_TEST_F(FeedbackTest, MAYBE_ShowLoginFeedback) {
   WaitForExtensionViewsToLoad();
 
   ASSERT_TRUE(IsFeedbackAppAvailable());
@@ -122,9 +129,16 @@
   EXPECT_TRUE(bool_result);
 }
 
+// Disabled for ASan due to flakiness on Mac ASan 64 Tests (1).
+// See crbug.com/757243.
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_AnonymousUser DISABLED_AnonymousUser
+#else
+#define MAYBE_AnonymousUser AnonymousUser
+#endif
 // Tests that there's an option in the email drop down box with a value
 // 'anonymous_user'.
-IN_PROC_BROWSER_TEST_F(FeedbackTest, AnonymousUser) {
+IN_PROC_BROWSER_TEST_F(FeedbackTest, MAYBE_AnonymousUser) {
   WaitForExtensionViewsToLoad();
 
   ASSERT_TRUE(IsFeedbackAppAvailable());
@@ -153,9 +167,16 @@
   EXPECT_TRUE(bool_result);
 }
 
+// Disabled for ASan due to flakiness on Mac ASan 64 Tests (1).
+// See crbug.com/757243.
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_ExtraDiagnostics DISABLED_ExtraDiagnostics
+#else
+#define MAYBE_ExtraDiagnostics ExtraDiagnostics
+#endif
 // Ensures that when extra diagnostics are provided with feedback, they are
 // injected properly in the system information.
-IN_PROC_BROWSER_TEST_F(FeedbackTest, ExtraDiagnostics) {
+IN_PROC_BROWSER_TEST_F(FeedbackTest, MAYBE_ExtraDiagnostics) {
   WaitForExtensionViewsToLoad();
 
   ASSERT_TRUE(IsFeedbackAppAvailable());
diff --git a/chrome/browser/media/router/discovery/dial/dial_media_sink_service.h b/chrome/browser/media/router/discovery/dial/dial_media_sink_service.h
index d303851..c96f8454 100644
--- a/chrome/browser/media/router/discovery/dial/dial_media_sink_service.h
+++ b/chrome/browser/media/router/discovery/dial/dial_media_sink_service.h
@@ -50,20 +50,23 @@
   // |dial_sink_added_cb_sequence|: The sequence |dial_sink_added_cb| is
   // invoked on, or nullptr if the callback is null.
   // Both callbacks may be invoked after |this| is destroyed.
-  void Start(const OnSinksDiscoveredCallback& sink_discovery_cb,
-             const OnDialSinkAddedCallback& dial_sink_added_cb,
-             const scoped_refptr<base::SequencedTaskRunner>&
-                 dial_sink_added_cb_sequence);
+  // Marked virtual for tests.
+  virtual void Start(const OnSinksDiscoveredCallback& sink_discovery_cb,
+                     const OnDialSinkAddedCallback& dial_sink_added_cb,
+                     const scoped_refptr<base::SequencedTaskRunner>&
+                         dial_sink_added_cb_sequence);
 
   // Forces the sink discovery callback to be invoked with the current list of
   // sinks. This method can only be called after |Start()|.
-  void ForceSinkDiscoveryCallback();
+  // Marked virtual for tests.
+  virtual void ForceSinkDiscoveryCallback();
 
   // Initiates discovery immediately in response to a user gesture
   // (i.e., opening the Media Router dialog). This method can only be called
   // after |Start()|.
   // TODO(imcheng): Rename to ManuallyInitiateDiscovery() or similar.
-  void OnUserGesture();
+  // Marked virtual for tests.
+  virtual void OnUserGesture();
 
  private:
   friend class DialMediaSinkServiceTest;
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service.h b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service.h
index 2c17e5bf..d768408 100644
--- a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service.h
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service.h
@@ -53,16 +53,19 @@
   // |sink_discovery_cb|: Callback to invoke when the list of discovered sinks
   // has been updated. The callback is invoked on the UI thread and may be
   // invoked after |this| is destroyed.
-  void Start(const OnSinksDiscoveredCallback& sinks_discovered_cb);
+  // Marked virtual for tests.
+  virtual void Start(const OnSinksDiscoveredCallback& sinks_discovered_cb);
 
   // Forces the sink discovery callback to be invoked with the current list of
   // sinks.
-  void ForceSinkDiscoveryCallback();
+  // Marked virtual for tests.
+  virtual void ForceSinkDiscoveryCallback();
 
   // Initiates discovery immediately in response to a user gesture
   // (i.e., opening the Media Router dialog).
   // TODO(imcheng): Rename to ManuallyInitiateDiscovery() or similar.
-  void OnUserGesture();
+  // Marked virtual for tests.
+  virtual void OnUserGesture();
 
   // Marked virtual for tests.
   virtual std::unique_ptr<CastMediaSinkServiceImpl> CreateImpl(
diff --git a/chrome/browser/media/router/media_router_feature.cc b/chrome/browser/media/router/media_router_feature.cc
index 6ab6efdc..819252e 100644
--- a/chrome/browser/media/router/media_router_feature.cc
+++ b/chrome/browser/media/router/media_router_feature.cc
@@ -20,8 +20,8 @@
 
 #if !defined(OS_ANDROID)
 // Controls if browser side DIAL device discovery is enabled.
-const base::Feature kEnableDialLocalDiscovery{
-    "EnableDialLocalDiscovery", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kEnableDialLocalDiscovery{"EnableDialLocalDiscovery",
+                                              base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Controls if browser side Cast device discovery is enabled.
 const base::Feature kEnableCastDiscovery{"EnableCastDiscovery",
diff --git a/chrome/browser/media/router/media_router_feature.h b/chrome/browser/media/router/media_router_feature.h
index 744b128..45ac8a9 100644
--- a/chrome/browser/media/router/media_router_feature.h
+++ b/chrome/browser/media/router/media_router_feature.h
@@ -18,6 +18,7 @@
 
 #if !defined(OS_ANDROID)
 
+extern const base::Feature kEnableDialLocalDiscovery;
 extern const base::Feature kEnableCastDiscovery;
 extern const base::Feature kEnableCastLocalMedia;
 
diff --git a/chrome/browser/media/router/mojo/media_route_controller.cc b/chrome/browser/media/router/mojo/media_route_controller.cc
index cb3280a..a8acb3aa 100644
--- a/chrome/browser/media/router/mojo/media_route_controller.cc
+++ b/chrome/browser/media/router/mojo/media_route_controller.cc
@@ -36,11 +36,12 @@
 void MediaRouteController::Observer::OnControllerInvalidated() {}
 
 MediaRouteController::MediaRouteController(const MediaRoute::Id& route_id,
-                                           content::BrowserContext* context)
+                                           content::BrowserContext* context,
+                                           MediaRouter* router)
     : route_id_(route_id),
       request_manager_(
           EventPageRequestManagerFactory::GetApiForBrowserContext(context)),
-      media_router_(MediaRouterFactory::GetApiForBrowserContext(context)),
+      media_router_(router),
       binding_(this) {
   DCHECK(media_router_);
   DCHECK(request_manager_);
@@ -167,8 +168,9 @@
 
 HangoutsMediaRouteController::HangoutsMediaRouteController(
     const MediaRoute::Id& route_id,
-    content::BrowserContext* context)
-    : MediaRouteController(route_id, context) {}
+    content::BrowserContext* context,
+    MediaRouter* router)
+    : MediaRouteController(route_id, context, router) {}
 
 HangoutsMediaRouteController::~HangoutsMediaRouteController() {}
 
@@ -217,8 +219,9 @@
 
 MirroringMediaRouteController::MirroringMediaRouteController(
     const MediaRoute::Id& route_id,
-    content::BrowserContext* context)
-    : MediaRouteController(route_id, context),
+    content::BrowserContext* context,
+    MediaRouter* router)
+    : MediaRouteController(route_id, context, router),
       prefs_(Profile::FromBrowserContext(context)->GetPrefs()) {
   DCHECK(prefs_);
   media_remoting_enabled_ =
diff --git a/chrome/browser/media/router/mojo/media_route_controller.h b/chrome/browser/media/router/mojo/media_route_controller.h
index eca6ebe..5027ba7 100644
--- a/chrome/browser/media/router/mojo/media_route_controller.h
+++ b/chrome/browser/media/router/mojo/media_route_controller.h
@@ -97,7 +97,8 @@
   // |mojo_media_controller_|. |media_router_| will be notified when the
   // MediaRouteController is destroyed via DetachRouteController().
   MediaRouteController(const MediaRoute::Id& route_id,
-                       content::BrowserContext* context);
+                       content::BrowserContext* context,
+                       MediaRouter* router);
 
   // Initializes the Mojo interfaces/bindings in this MediaRouteController.
   // This should only be called when the Mojo interfaces/bindings are not bound.
@@ -203,7 +204,8 @@
   static HangoutsMediaRouteController* From(MediaRouteController* controller);
 
   HangoutsMediaRouteController(const MediaRoute::Id& route_id,
-                               content::BrowserContext* context);
+                               content::BrowserContext* context,
+                               MediaRouter* router);
 
   // MediaRouteController
   RouteControllerType GetType() const override;
@@ -235,7 +237,8 @@
   static MirroringMediaRouteController* From(MediaRouteController* controller);
 
   MirroringMediaRouteController(const MediaRoute::Id& route_id,
-                                content::BrowserContext* context);
+                                content::BrowserContext* context,
+                                MediaRouter* router);
 
   // MediaRouteController
   RouteControllerType GetType() const override;
diff --git a/chrome/browser/media/router/mojo/media_route_controller_unittest.cc b/chrome/browser/media/router/mojo/media_route_controller_unittest.cc
index 6b0d2a0..5b27ad9 100644
--- a/chrome/browser/media/router/mojo/media_route_controller_unittest.cc
+++ b/chrome/browser/media/router/mojo/media_route_controller_unittest.cc
@@ -49,7 +49,8 @@
   void TearDown() override { observer_.reset(); }
 
   virtual scoped_refptr<MediaRouteController> CreateMediaRouteController() {
-    return base::MakeRefCounted<MediaRouteController>(kRouteId, &profile_);
+    return base::MakeRefCounted<MediaRouteController>(kRouteId, &profile_,
+                                                      &router_);
   }
 
   scoped_refptr<MediaRouteController> GetController() const {
@@ -67,7 +68,7 @@
   content::TestBrowserThreadBundle test_thread_bundle_;
   TestingProfile profile_;
 
-  MockMediaRouter* router_ = nullptr;
+  MockMediaRouter router_;
   MockEventPageRequestManager* request_manager_ = nullptr;
   MockMediaController mock_media_controller_;
   mojom::MediaStatusObserverPtr mojo_media_status_observer_;
@@ -79,10 +80,6 @@
         EventPageRequestManagerFactory::GetInstance()->SetTestingFactoryAndUse(
             &profile_, &MockEventPageRequestManager::Create));
     request_manager_->set_mojo_connections_ready_for_test(true);
-
-    router_ = static_cast<MockMediaRouter*>(
-        MediaRouterFactory::GetInstance()->SetTestingFactoryAndUse(
-            &profile_, &MockMediaRouter::Create));
   }
 
   DISALLOW_COPY_AND_ASSIGN(MediaRouteControllerTest);
@@ -99,8 +96,8 @@
   }
 
   scoped_refptr<MediaRouteController> CreateMediaRouteController() override {
-    return base::MakeRefCounted<HangoutsMediaRouteController>(kRouteId,
-                                                              &profile_);
+    return base::MakeRefCounted<HangoutsMediaRouteController>(
+        kRouteId, &profile_, &router_);
   }
 };
 
@@ -109,8 +106,8 @@
   ~MirroringMediaRouteControllerTest() override {}
 
   scoped_refptr<MediaRouteController> CreateMediaRouteController() override {
-    return base::MakeRefCounted<MirroringMediaRouteController>(kRouteId,
-                                                               &profile_);
+    return base::MakeRefCounted<MirroringMediaRouteController>(
+        kRouteId, &profile_, &router_);
   }
 };
 
@@ -207,14 +204,14 @@
   // Get rid of |observer_| and its reference to the controller.
   observer_.reset();
 
-  EXPECT_CALL(*router_, DetachRouteController(kRouteId, controller)).Times(0);
+  EXPECT_CALL(router_, DetachRouteController(kRouteId, controller)).Times(0);
   observer1.reset();
 
   // DetachRouteController() should be called when the controller no longer
   // has any observers.
-  EXPECT_CALL(*router_, DetachRouteController(kRouteId, controller)).Times(1);
+  EXPECT_CALL(router_, DetachRouteController(kRouteId, controller)).Times(1);
   observer2.reset();
-  EXPECT_TRUE(Mock::VerifyAndClearExpectations(router_));
+  EXPECT_TRUE(Mock::VerifyAndClearExpectations(&router_));
 }
 
 TEST_F(HangoutsMediaRouteControllerTest, HangoutsCommands) {
diff --git a/chrome/browser/media/router/mojo/media_router_desktop.cc b/chrome/browser/media/router/mojo/media_router_desktop.cc
index e435276..d12b465d 100644
--- a/chrome/browser/media/router/mojo/media_router_desktop.cc
+++ b/chrome/browser/media/router/mojo/media_router_desktop.cc
@@ -66,20 +66,27 @@
   return MediaRouterMojoImpl::GetProviderIdForPresentation(presentation_id);
 }
 
-MediaRouterDesktop::MediaRouterDesktop(content::BrowserContext* context,
-                                       FirewallCheck check_firewall)
-    : MediaRouterMojoImpl(context),
-      weak_factory_(this) {
+MediaRouterDesktop::MediaRouterDesktop(content::BrowserContext* context)
+    : MediaRouterMojoImpl(context), weak_factory_(this) {
   InitializeMediaRouteProviders();
 #if defined(OS_WIN)
-  if (check_firewall == FirewallCheck::RUN) {
-    CanFirewallUseLocalPorts(
-        base::BindOnce(&MediaRouterDesktop::OnFirewallCheckComplete,
-                       weak_factory_.GetWeakPtr()));
-  }
+  CanFirewallUseLocalPorts(
+      base::BindOnce(&MediaRouterDesktop::OnFirewallCheckComplete,
+                     weak_factory_.GetWeakPtr()));
 #endif
 }
 
+MediaRouterDesktop::MediaRouterDesktop(
+    content::BrowserContext* context,
+    std::unique_ptr<DialMediaSinkService> dial_media_sink_service,
+    std::unique_ptr<CastMediaSinkService> cast_media_sink_service)
+    : MediaRouterMojoImpl(context),
+      dial_media_sink_service_(std::move(dial_media_sink_service)),
+      cast_media_sink_service_(std::move(cast_media_sink_service)),
+      weak_factory_(this) {
+  InitializeMediaRouteProviders();
+}
+
 void MediaRouterDesktop::RegisterMediaRouteProvider(
     MediaRouteProviderId provider_id,
     mojom::MediaRouteProviderPtr media_route_provider_ptr,
diff --git a/chrome/browser/media/router/mojo/media_router_desktop.h b/chrome/browser/media/router/mojo/media_router_desktop.h
index 1d6ea5a..a673273 100644
--- a/chrome/browser/media/router/mojo/media_router_desktop.h
+++ b/chrome/browser/media/router/mojo/media_router_desktop.h
@@ -57,16 +57,16 @@
   friend class MediaRouterFactory;
   FRIEND_TEST_ALL_PREFIXES(MediaRouterDesktopTest, TestProvideSinks);
 
-  enum class FirewallCheck {
-    // Skips the firewall check for the benefit of unit tests so they do not
-    // have to depend on the system's firewall configuration.
-    SKIP_FOR_TESTING,
-    // Perform the firewall check (default).
-    RUN,
-  };
+  // This constructor performs a firewall check on Windows and is not suitable
+  // for use in unit tests; instead use the constructor below.
+  explicit MediaRouterDesktop(content::BrowserContext* context);
 
-  MediaRouterDesktop(content::BrowserContext* context,
-                     FirewallCheck check_firewall = FirewallCheck::RUN);
+  // Used by tests only. This constructor skips the firewall check so unit tests
+  // do not have to depend on the system's firewall configuration.
+  MediaRouterDesktop(
+      content::BrowserContext* context,
+      std::unique_ptr<DialMediaSinkService> dial_media_sink_service,
+      std::unique_ptr<CastMediaSinkService> cast_media_sink_service);
 
   // mojom::MediaRouter implementation.
   void RegisterMediaRouteProvider(
diff --git a/chrome/browser/media/router/mojo/media_router_desktop_unittest.cc b/chrome/browser/media/router/mojo/media_router_desktop_unittest.cc
index 3476acc..1e347d7 100644
--- a/chrome/browser/media/router/mojo/media_router_desktop_unittest.cc
+++ b/chrome/browser/media/router/mojo/media_router_desktop_unittest.cc
@@ -12,10 +12,14 @@
 #include "chrome/browser/media/router/mojo/media_router_desktop.h"
 
 #include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
+#include "chrome/browser/media/router/discovery/dial/dial_media_sink_service.h"
+#include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service.h"
 #include "chrome/browser/media/router/event_page_request_manager.h"
 #include "chrome/browser/media/router/event_page_request_manager_factory.h"
 #include "chrome/browser/media/router/media_router_factory.h"
+#include "chrome/browser/media/router/media_router_feature.h"
 #include "chrome/browser/media/router/mojo/media_router_mojo_test.h"
 #include "chrome/common/media_router/media_source_helper.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -45,6 +49,31 @@
       final {}
 };
 
+class MockDialMediaSinkService : public DialMediaSinkService {
+ public:
+  explicit MockDialMediaSinkService(content::BrowserContext* context)
+      : DialMediaSinkService(context) {}
+  ~MockDialMediaSinkService() override {}
+
+  MOCK_METHOD3(Start,
+               void(const OnSinksDiscoveredCallback&,
+                    const OnDialSinkAddedCallback&,
+                    const scoped_refptr<base::SequencedTaskRunner>&));
+  MOCK_METHOD0(ForceSinkDiscoveryCallback, void());
+  MOCK_METHOD0(OnUserGesture, void());
+};
+
+class MockCastMediaSinkService : public CastMediaSinkService {
+ public:
+  explicit MockCastMediaSinkService(content::BrowserContext* context)
+      : CastMediaSinkService(context) {}
+  ~MockCastMediaSinkService() override {}
+
+  MOCK_METHOD1(Start, void(const OnSinksDiscoveredCallback&));
+  MOCK_METHOD0(ForceSinkDiscoveryCallback, void());
+  MOCK_METHOD0(OnUserGesture, void());
+};
+
 }  // namespace
 
 class MediaRouterDesktopTest : public MediaRouterMojoTest {
@@ -53,46 +82,76 @@
   ~MediaRouterDesktopTest() override {}
 
  protected:
-  MediaRouterMojoImpl* SetTestingFactoryAndUse() override {
-    return static_cast<MediaRouterMojoImpl*>(
-        MediaRouterFactory::GetInstance()->SetTestingFactoryAndUse(
-            profile(), &CreateMediaRouter));
+  void SetUp() override {
+    feature_list_.InitWithFeatures(
+        {kEnableDialLocalDiscovery, kEnableCastDiscovery}, {});
+    MediaRouterMojoTest::SetUp();
+  }
+  std::unique_ptr<MediaRouterMojoImpl> CreateMediaRouter() override {
+    auto dial_media_sink_service =
+        std::make_unique<MockDialMediaSinkService>(profile());
+    dial_media_sink_service_ = dial_media_sink_service.get();
+    EXPECT_CALL(*dial_media_sink_service_, ForceSinkDiscoveryCallback());
+
+    auto cast_media_sink_service =
+        std::make_unique<MockCastMediaSinkService>(profile());
+    cast_media_sink_service_ = cast_media_sink_service.get();
+    EXPECT_CALL(*cast_media_sink_service_, ForceSinkDiscoveryCallback());
+    return std::unique_ptr<MediaRouterDesktop>(
+        new MediaRouterDesktop(profile(), std::move(dial_media_sink_service),
+                               std::move(cast_media_sink_service)));
+  }
+
+  MockDialMediaSinkService* dial_media_sink_service() const {
+    return dial_media_sink_service_;
+  }
+
+  MockCastMediaSinkService* cast_media_sink_service() const {
+    return cast_media_sink_service_;
   }
 
  private:
-  static std::unique_ptr<KeyedService> CreateMediaRouter(
-      content::BrowserContext* context) {
-    return std::unique_ptr<KeyedService>(new MediaRouterDesktop(
-        context, MediaRouterDesktop::FirewallCheck::SKIP_FOR_TESTING));
-  }
+  base::test::ScopedFeatureList feature_list_;
+  MockDialMediaSinkService* dial_media_sink_service_ = nullptr;
+  MockCastMediaSinkService* cast_media_sink_service_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaRouterDesktopTest);
 };
 
 #if defined(OS_WIN)
 TEST_F(MediaRouterDesktopTest, EnableMdnsAfterEachRegister) {
   EXPECT_CALL(mock_extension_provider_, EnableMdnsDiscovery()).Times(0);
+  EXPECT_CALL(*dial_media_sink_service(), ForceSinkDiscoveryCallback());
+  EXPECT_CALL(*cast_media_sink_service(), ForceSinkDiscoveryCallback());
   RegisterExtensionProvider();
   base::RunLoop().RunUntilIdle();
 
   EXPECT_CALL(mock_extension_provider_,
               UpdateMediaSinks(MediaSourceForDesktop().id()));
   EXPECT_CALL(mock_extension_provider_, EnableMdnsDiscovery());
+  EXPECT_CALL(*dial_media_sink_service(), OnUserGesture());
+  EXPECT_CALL(*cast_media_sink_service(), OnUserGesture());
   router()->OnUserGesture();
   base::RunLoop().RunUntilIdle();
 
   // EnableMdnsDiscovery() is called on this RegisterExtensionProvider() because
   // we've already seen an mdns-enabling event.
   EXPECT_CALL(mock_extension_provider_, EnableMdnsDiscovery());
+  EXPECT_CALL(*dial_media_sink_service(), ForceSinkDiscoveryCallback());
+  EXPECT_CALL(*cast_media_sink_service(), ForceSinkDiscoveryCallback());
   RegisterExtensionProvider();
   base::RunLoop().RunUntilIdle();
 }
 #endif
 
-TEST_F(MediaRouterDesktopTest, UpdateMediaSinksOnUserGesture) {
+TEST_F(MediaRouterDesktopTest, OnUserGesture) {
 #if defined(OS_WIN)
   EXPECT_CALL(mock_extension_provider_, EnableMdnsDiscovery());
 #endif
   EXPECT_CALL(mock_extension_provider_,
               UpdateMediaSinks(MediaSourceForDesktop().id()));
+  EXPECT_CALL(*dial_media_sink_service(), OnUserGesture());
+  EXPECT_CALL(*cast_media_sink_service(), OnUserGesture());
   router()->OnUserGesture();
   base::RunLoop().RunUntilIdle();
 }
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
index 97279e8..bee5d359 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
@@ -364,17 +364,18 @@
           << __func__ << ": route does not support controller: " << route_id;
       return nullptr;
     case RouteControllerType::kGeneric:
-      route_controller = new MediaRouteController(route_id, context_);
+      route_controller = new MediaRouteController(route_id, context_, this);
       break;
     case RouteControllerType::kHangouts:
-      route_controller = new HangoutsMediaRouteController(route_id, context_);
+      route_controller =
+          new HangoutsMediaRouteController(route_id, context_, this);
       break;
     case RouteControllerType::kMirroring:
       // TODO(imcheng): Remove this check when remoting is default enabled.
       route_controller =
           base::FeatureList::IsEnabled(features::kMediaRemoting)
-              ? new MirroringMediaRouteController(route_id, context_)
-              : new MediaRouteController(route_id, context_);
+              ? new MirroringMediaRouteController(route_id, context_, this)
+              : new MediaRouteController(route_id, context_, this);
       break;
   }
   DCHECK(route_controller);
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
index d1714799..44a057e 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
@@ -143,18 +143,12 @@
         expected_count);
   }
 
-  MediaRouterMojoImpl* SetTestingFactoryAndUse() override {
-    return static_cast<MediaRouterMojoImpl*>(
-        MediaRouterFactory::GetInstance()->SetTestingFactoryAndUse(
-            profile(), &CreateMediaRouter));
+  std::unique_ptr<MediaRouterMojoImpl> CreateMediaRouter() override {
+    return std::unique_ptr<MediaRouterMojoImpl>(
+        new MediaRouterMojoImpl(profile()));
   }
 
  private:
-  static std::unique_ptr<KeyedService> CreateMediaRouter(
-      content::BrowserContext* context) {
-    return std::unique_ptr<KeyedService>(new MediaRouterMojoImpl(context));
-  }
-
   base::HistogramTester histogram_tester_;
 };
 
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_test.cc b/chrome/browser/media/router/mojo/media_router_mojo_test.cc
index 3ebd8570..5cabbf9 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_test.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_test.cc
@@ -151,8 +151,9 @@
 
 MockMediaRouteController::MockMediaRouteController(
     const MediaRoute::Id& route_id,
-    content::BrowserContext* context)
-    : MediaRouteController(route_id, context) {}
+    content::BrowserContext* context,
+    MediaRouter* router)
+    : MediaRouteController(route_id, context, router) {}
 
 MockMediaRouteController::~MockMediaRouteController() {}
 
@@ -187,7 +188,7 @@
 }
 
 void MediaRouterMojoTest::SetUp() {
-  media_router_ = SetTestingFactoryAndUse();
+  media_router_ = CreateMediaRouter();
   media_router_->set_instance_id_for_test(kInstanceId);
   RegisterExtensionProvider();
   media_router_->Initialize();
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_test.h b/chrome/browser/media/router/mojo/media_router_mojo_test.h
index c7fb3e8..b5ae968 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_test.h
+++ b/chrome/browser/media/router/mojo/media_router_mojo_test.h
@@ -244,7 +244,8 @@
 class MockMediaRouteController : public MediaRouteController {
  public:
   MockMediaRouteController(const MediaRoute::Id& route_id,
-                           content::BrowserContext* context);
+                           content::BrowserContext* context,
+                           MediaRouter* router);
   MOCK_METHOD0(Play, void());
   MOCK_METHOD0(Pause, void());
   MOCK_METHOD1(Seek, void(base::TimeDelta time));
@@ -277,9 +278,8 @@
   void SetUp() override;
   void TearDown() override;
 
-  // Set the MediaRouter instance to be used by the MediaRouterFactory and
-  // return it.
-  virtual MediaRouterMojoImpl* SetTestingFactoryAndUse() = 0;
+  // Creates a MediaRouterMojoImpl instance to be used for this test.
+  virtual std::unique_ptr<MediaRouterMojoImpl> CreateMediaRouter() = 0;
 
   // Notify media router that the provider provides a route or a sink.
   // Need to be called after the provider is registered.
@@ -308,7 +308,7 @@
 
   const std::string& extension_id() const { return extension_->id(); }
 
-  MediaRouterMojoImpl* router() const { return media_router_; }
+  MediaRouterMojoImpl* router() const { return media_router_.get(); }
 
   Profile* profile() { return &profile_; }
 
@@ -326,7 +326,7 @@
   content::TestBrowserThreadBundle test_thread_bundle_;
   scoped_refptr<extensions::Extension> extension_;
   TestingProfile profile_;
-  MediaRouterMojoImpl* media_router_ = nullptr;
+  std::unique_ptr<MediaRouterMojoImpl> media_router_;
   mojo::BindingSet<mojom::MediaRouteProvider> provider_bindings_;
   std::unique_ptr<MediaRoutesObserver> routes_observer_;
   std::unique_ptr<MockMediaSinksObserver> sinks_observer_;
diff --git a/chrome/browser/metrics/tab_stats_tracker.cc b/chrome/browser/metrics/tab_stats_tracker.cc
index ae58d27..77c50512 100644
--- a/chrome/browser/metrics/tab_stats_tracker.cc
+++ b/chrome/browser/metrics/tab_stats_tracker.cc
@@ -158,10 +158,8 @@
     size_t tab_count) {
   // Don't report the number of tabs on resume if Chrome is running in
   // background with no visible window.
-  if (g_browser_process && g_browser_process->background_mode_manager()
-                               ->IsBackgroundWithoutWindows()) {
+  if (IsChromeBackgroundedWithoutWindows())
     return;
-  }
   UMA_HISTOGRAM_COUNTS_10000(kNumberOfTabsOnResumeHistogramName, tab_count);
 }
 
@@ -180,4 +178,13 @@
                              tab_stats.window_count_max);
 }
 
+bool TabStatsTracker::UmaStatsReportingDelegate::
+    IsChromeBackgroundedWithoutWindows() {
+  if (g_browser_process && g_browser_process->background_mode_manager()
+                               ->IsBackgroundWithoutWindows()) {
+    return true;
+  }
+  return false;
+}
+
 }  // namespace metrics
diff --git a/chrome/browser/metrics/tab_stats_tracker.h b/chrome/browser/metrics/tab_stats_tracker.h
index 3b9cbe85..8e3ed6b 100644
--- a/chrome/browser/metrics/tab_stats_tracker.h
+++ b/chrome/browser/metrics/tab_stats_tracker.h
@@ -85,6 +85,12 @@
     return reporting_delegate_.get();
   }
 
+  // Reset the |reporting_delegate_| object to |reporting_delegate|, for testing
+  // purposes.
+  void reset_reporting_delegate(UmaStatsReportingDelegate* reporting_delegate) {
+    reporting_delegate_.reset(reporting_delegate);
+  }
+
   // Reset the DailyEvent object to |daily_event|, for testing purposes.
   void reset_daily_event(DailyEvent* daily_event) {
     daily_event_.reset(daily_event);
@@ -148,7 +154,7 @@
   static const char kMaxWindowsInADayHistogramName[];
 
   UmaStatsReportingDelegate() {}
-  ~UmaStatsReportingDelegate() {}
+  virtual ~UmaStatsReportingDelegate() {}
 
   // Called at resume from sleep/hibernate.
   void ReportTabCountOnResume(size_t tab_count);
@@ -156,6 +162,11 @@
   // Called once per day to report the metrics.
   void ReportDailyMetrics(const TabStatsDataStore::TabsStats& tab_stats);
 
+ protected:
+  // Checks if Chrome is running in background with no visible windows, virtual
+  // for unittesting.
+  virtual bool IsChromeBackgroundedWithoutWindows();
+
  private:
   DISALLOW_COPY_AND_ASSIGN(UmaStatsReportingDelegate);
 };
diff --git a/chrome/browser/metrics/tab_stats_tracker_browsertest.cc b/chrome/browser/metrics/tab_stats_tracker_browsertest.cc
new file mode 100644
index 0000000..b4ded2a
--- /dev/null
+++ b/chrome/browser/metrics/tab_stats_tracker_browsertest.cc
@@ -0,0 +1,96 @@
+// 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/metrics/tab_stats_tracker.h"
+
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace metrics {
+
+namespace {
+
+using TabsStats = TabStatsDataStore::TabsStats;
+
+class TabStatsTrackerBrowserTest : public InProcessBrowserTest {
+ public:
+  TabStatsTrackerBrowserTest() : tab_stats_tracker_(nullptr) {}
+
+  void SetUpOnMainThread() override {
+    tab_stats_tracker_ = TabStatsTracker::GetInstance();
+    ASSERT_TRUE(tab_stats_tracker_ != nullptr);
+  }
+
+ protected:
+  TabStatsTracker* tab_stats_tracker_;
+
+  DISALLOW_COPY_AND_ASSIGN(TabStatsTrackerBrowserTest);
+};
+
+void EnsureTabStatsMatchExpectations(const TabsStats& expected,
+                                     const TabsStats& actual) {
+  EXPECT_EQ(expected.total_tab_count, actual.total_tab_count);
+  EXPECT_EQ(expected.total_tab_count_max, actual.total_tab_count_max);
+  EXPECT_EQ(expected.max_tab_per_window, actual.max_tab_per_window);
+  EXPECT_EQ(expected.window_count, actual.window_count);
+  EXPECT_EQ(expected.window_count_max, actual.window_count_max);
+}
+
+}  // namespace
+
+IN_PROC_BROWSER_TEST_F(TabStatsTrackerBrowserTest,
+                       TabsAndWindowsAreCountedAccurately) {
+  // Assert that the |TabStatsTracker| instance is initialized during the
+  // creation of the main browser.
+  ASSERT_NE(static_cast<TabStatsTracker*>(nullptr), tab_stats_tracker_);
+
+  TabsStats expected_stats = {};
+
+  // There should be only one window with one tab at startup.
+  expected_stats.total_tab_count = 1;
+  expected_stats.total_tab_count_max = 1;
+  expected_stats.max_tab_per_window = 1;
+  expected_stats.window_count = 1;
+  expected_stats.window_count_max = 1;
+
+  EnsureTabStatsMatchExpectations(expected_stats,
+                                  tab_stats_tracker_->tab_stats());
+
+  // Add a tab and make sure that the counters get updated.
+  AddTabAtIndex(1, GURL("about:blank"), ui::PAGE_TRANSITION_TYPED);
+  ++expected_stats.total_tab_count;
+  ++expected_stats.total_tab_count_max;
+  ++expected_stats.max_tab_per_window;
+  EnsureTabStatsMatchExpectations(expected_stats,
+                                  tab_stats_tracker_->tab_stats());
+
+  browser()->tab_strip_model()->CloseWebContentsAt(1, 0);
+  --expected_stats.total_tab_count;
+  EnsureTabStatsMatchExpectations(expected_stats,
+                                  tab_stats_tracker_->tab_stats());
+
+  Browser* browser = CreateBrowser(ProfileManager::GetActiveUserProfile());
+  ++expected_stats.total_tab_count;
+  ++expected_stats.window_count;
+  ++expected_stats.window_count_max;
+  EnsureTabStatsMatchExpectations(expected_stats,
+                                  tab_stats_tracker_->tab_stats());
+
+  AddTabAtIndexToBrowser(browser, 1, GURL("about:blank"),
+                         ui::PAGE_TRANSITION_TYPED, true);
+  ++expected_stats.total_tab_count;
+  ++expected_stats.total_tab_count_max;
+  EnsureTabStatsMatchExpectations(expected_stats,
+                                  tab_stats_tracker_->tab_stats());
+
+  CloseBrowserSynchronously(browser);
+  expected_stats.total_tab_count = 1;
+  expected_stats.window_count = 1;
+  EnsureTabStatsMatchExpectations(expected_stats,
+                                  tab_stats_tracker_->tab_stats());
+}
+
+}  // namespace metrics
diff --git a/chrome/browser/metrics/tab_stats_tracker_unittests.cc b/chrome/browser/metrics/tab_stats_tracker_unittests.cc
index 9b600a70..c877dcf 100644
--- a/chrome/browser/metrics/tab_stats_tracker_unittests.cc
+++ b/chrome/browser/metrics/tab_stats_tracker_unittests.cc
@@ -7,12 +7,8 @@
 #include "base/test/histogram_tester.h"
 #include "base/test/power_monitor_test_base.h"
 #include "base/test/scoped_task_environment.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/test/base/in_process_browser_test.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -23,32 +19,11 @@
 
 using TabsStats = TabStatsDataStore::TabsStats;
 
-class TabStatsTrackerBrowserTest : public InProcessBrowserTest {
- public:
-  TabStatsTrackerBrowserTest() : tab_stats_tracker_(nullptr) {}
-
-  void SetUpOnMainThread() override {
-    tab_stats_tracker_ = TabStatsTracker::GetInstance();
-    ASSERT_TRUE(tab_stats_tracker_ != nullptr);
-  }
-
- protected:
-  TabStatsTracker* tab_stats_tracker_;
-
-  DISALLOW_COPY_AND_ASSIGN(TabStatsTrackerBrowserTest);
-};
-
 class TestTabStatsTracker : public TabStatsTracker {
  public:
   using UmaStatsReportingDelegate = TabStatsTracker::UmaStatsReportingDelegate;
 
-  explicit TestTabStatsTracker(PrefService* pref_service)
-      : TabStatsTracker(pref_service), pref_service_(pref_service) {
-    // Stop the timer to ensure that the stats don't get reported (and reset)
-    // while running the tests.
-    EXPECT_TRUE(timer()->IsRunning());
-    timer()->Stop();
-  }
+  explicit TestTabStatsTracker(PrefService* pref_service);
   ~TestTabStatsTracker() override {}
 
   // Helper functions to update the number of tabs/windows.
@@ -106,6 +81,20 @@
   DISALLOW_COPY_AND_ASSIGN(TestTabStatsTracker);
 };
 
+class TestUmaStatsReportingDelegate
+    : public TestTabStatsTracker::UmaStatsReportingDelegate {
+ public:
+  TestUmaStatsReportingDelegate() {}
+
+ protected:
+  // Skip the check that ensures that there's at least one visible window as
+  // there's no window in the context of these tests.
+  bool IsChromeBackgroundedWithoutWindows() override { return false; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestUmaStatsReportingDelegate);
+};
+
 class TabStatsTrackerTest : public testing::Test {
  public:
   using UmaStatsReportingDelegate =
@@ -144,74 +133,23 @@
   DISALLOW_COPY_AND_ASSIGN(TabStatsTrackerTest);
 };
 
+TestTabStatsTracker::TestTabStatsTracker(PrefService* pref_service)
+    : TabStatsTracker(pref_service), pref_service_(pref_service) {
+  // Stop the timer to ensure that the stats don't get reported (and reset)
+  // while running the tests.
+  EXPECT_TRUE(timer()->IsRunning());
+  timer()->Stop();
+
+  reset_reporting_delegate(new TestUmaStatsReportingDelegate());
+}
+
 // Comparator for base::Bucket values.
 bool CompareHistogramBucket(const base::Bucket& l, const base::Bucket& r) {
   return l.min < r.min;
 }
 
-void EnsureTabStatsMatchExpectations(const TabsStats& expected,
-                                     const TabsStats& actual) {
-  EXPECT_EQ(expected.total_tab_count, actual.total_tab_count);
-  EXPECT_EQ(expected.total_tab_count_max, actual.total_tab_count_max);
-  EXPECT_EQ(expected.max_tab_per_window, actual.max_tab_per_window);
-  EXPECT_EQ(expected.window_count, actual.window_count);
-  EXPECT_EQ(expected.window_count_max, actual.window_count_max);
-}
-
 }  // namespace
 
-IN_PROC_BROWSER_TEST_F(TabStatsTrackerBrowserTest,
-                       TabsAndWindowsAreCountedAccurately) {
-  // Assert that the |TabStatsTracker| instance is initialized during the
-  // creation of the main browser.
-  ASSERT_NE(static_cast<TabStatsTracker*>(nullptr), tab_stats_tracker_);
-
-  TabsStats expected_stats = {};
-
-  // There should be only one window with one tab at startup.
-  expected_stats.total_tab_count = 1;
-  expected_stats.total_tab_count_max = 1;
-  expected_stats.max_tab_per_window = 1;
-  expected_stats.window_count = 1;
-  expected_stats.window_count_max = 1;
-
-  EnsureTabStatsMatchExpectations(expected_stats,
-                                  tab_stats_tracker_->tab_stats());
-
-  // Add a tab and make sure that the counters get updated.
-  AddTabAtIndex(1, GURL("about:blank"), ui::PAGE_TRANSITION_TYPED);
-  ++expected_stats.total_tab_count;
-  ++expected_stats.total_tab_count_max;
-  ++expected_stats.max_tab_per_window;
-  EnsureTabStatsMatchExpectations(expected_stats,
-                                  tab_stats_tracker_->tab_stats());
-
-  browser()->tab_strip_model()->CloseWebContentsAt(1, 0);
-  --expected_stats.total_tab_count;
-  EnsureTabStatsMatchExpectations(expected_stats,
-                                  tab_stats_tracker_->tab_stats());
-
-  Browser* browser = CreateBrowser(ProfileManager::GetActiveUserProfile());
-  ++expected_stats.total_tab_count;
-  ++expected_stats.window_count;
-  ++expected_stats.window_count_max;
-  EnsureTabStatsMatchExpectations(expected_stats,
-                                  tab_stats_tracker_->tab_stats());
-
-  AddTabAtIndexToBrowser(browser, 1, GURL("about:blank"),
-                         ui::PAGE_TRANSITION_TYPED, true);
-  ++expected_stats.total_tab_count;
-  ++expected_stats.total_tab_count_max;
-  EnsureTabStatsMatchExpectations(expected_stats,
-                                  tab_stats_tracker_->tab_stats());
-
-  CloseBrowserSynchronously(browser);
-  expected_stats.total_tab_count = 1;
-  expected_stats.window_count = 1;
-  EnsureTabStatsMatchExpectations(expected_stats,
-                                  tab_stats_tracker_->tab_stats());
-}
-
 TEST_F(TabStatsTrackerTest, OnResume) {
   // Makes sure that there's no sample initially.
   histogram_tester_.ExpectTotalCount(
diff --git a/chrome/browser/printing/printer_manager_dialog.h b/chrome/browser/printing/printer_manager_dialog.h
index 6a1df0c..cc99921 100644
--- a/chrome/browser/printing/printer_manager_dialog.h
+++ b/chrome/browser/printing/printer_manager_dialog.h
@@ -6,10 +6,16 @@
 #define CHROME_BROWSER_PRINTING_PRINTER_MANAGER_DIALOG_H_
 
 #include "base/macros.h"
+#include "printing/features/features.h"
+
+#if !BUILDFLAG(ENABLE_PRINTING)
+#error "Printing must be enabled"
+#endif
 
 namespace printing {
 
-// An abstraction of a printer manager dialog. This is used for print preview.
+// An abstraction of a printer manager dialog. This is used for the printing
+// sub-section of Settings.
 // This includes the OS-dependent UI to manage the network and local printers.
 class PrinterManagerDialog {
  public:
diff --git a/chrome/browser/resources/print_preview/compiled_resources2.gyp b/chrome/browser/resources/print_preview/compiled_resources2.gyp
index 854ca2d0..c1a9334e 100644
--- a/chrome/browser/resources/print_preview/compiled_resources2.gyp
+++ b/chrome/browser/resources/print_preview/compiled_resources2.gyp
@@ -35,5 +35,14 @@
       ],
       'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
     },
+    {
+      'target_name': 'native_layer',
+      'dependencies': [
+        'data/compiled_resources2.gyp:destination',
+        'data/compiled_resources2.gyp:measurement_system',
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+      ],
+      'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
+    },
   ],
 }
diff --git a/chrome/browser/resources/print_preview/data/compiled_resources2.gyp b/chrome/browser/resources/print_preview/data/compiled_resources2.gyp
index d69be6d..bf9b2ef 100644
--- a/chrome/browser/resources/print_preview/data/compiled_resources2.gyp
+++ b/chrome/browser/resources/print_preview/data/compiled_resources2.gyp
@@ -32,6 +32,13 @@
       'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
     },
     {
+      'target_name': 'measurement_system',
+      'dependencies': [
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+      ],
+      'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'],
+    },
+    {
       'target_name': 'printable_area',
       'dependencies': [
         'size',
diff --git a/chrome/browser/resources/print_preview/native_layer.html b/chrome/browser/resources/print_preview/native_layer.html
new file mode 100644
index 0000000..5e71fec
--- /dev/null
+++ b/chrome/browser/resources/print_preview/native_layer.html
@@ -0,0 +1,3 @@
+<link rel="import" href="chrome://resources/html/cr.html">
+
+<script src="native_layer.js"></script>
diff --git a/chrome/browser/resources/print_preview/native_layer.js b/chrome/browser/resources/print_preview/native_layer.js
index e797402..d2227130 100644
--- a/chrome/browser/resources/print_preview/native_layer.js
+++ b/chrome/browser/resources/print_preview/native_layer.js
@@ -190,199 +190,31 @@
     }
 
     /**
-     * @param {!print_preview.Destination} destination Destination to print to.
-     * @param {!print_preview.ticket_items.Color} color Color ticket item.
-     * @return {number} Native layer color model.
-     * @private
-     */
-    getNativeColorModel_(destination, color) {
-      // For non-local printers native color model is ignored anyway.
-      const option = destination.isLocal ? color.getSelectedOption() : null;
-      const nativeColorModel = parseInt(option ? option.vendor_id : null, 10);
-      if (isNaN(nativeColorModel)) {
-        return color.getValue() ? NativeLayer.ColorMode_.COLOR :
-                                  NativeLayer.ColorMode_.GRAY;
-      }
-      return nativeColorModel;
-    }
-
-    /**
      * Requests that a preview be generated. The following Web UI events may
      * be triggered in response:
      *   'print-preset-options',
      *   'page-count-ready',
      *   'page-layout-ready',
      *   'page-preview-ready'
-     * @param {!print_preview.Destination} destination Destination to print to.
-     * @param {!print_preview.PrintTicketStore} printTicketStore Used to get the
-     *     state of the print ticket.
-     * @param {!print_preview.DocumentInfo} documentInfo Document data model.
-     * @param {boolean} generateDraft Tell the renderer to re-render.
-     * @param {number} requestId ID of the preview request.
+     * @param {string} printTicket JSON print ticket for the request.
+     * @param {number} pageCount Page count for the preview request, or -1 if
+     *     unknown (first request).
      * @return {!Promise<number>} Promise that resolves with the unique ID of
      *     the preview UI when the preview has been generated.
      */
-    getPreview(
-        destination, printTicketStore, documentInfo, generateDraft, requestId) {
-      assert(
-          printTicketStore.isTicketValidForPreview(),
-          'Trying to generate preview when ticket is not valid');
-
-      const ticket = {
-        'pageRange': printTicketStore.pageRange.getDocumentPageRanges(),
-        'mediaSize': printTicketStore.mediaSize.getValue(),
-        'landscape': printTicketStore.landscape.getValue(),
-        'color': this.getNativeColorModel_(destination, printTicketStore.color),
-        'headerFooterEnabled': printTicketStore.headerFooter.getValue(),
-        'marginsType': printTicketStore.marginsType.getValue(),
-        'isFirstRequest': requestId == 0,
-        'requestID': requestId,
-        'previewModifiable': documentInfo.isModifiable,
-        'generateDraftData': generateDraft,
-        'fitToPageEnabled': printTicketStore.fitToPage.getValue(),
-        'scaleFactor': printTicketStore.scaling.getValueAsNumber(),
-        // NOTE: Even though the following fields don't directly relate to the
-        // preview, they still need to be included.
-        // e.g. printing::PrintSettingsFromJobSettings() still checks for them.
-        'collate': true,
-        'copies': 1,
-        'deviceName': destination.id,
-        'dpiHorizontal': 'horizontal_dpi' in printTicketStore.dpi.getValue() ?
-            printTicketStore.dpi.getValue().horizontal_dpi :
-            0,
-        'dpiVertical': 'vertical_dpi' in printTicketStore.dpi.getValue() ?
-            printTicketStore.dpi.getValue().vertical_dpi :
-            0,
-        'duplex': printTicketStore.duplex.getValue() ?
-            NativeLayer.DuplexMode.LONG_EDGE :
-            NativeLayer.DuplexMode.SIMPLEX,
-        'printToPDF': destination.id ==
-            print_preview.Destination.GooglePromotedId.SAVE_AS_PDF,
-        'printWithCloudPrint': !destination.isLocal,
-        'printWithPrivet': destination.isPrivet,
-        'printWithExtension': destination.isExtension,
-        'rasterizePDF': false,
-        'shouldPrintBackgrounds': printTicketStore.cssBackground.getValue(),
-        'shouldPrintSelectionOnly': printTicketStore.selectionOnly.getValue()
-      };
-
-      // Set 'cloudPrintID' only if the destination is not local.
-      if (destination && !destination.isLocal) {
-        ticket['cloudPrintID'] = destination.id;
-      }
-
-      if (printTicketStore.marginsType.isCapabilityAvailable() &&
-          printTicketStore.marginsType.getValue() ==
-              print_preview.ticket_items.MarginsTypeValue.CUSTOM) {
-        const customMargins = printTicketStore.customMargins.getValue();
-        const orientationEnum =
-            print_preview.ticket_items.CustomMarginsOrientation;
-        ticket['marginsCustom'] = {
-          'marginTop': customMargins.get(orientationEnum.TOP),
-          'marginRight': customMargins.get(orientationEnum.RIGHT),
-          'marginBottom': customMargins.get(orientationEnum.BOTTOM),
-          'marginLeft': customMargins.get(orientationEnum.LEFT)
-        };
-      }
-
-      return cr.sendWithPromise(
-          'getPreview', JSON.stringify(ticket),
-          requestId > 0 ? documentInfo.pageCount : -1);
+    getPreview(printTicket, pageCount) {
+      return cr.sendWithPromise('getPreview', printTicket, pageCount);
     }
 
     /**
      * Requests that the document be printed.
-     * @param {!print_preview.Destination} destination Destination to print to.
-     * @param {!print_preview.PrintTicketStore} printTicketStore Used to get the
-     *     state of the print ticket.
-     * @param {!print_preview.DocumentInfo} documentInfo Document data model.
-     * @param {boolean=} opt_isOpenPdfInPreview Whether to open the PDF in the
-     *     system's preview application.
-     * @param {boolean=} opt_showSystemDialog Whether to open system dialog for
-     *     advanced settings.
+     * @param {string} printTicket The serialized print ticket for the print
+     *     job.
      * @return {!Promise} Promise that will resolve when the print request is
      *     finished or rejected.
      */
-    print(
-        destination, printTicketStore, documentInfo, opt_isOpenPdfInPreview,
-        opt_showSystemDialog) {
-      assert(
-          printTicketStore.isTicketValid(),
-          'Trying to print when ticket is not valid');
-
-      assert(
-          !opt_showSystemDialog || (cr.isWindows && destination.isLocal),
-          'Implemented for Windows only');
-
-      // Note: update
-      // chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
-      // with any changes to ticket creation.
-      const ticket = {
-        'mediaSize': printTicketStore.mediaSize.getValue(),
-        'pageCount': printTicketStore.pageRange.getPageNumberSet().size,
-        'landscape': printTicketStore.landscape.getValue(),
-        'color': this.getNativeColorModel_(destination, printTicketStore.color),
-        'headerFooterEnabled': false,  // Only used in print preview
-        'marginsType': printTicketStore.marginsType.getValue(),
-        'duplex': printTicketStore.duplex.getValue() ?
-            NativeLayer.DuplexMode.LONG_EDGE :
-            NativeLayer.DuplexMode.SIMPLEX,
-        'copies': printTicketStore.copies.getValueAsNumber(),
-        'collate': printTicketStore.collate.getValue(),
-        'shouldPrintBackgrounds': printTicketStore.cssBackground.getValue(),
-        'shouldPrintSelectionOnly': false,  // Only used in print preview
-        'previewModifiable': documentInfo.isModifiable,
-        'printToPDF': destination.id ==
-            print_preview.Destination.GooglePromotedId.SAVE_AS_PDF,
-        'printWithCloudPrint': !destination.isLocal,
-        'printWithPrivet': destination.isPrivet,
-        'printWithExtension': destination.isExtension,
-        'rasterizePDF': printTicketStore.rasterize.getValue(),
-        'scaleFactor': printTicketStore.scaling.getValueAsNumber(),
-        'dpiHorizontal': 'horizontal_dpi' in printTicketStore.dpi.getValue() ?
-            printTicketStore.dpi.getValue().horizontal_dpi :
-            0,
-        'dpiVertical': 'vertical_dpi' in printTicketStore.dpi.getValue() ?
-            printTicketStore.dpi.getValue().vertical_dpi :
-            0,
-        'deviceName': destination.id,
-        'fitToPageEnabled': printTicketStore.fitToPage.getValue(),
-        'pageWidth': documentInfo.pageSize.width,
-        'pageHeight': documentInfo.pageSize.height,
-        'showSystemDialog': opt_showSystemDialog
-      };
-
-      if (!destination.isLocal) {
-        // We can't set cloudPrintID if the destination is "Print with Cloud
-        // Print" because the native system will try to print to Google Cloud
-        // Print with this ID instead of opening a Google Cloud Print dialog.
-        ticket['cloudPrintID'] = destination.id;
-      }
-
-      if (printTicketStore.marginsType.isCapabilityAvailable() &&
-          printTicketStore.marginsType.isValueEqual(
-              print_preview.ticket_items.MarginsTypeValue.CUSTOM)) {
-        const customMargins = printTicketStore.customMargins.getValue();
-        const orientationEnum =
-            print_preview.ticket_items.CustomMarginsOrientation;
-        ticket['marginsCustom'] = {
-          'marginTop': customMargins.get(orientationEnum.TOP),
-          'marginRight': customMargins.get(orientationEnum.RIGHT),
-          'marginBottom': customMargins.get(orientationEnum.BOTTOM),
-          'marginLeft': customMargins.get(orientationEnum.LEFT)
-        };
-      }
-
-      if (destination.isPrivet || destination.isExtension) {
-        ticket['ticket'] = printTicketStore.createPrintTicket(destination);
-        ticket['capabilities'] = JSON.stringify(destination.capabilities);
-      }
-
-      if (opt_isOpenPdfInPreview) {
-        ticket['OpenPDFInPreview'] = true;
-      }
-
-      return cr.sendWithPromise('print', JSON.stringify(ticket));
+    print(printTicket) {
+      return cr.sendWithPromise('print', printTicket);
     }
 
     /** Requests that the current pending print request be cancelled. */
@@ -392,7 +224,7 @@
 
     /**
      * Sends the app state to be saved in the sticky settings.
-     * @param {string} appStateStr JSON string of the app state to persist
+     * @param {string} appStateStr JSON string of the app state to persist.
      */
     saveAppState(appStateStr) {
       chrome.send('saveAppState', [appStateStr]);
@@ -480,19 +312,6 @@
   let currentInstance = null;
 
   /**
-   * Constant values matching printing::DuplexMode enum.
-   * @enum {number}
-   */
-  NativeLayer.DuplexMode = {SIMPLEX: 0, LONG_EDGE: 1, UNKNOWN_DUPLEX_MODE: -1};
-
-  /**
-   * Enumeration of color modes used by Chromium.
-   * @enum {number}
-   * @private
-   */
-  NativeLayer.ColorMode_ = {GRAY: 1, COLOR: 2};
-
-  /**
    * Version of the serialized state of the print preview.
    * @type {number}
    * @const
diff --git a/chrome/browser/resources/print_preview/new/compiled_resources2.gyp b/chrome/browser/resources/print_preview/new/compiled_resources2.gyp
index d8e8670..6b6454e 100644
--- a/chrome/browser/resources/print_preview/new/compiled_resources2.gyp
+++ b/chrome/browser/resources/print_preview/new/compiled_resources2.gyp
@@ -126,6 +126,7 @@
       'target_name': 'model',
       'dependencies': [
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+        '../compiled_resources2.gyp:native_layer',
         '../data/compiled_resources2.gyp:destination',
         '../data/compiled_resources2.gyp:document_info',
       ],
diff --git a/chrome/browser/resources/print_preview/new/model.html b/chrome/browser/resources/print_preview/new/model.html
index 3c40ff26..b983e9d 100644
--- a/chrome/browser/resources/print_preview/new/model.html
+++ b/chrome/browser/resources/print_preview/new/model.html
@@ -1,5 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="../native_layer.html">
 <link rel="import" href="../data/destination.html">
 <link rel="import" href="../data/document_info.html">
 
diff --git a/chrome/browser/resources/print_preview/new/model.js b/chrome/browser/resources/print_preview/new/model.js
index ee102c1..ba95eeb 100644
--- a/chrome/browser/resources/print_preview/new/model.js
+++ b/chrome/browser/resources/print_preview/new/model.js
@@ -273,6 +273,23 @@
    */
   MONOCHROME_TYPES_: ['STANDARD_MONOCHROME', 'CUSTOM_MONOCHROME'],
 
+  /** @private {!print_preview.NativeLayer} */
+  nativeLayer_: print_preview.NativeLayer.getInstance(),
+
+  /** @override */
+  attached: function() {
+    this.nativeLayer_.getInitialSettings().then(
+        this.onInitialSettingsSet_.bind(this));
+  },
+
+  /**
+   * @param {!print_preview.NativeInitialSettings} settings
+   * @private
+   */
+  onInitialSettingsSet_: function(settings) {
+    // Do nothing here for now.
+  },
+
   /**
    * Updates the availability of the settings sections.
    * @private
diff --git a/chrome/browser/resources/print_preview/preview_generator.js b/chrome/browser/resources/print_preview/preview_generator.js
index 93c0c2f0..c0ff0a1 100644
--- a/chrome/browser/resources/print_preview/preview_generator.js
+++ b/chrome/browser/resources/print_preview/preview_generator.js
@@ -189,13 +189,87 @@
       this.generateDraft_ = this.documentInfo_.isModifiable;
       return {
         id: this.inFlightRequestId_,
-        request: this.nativeLayer_.getPreview(
-            this.destinationStore_.selectedDestination, this.printTicketStore_,
-            this.documentInfo_, this.generateDraft_, this.inFlightRequestId_),
+        request: this.getPreview_(),
       };
     }
 
     /**
+     * @return {!Promise} Promise that resolves when the preview has been
+     *     generated.
+     * @private
+     */
+    getPreview_() {
+      const printTicketStore = this.printTicketStore_;
+      const destination = assert(this.destinationStore_.selectedDestination);
+
+      assert(
+          printTicketStore.isTicketValidForPreview(),
+          'Trying to generate preview when ticket is not valid');
+
+      const ticket = {
+        pageRange: printTicketStore.pageRange.getDocumentPageRanges(),
+        mediaSize: printTicketStore.mediaSize.getValue(),
+        landscape: printTicketStore.landscape.getValue(),
+        color: PreviewGenerator.getNativeColorModel(
+            destination, printTicketStore.color),
+        headerFooterEnabled: printTicketStore.headerFooter.getValue(),
+        marginsType: printTicketStore.marginsType.getValue(),
+        isFirstRequest: this.inFlightRequestId_ == 0,
+        requestID: this.inFlightRequestId_,
+        previewModifiable: this.documentInfo_.isModifiable,
+        generateDraftData: this.generateDraft_,
+        fitToPageEnabled: printTicketStore.fitToPage.getValue(),
+        scaleFactor: printTicketStore.scaling.getValueAsNumber(),
+        // NOTE: Even though the following fields dont directly relate to the
+        // preview, they still need to be included.
+        // e.g. printing::PrintSettingsFromJobSettings() still checks for them.
+        collate: true,
+        copies: 1,
+        deviceName: destination.id,
+        dpiHorizontal: 'horizontal_dpi' in printTicketStore.dpi.getValue() ?
+            printTicketStore.dpi.getValue().horizontal_dpi :
+            0,
+        dpiVertical: 'vertical_dpi' in printTicketStore.dpi.getValue() ?
+            printTicketStore.dpi.getValue().vertical_dpi :
+            0,
+        duplex: printTicketStore.duplex.getValue() ?
+            PreviewGenerator.DuplexMode.LONG_EDGE :
+            PreviewGenerator.DuplexMode.SIMPLEX,
+        printToPDF: destination.id ==
+            print_preview.Destination.GooglePromotedId.SAVE_AS_PDF,
+        printWithCloudPrint: !destination.isLocal,
+        printWithPrivet: destination.isPrivet,
+        printWithExtension: destination.isExtension,
+        rasterizePDF: false,
+        shouldPrintBackgrounds: printTicketStore.cssBackground.getValue(),
+        shouldPrintSelectionOnly: printTicketStore.selectionOnly.getValue()
+      };
+
+      // Set 'cloudPrintID' only if the destination is not local.
+      if (destination && !destination.isLocal) {
+        ticket.cloudPrintID = destination.id;
+      }
+
+      if (printTicketStore.marginsType.isCapabilityAvailable() &&
+          printTicketStore.marginsType.getValue() ==
+              print_preview.ticket_items.MarginsTypeValue.CUSTOM) {
+        const customMargins = printTicketStore.customMargins.getValue();
+        const orientationEnum =
+            print_preview.ticket_items.CustomMarginsOrientation;
+        ticket.marginsCustom = {
+          marginTop: customMargins.get(orientationEnum.TOP),
+          marginRight: customMargins.get(orientationEnum.RIGHT),
+          marginBottom: customMargins.get(orientationEnum.BOTTOM),
+          marginLeft: customMargins.get(orientationEnum.LEFT)
+        };
+      }
+
+      const pageCount =
+          this.inFlightRequestId_ > 0 ? this.documentInfo_.pageCount : -1;
+      return this.nativeLayer_.getPreview(JSON.stringify(ticket), pageCount);
+    }
+
+    /**
      * Dispatches a PAGE_READY event to signal that a page preview is ready.
      * @param {number} previewIndex Index of the page with respect to the pages
      *     shown in the preview. E.g an index of 0 is the first displayed page,
@@ -409,6 +483,35 @@
     FAIL: 'print_preview.PreviewGenerator.FAIL'
   };
 
+  /**
+   * Enumeration of color modes used by Chromium.
+   * @enum {number}
+   */
+  PreviewGenerator.ColorMode = {GRAY: 1, COLOR: 2};
+
+  /**
+   * @param {!print_preview.Destination} destination Destination to print to.
+   * @param {!print_preview.ticket_items.Color} color Color ticket item.
+   * @return {number} Native color model.
+   */
+  PreviewGenerator.getNativeColorModel = function(destination, color) {
+    // For non-local printers native color model is ignored anyway.
+    const option = destination.isLocal ? color.getSelectedOption() : null;
+    const nativeColorModel = parseInt(option ? option.vendor_id : null, 10);
+    if (isNaN(nativeColorModel)) {
+      return color.getValue() ? PreviewGenerator.ColorMode.COLOR :
+                                PreviewGenerator.ColorMode.GRAY;
+    }
+    return nativeColorModel;
+  };
+
+  /**
+   * Constant values matching printing::DuplexMode enum.
+   * @enum {number}
+   */
+  PreviewGenerator
+      .DuplexMode = {SIMPLEX: 0, LONG_EDGE: 1, UNKNOWN_DUPLEX_MODE: -1};
+
   // Export
   return {PreviewGenerator: PreviewGenerator};
 });
diff --git a/chrome/browser/resources/print_preview/print_preview.js b/chrome/browser/resources/print_preview/print_preview.js
index d806202..e7ba6397 100644
--- a/chrome/browser/resources/print_preview/print_preview.js
+++ b/chrome/browser/resources/print_preview/print_preview.js
@@ -559,10 +559,7 @@
                     .PRINT_WITH_SETTINGS_COLLAPSED);
       }
       const destination = assert(this.destinationStore_.selectedDestination);
-      const whenPrintDone = this.nativeLayer_.print(
-          destination, this.printTicketStore_, this.documentInfo_,
-          this.uiState_ == PrintPreviewUiState_.OPENING_PDF_PREVIEW,
-          this.showSystemDialogBeforeNextPrint_);
+      const whenPrintDone = this.sendPrintRequest_(destination);
       if (this.uiState_ == PrintPreviewUiState_.OPENING_PDF_PREVIEW ||
           (destination.isLocal && !destination.isPrivet &&
            !destination.isExtension &&
@@ -601,6 +598,96 @@
     },
 
     /**
+     * @param {!print_preview.Destination} destination Destination to print to.
+     * @return {!Promise} Promise that resolves when print request is resolved
+     *     or rejected.
+     * @private
+     */
+    sendPrintRequest_: function(destination) {
+      const printTicketStore = this.printTicketStore_;
+      const documentInfo = this.documentInfo_;
+      assert(
+          printTicketStore.isTicketValid(),
+          'Trying to print when ticket is not valid');
+
+      assert(
+          !this.showSystemDialogBeforeNextPrint_ ||
+              (cr.isWindows && destination.isLocal),
+          'Implemented for Windows only');
+
+      // Note: update
+      // chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
+      // with any changes to ticket creation.
+      const ticket = {
+        mediaSize: printTicketStore.mediaSize.getValue(),
+        pageCount: printTicketStore.pageRange.getPageNumberSet().size,
+        landscape: printTicketStore.landscape.getValue(),
+        color: print_preview.PreviewGenerator.getNativeColorModel(
+            destination, printTicketStore.color),
+        headerFooterEnabled: false,  // Only used in print preview
+        marginsType: printTicketStore.marginsType.getValue(),
+        duplex: printTicketStore.duplex.getValue() ?
+            print_preview.PreviewGenerator.DuplexMode.LONG_EDGE :
+            print_preview.PreviewGenerator.DuplexMode.SIMPLEX,
+        copies: printTicketStore.copies.getValueAsNumber(),
+        collate: printTicketStore.collate.getValue(),
+        shouldPrintBackgrounds: printTicketStore.cssBackground.getValue(),
+        shouldPrintSelectionOnly: false,  // Only used in print preview
+        previewModifiable: documentInfo.isModifiable,
+        printToPDF: destination.id ==
+            print_preview.Destination.GooglePromotedId.SAVE_AS_PDF,
+        printWithCloudPrint: !destination.isLocal,
+        printWithPrivet: destination.isPrivet,
+        printWithExtension: destination.isExtension,
+        rasterizePDF: printTicketStore.rasterize.getValue(),
+        scaleFactor: printTicketStore.scaling.getValueAsNumber(),
+        dpiHorizontal: 'horizontal_dpi' in printTicketStore.dpi.getValue() ?
+            printTicketStore.dpi.getValue().horizontal_dpi :
+            0,
+        dpiVertical: 'vertical_dpi' in printTicketStore.dpi.getValue() ?
+            printTicketStore.dpi.getValue().vertical_dpi :
+            0,
+        deviceName: destination.id,
+        fitToPageEnabled: printTicketStore.fitToPage.getValue(),
+        pageWidth: documentInfo.pageSize.width,
+        pageHeight: documentInfo.pageSize.height,
+        showSystemDialog: this.showSystemDialogBeforeNextPrint_
+      };
+
+      if (!destination.isLocal) {
+        // We can't set cloudPrintID if the destination is "Print with Cloud
+        // Print" because the native system will try to print to Google Cloud
+        // Print with this ID instead of opening a Google Cloud Print dialog.
+        ticket.cloudPrintID = destination.id;
+      }
+
+      if (printTicketStore.marginsType.isCapabilityAvailable() &&
+          printTicketStore.marginsType.isValueEqual(
+              print_preview.ticket_items.MarginsTypeValue.CUSTOM)) {
+        const customMargins = printTicketStore.customMargins.getValue();
+        const orientationEnum =
+            print_preview.ticket_items.CustomMarginsOrientation;
+        ticket.marginsCustom = {
+          marginTop: customMargins.get(orientationEnum.TOP),
+          marginRight: customMargins.get(orientationEnum.RIGHT),
+          marginBottom: customMargins.get(orientationEnum.BOTTOM),
+          marginLeft: customMargins.get(orientationEnum.LEFT)
+        };
+      }
+
+      if (destination.isPrivet || destination.isExtension) {
+        ticket.ticket = printTicketStore.createPrintTicket(destination);
+        ticket.capabilities = JSON.stringify(destination.capabilities);
+      }
+
+      if (this.uiState_ == PrintPreviewUiState_.OPENING_PDF_PREVIEW) {
+        ticket.OpenPDFInPreview = true;
+      }
+
+      return this.nativeLayer_.print(JSON.stringify(ticket));
+    },
+
+    /**
      * Closes the print preview.
      * @param {boolean} isCancel Whether this was called due to the user
      *     closing the dialog without printing.
diff --git a/chrome/browser/resources/print_preview/print_preview_resources.grd b/chrome/browser/resources/print_preview/print_preview_resources.grd
index 9066a7e..42d1a5b5c 100644
--- a/chrome/browser/resources/print_preview/print_preview_resources.grd
+++ b/chrome/browser/resources/print_preview/print_preview_resources.grd
@@ -29,6 +29,12 @@
       <structure name="IDR_PRINT_PREVIEW_NEW_MODEL_JS"
                  file="new/model.js"
                  type="chrome_html" />
+      <structure name="IDR_PRINT_PREVIEW_NATIVE_LAYER_HTML"
+                 file="native_layer.html"
+                 type="chrome_html" />
+      <structure name="IDR_PRINT_PREVIEW_NATIVE_LAYER_JS"
+                 file="native_layer.js"
+                 type="chrome_html" />
       <structure name="IDR_PRINT_PREVIEW_DATA_DESTINATION_HTML"
                  file="data/destination.html"
                  type="chrome_html" />
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 25c6a70..c787664d 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1556,12 +1556,6 @@
     if (use_cras) {
       defines += [ "USE_CRAS" ]
     }
-  } else if (!is_android) {
-    # !is_chromeos && !is_android
-    sources += [
-      "webui/settings/printing_handler.cc",
-      "webui/settings/printing_handler.h",
-    ]
   }
 
   if (is_win || is_mac || is_desktop_linux || is_chromeos) {
@@ -3576,6 +3570,12 @@
       "//components/printing/browser",
       "//printing",
     ]
+    if (!is_chromeos && !is_android) {
+      sources += [
+        "webui/settings/printing_handler.cc",
+        "webui/settings/printing_handler.h",
+      ]
+    }
     if (use_cups) {
       configs += [ "//printing:cups" ]
     }
diff --git a/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc b/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
index 11b8c3b1..61937ce 100644
--- a/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_ui_unittest.cc
@@ -108,19 +108,16 @@
     ChromeRenderViewHostTestHarness::SetUp();
     EventPageRequestManagerFactory::GetInstance()->SetTestingFactory(
         profile(), &MockEventPageRequestManager::Create);
-    mock_router_ = static_cast<MockMediaRouter*>(
-        MediaRouterFactory::GetInstance()->SetTestingFactoryAndUse(
-            profile(), &MockMediaRouter::Create));
-    EXPECT_CALL(*mock_router_, OnUserGesture()).Times(AnyNumber());
-    EXPECT_CALL(*mock_router_, GetCurrentRoutes())
+    EXPECT_CALL(mock_router_, OnUserGesture()).Times(AnyNumber());
+    EXPECT_CALL(mock_router_, GetCurrentRoutes())
         .Times(AnyNumber())
         .WillRepeatedly(Return(std::vector<MediaRoute>()));
   }
 
   void TearDown() override {
-    EXPECT_CALL(*mock_router_, UnregisterMediaSinksObserver(_))
+    EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_))
         .Times(AnyNumber());
-    EXPECT_CALL(*mock_router_, UnregisterMediaRoutesObserver(_))
+    EXPECT_CALL(mock_router_, UnregisterMediaRoutesObserver(_))
         .Times(AnyNumber());
     web_ui_contents_.reset();
     start_presentation_context_.reset();
@@ -149,15 +146,15 @@
     auto file_dialog = base::MakeUnique<MockMediaRouterFileDialog>();
     mock_file_dialog_ = file_dialog.get();
 
-    EXPECT_CALL(*mock_router_, RegisterMediaSinksObserver(_))
+    EXPECT_CALL(mock_router_, RegisterMediaSinksObserver(_))
         .WillRepeatedly(Invoke([this](MediaSinksObserver* observer) {
           this->media_sinks_observers_.push_back(observer);
           return true;
         }));
-    EXPECT_CALL(*mock_router_, RegisterMediaRoutesObserver(_))
+    EXPECT_CALL(mock_router_, RegisterMediaRoutesObserver(_))
         .Times(AnyNumber());
     media_router_ui_->InitForTest(
-        mock_router_, web_contents(), message_handler_.get(),
+        &mock_router_, web_contents(), message_handler_.get(),
         std::move(start_presentation_context_), std::move(file_dialog));
     message_handler_->SetWebUIForTest(&web_ui_);
   }
@@ -174,13 +171,13 @@
   // controller. Returns a reference to the mock controller.
   scoped_refptr<MockMediaRouteController> OpenUIDetailsView(
       const MediaRoute::Id& route_id) {
-    auto controller =
-        base::MakeRefCounted<MockMediaRouteController>(route_id, profile());
+    auto controller = base::MakeRefCounted<MockMediaRouteController>(
+        route_id, profile(), &mock_router_);
     MediaSource media_source("mediaSource");
     MediaRoute route(route_id, media_source, "sinkId", "", true, "", true);
 
     media_router_ui_->OnRoutesUpdated({route}, std::vector<MediaRoute::Id>());
-    EXPECT_CALL(*mock_router_, GetRouteController(route_id))
+    EXPECT_CALL(mock_router_, GetRouteController(route_id))
         .WillOnce(Return(controller));
     media_router_ui_->OnMediaControllerUIAvailable(route_id);
 
@@ -188,8 +185,8 @@
   }
 
  protected:
+  MockMediaRouter mock_router_;
   content::PresentationRequest presentation_request_;
-  MockMediaRouter* mock_router_ = nullptr;
   content::TestWebUI web_ui_;
   std::unique_ptr<WebContents> web_ui_contents_;
   std::unique_ptr<StartPresentationContext> start_presentation_context_;
@@ -203,7 +200,7 @@
 TEST_F(MediaRouterUITest, RouteCreationTimeoutForTab) {
   CreateMediaRouterUI(profile());
   std::vector<MediaRouteResponseCallback> callbacks;
-  EXPECT_CALL(*mock_router_,
+  EXPECT_CALL(mock_router_,
               CreateRouteInternal(_, _, _, _, _,
                                   base::TimeDelta::FromSeconds(60), false))
       .WillOnce(SaveArgWithMove<4>(&callbacks));
@@ -222,7 +219,7 @@
 TEST_F(MediaRouterUITest, RouteCreationTimeoutForDesktop) {
   CreateMediaRouterUI(profile());
   std::vector<MediaRouteResponseCallback> callbacks;
-  EXPECT_CALL(*mock_router_,
+  EXPECT_CALL(mock_router_,
               CreateRouteInternal(_, _, _, _, _,
                                   base::TimeDelta::FromSeconds(120), false))
       .WillOnce(SaveArgWithMove<4>(&callbacks));
@@ -245,7 +242,7 @@
       url::Origin::Create(GURL("https://frameurl.fakeurl")));
   media_router_ui_->OnDefaultPresentationChanged(presentation_request);
   std::vector<MediaRouteResponseCallback> callbacks;
-  EXPECT_CALL(*mock_router_,
+  EXPECT_CALL(mock_router_,
               CreateRouteInternal(_, _, _, _, _,
                                   base::TimeDelta::FromSeconds(20), false))
       .WillOnce(SaveArgWithMove<4>(&callbacks));
@@ -278,7 +275,7 @@
 
   // Expect that the media_router_ will make a call to the mock_router
   // then we will want to check that it made the call with.
-  EXPECT_CALL(*mock_router_, CreateRouteInternal(_, _, _, _, _, _, _))
+  EXPECT_CALL(mock_router_, CreateRouteInternal(_, _, _, _, _, _, _))
       .WillOnce(SaveArgWithMove<3>(&location_file_opened));
 
   media_router_ui_->CreateRoute(CreateSinkCompatibleWithAllSources().id(),
@@ -291,7 +288,7 @@
 TEST_F(MediaRouterUITest, RouteCreationParametersCantBeCreated) {
   CreateMediaRouterUI(profile());
   MediaSinkSearchResponseCallback sink_callback;
-  EXPECT_CALL(*mock_router_, SearchSinksInternal(_, _, _, _, _))
+  EXPECT_CALL(mock_router_, SearchSinksInternal(_, _, _, _, _))
       .WillOnce(SaveArgWithMove<4>(&sink_callback));
 
   // Use PRESENTATION mode without setting a PresentationRequest.
@@ -307,7 +304,7 @@
   CreateMediaRouterUI(profile()->GetOffTheRecordProfile());
   media_router_ui_->OnDefaultPresentationChanged(presentation_request_);
 
-  EXPECT_CALL(*mock_router_,
+  EXPECT_CALL(mock_router_,
               CreateRouteInternal(_, _, _, _, _,
                                   base::TimeDelta::FromSeconds(20), true));
   media_router_ui_->CreateRoute(CreateSinkCompatibleWithAllSources().id(),
@@ -434,7 +431,7 @@
   MediaSource media_source_3(MediaSourceForDesktop());
   std::unique_ptr<MediaRouterUI::UIMediaRoutesObserver> observer(
       new MediaRouterUI::UIMediaRoutesObserver(
-          mock_router_, MediaSource::Id(),
+          &mock_router_, MediaSource::Id(),
           base::Bind(&MediaRouterUI::OnRoutesUpdated,
                      base::Unretained(media_router_ui_.get()))));
 
@@ -471,7 +468,7 @@
   EXPECT_NE(end(current_cast_modes), cast_mode_entry);
   EXPECT_EQ(MediaCastMode::DESKTOP_MIRROR, cast_mode_entry->second);
 
-  EXPECT_CALL(*mock_router_, UnregisterMediaRoutesObserver(_)).Times(1);
+  EXPECT_CALL(mock_router_, UnregisterMediaRoutesObserver(_)).Times(1);
   observer.reset();
 }
 
@@ -482,7 +479,7 @@
   MediaSource media_source_3(MediaSourceForDesktop());
   std::unique_ptr<MediaRouterUI::UIMediaRoutesObserver> observer(
       new MediaRouterUI::UIMediaRoutesObserver(
-          mock_router_, MediaSource::Id(),
+          &mock_router_, MediaSource::Id(),
           base::Bind(&MediaRouterUI::OnRoutesUpdated,
                      base::Unretained(media_router_ui_.get()))));
 
@@ -519,7 +516,7 @@
   EXPECT_NE(end(current_cast_modes), cast_mode_entry);
   EXPECT_EQ(MediaCastMode::DESKTOP_MIRROR, cast_mode_entry->second);
 
-  EXPECT_CALL(*mock_router_, UnregisterMediaRoutesObserver(_)).Times(1);
+  EXPECT_CALL(mock_router_, UnregisterMediaRoutesObserver(_)).Times(1);
   observer.reset();
 }
 
@@ -699,10 +696,10 @@
 
   // When the route details view is closed, the route controller observer should
   // be destroyed, also triggering the destruction of the controller.
-  EXPECT_CALL(*mock_router_, DetachRouteController(route_id, _));
+  EXPECT_CALL(mock_router_, DetachRouteController(route_id, _));
   media_router_ui_->OnMediaControllerUIClosed();
 
-  EXPECT_TRUE(Mock::VerifyAndClearExpectations(mock_router_));
+  EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_router_));
 }
 
 TEST_F(MediaRouterUITest, SendMediaStatusUpdate) {
@@ -727,8 +724,8 @@
   MediaStatus status;
   status.title = "test title";
   std::string route_id = "routeId";
-  auto controller =
-      base::MakeRefCounted<MockMediaRouteController>(route_id, profile());
+  auto controller = base::MakeRefCounted<MockMediaRouteController>(
+      route_id, profile(), &mock_router_);
   controller->OnMediaStatusUpdated(status);
 
   CreateMediaRouterUI(profile());
@@ -738,7 +735,7 @@
 
   // If the controller has already received a media status update, MediaRouterUI
   // should be notified with it when it starts observing the controller.
-  EXPECT_CALL(*mock_router_, GetRouteController(route_id))
+  EXPECT_CALL(mock_router_, GetRouteController(route_id))
       .WillOnce(Return(controller));
   EXPECT_CALL(*message_handler_, UpdateMediaRouteStatus(status));
   media_router_ui_->OnMediaControllerUIAvailable(route_id);
@@ -771,12 +768,12 @@
   message_handler_ = base::MakeUnique<MockMediaRouterWebUIMessageHandler>(
       media_router_ui_.get());
   message_handler_->SetWebUIForTest(&web_ui_);
-  EXPECT_CALL(*mock_router_, RegisterMediaSinksObserver(_))
+  EXPECT_CALL(mock_router_, RegisterMediaSinksObserver(_))
       .WillRepeatedly(Invoke([this](MediaSinksObserver* observer) {
         this->media_sinks_observers_.push_back(observer);
         return true;
       }));
-  EXPECT_CALL(*mock_router_, RegisterMediaRoutesObserver(_)).Times(AnyNumber());
+  EXPECT_CALL(mock_router_, RegisterMediaRoutesObserver(_)).Times(AnyNumber());
   // For some reason we push two sets of cast modes to the dialog, even when
   // initializing the dialog with a presentation request.  The WebUI can handle
   // the forced mode that is not in the initial cast mode set, but is this a
@@ -795,7 +792,7 @@
                   base::Optional<MediaCastMode>(MediaCastMode::PRESENTATION)));
   media_router_ui_->UIInitialized();
   media_router_ui_->InitForTest(
-      mock_router_, web_contents(), message_handler_.get(),
+      &mock_router_, web_contents(), message_handler_.get(),
       std::move(start_presentation_context_), nullptr);
   // |media_router_ui_| takes ownership of |request_callbacks|.
   media_router_ui_.reset();
diff --git a/chrome/browser/ui/webui/media_router/media_router_webui_message_handler_unittest.cc b/chrome/browser/ui/webui/media_router/media_router_webui_message_handler_unittest.cc
index 9a541e8c..6c726603 100644
--- a/chrome/browser/ui/webui/media_router/media_router_webui_message_handler_unittest.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_webui_message_handler_unittest.cc
@@ -207,8 +207,11 @@
     return dict_value;
   }
 
+  MockMediaRouter* router() { return &router_; }
+
  protected:
   std::unique_ptr<content::TestWebUI> web_ui_;
+  MockMediaRouter router_;
   std::unique_ptr<MockMediaRouterUI> mock_media_router_ui_;
   std::unique_ptr<TestMediaRouterWebUIMessageHandler> handler_;
   const std::string provider_extension_id_;
@@ -619,8 +622,8 @@
 }
 
 TEST_F(MediaRouterWebUIMessageHandlerTest, OnMediaCommandsReceived) {
-  auto controller =
-      base::MakeRefCounted<MockMediaRouteController>("routeId", profile());
+  auto controller = base::MakeRefCounted<MockMediaRouteController>(
+      "routeId", profile(), router());
   EXPECT_CALL(*mock_media_router_ui_, GetMediaRouteController())
       .WillRepeatedly(Return(controller.get()));
   MediaStatus status;
@@ -657,8 +660,8 @@
 }
 
 TEST_F(MediaRouterWebUIMessageHandlerTest, OnSetMediaRemotingEnabled) {
-  auto controller =
-      base::MakeRefCounted<MirroringMediaRouteController>("routeId", profile());
+  auto controller = base::MakeRefCounted<MirroringMediaRouteController>(
+      "routeId", profile(), router());
   EXPECT_CALL(*mock_media_router_ui_, GetMediaRouteController())
       .WillRepeatedly(Return(controller.get()));
 
@@ -669,8 +672,8 @@
 }
 
 TEST_F(MediaRouterWebUIMessageHandlerTest, OnInvalidMediaCommandsReceived) {
-  auto controller =
-      base::MakeRefCounted<MockMediaRouteController>("routeId", profile());
+  auto controller = base::MakeRefCounted<MockMediaRouteController>(
+      "routeId", profile(), router());
   EXPECT_CALL(*mock_media_router_ui_, GetMediaRouteController())
       .WillRepeatedly(Return(controller.get()));
 
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui.cc b/chrome/browser/ui/webui/settings/md_settings_ui.cc
index 6c23035..31915a45 100644
--- a/chrome/browser/ui/webui/settings/md_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_ui.cc
@@ -9,6 +9,7 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
@@ -46,6 +47,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
+#include "printing/features/features.h"
 
 #if defined(OS_WIN)
 #include "chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h"
@@ -84,7 +86,6 @@
 #include "chromeos/chromeos_switches.h"
 #include "components/arc/arc_util.h"
 #else  // !defined(OS_CHROMEOS)
-#include "chrome/browser/ui/webui/settings/printing_handler.h"
 #include "chrome/browser/ui/webui/settings/settings_default_browser_handler.h"
 #include "chrome/browser/ui/webui/settings/settings_manage_profile_handler.h"
 #include "chrome/browser/ui/webui/settings/system_handler.h"
@@ -96,6 +97,10 @@
 #include "chrome/browser/ui/webui/settings/native_certificates_handler.h"
 #endif  // defined(USE_NSS_CERTS)
 
+#if BUILDFLAG(ENABLE_PRINTING) && !defined(OS_CHROMEOS)
+#include "chrome/browser/ui/webui/settings/printing_handler.h"
+#endif
+
 #if defined(SAFE_BROWSING_DB_LOCAL)
 #include "chrome/browser/safe_browsing/chrome_password_protection_service.h"
 #include "chrome/browser/ui/webui/settings/change_password_handler.h"
@@ -186,6 +191,9 @@
   AddSettingsPageUIHandler(base::MakeUnique<DefaultBrowserHandler>(web_ui));
   AddSettingsPageUIHandler(base::MakeUnique<ManageProfileHandler>(profile));
   AddSettingsPageUIHandler(base::MakeUnique<SystemHandler>());
+#endif
+
+#if BUILDFLAG(ENABLE_PRINTING) && !defined(OS_CHROMEOS)
   AddSettingsPageUIHandler(base::MakeUnique<PrintingHandler>());
 #endif
 
diff --git a/chrome/browser/ui/webui/settings/printing_handler.h b/chrome/browser/ui/webui/settings/printing_handler.h
index b233575e..5759261 100644
--- a/chrome/browser/ui/webui/settings/printing_handler.h
+++ b/chrome/browser/ui/webui/settings/printing_handler.h
@@ -6,7 +6,17 @@
 #define CHROME_BROWSER_UI_WEBUI_SETTINGS_PRINTING_HANDLER_H_
 
 #include "base/macros.h"
+#include "build/build_config.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "printing/features/features.h"
+
+#if defined(OS_CHROMEOS)
+#error "Not for use on ChromeOS"
+#endif
+
+#if !BUILDFLAG(ENABLE_PRINTING)
+#error "Printing must be enabled"
+#endif
 
 namespace settings {
 
diff --git a/chrome/common/extensions/docs/examples/api/default_command_override/background.js b/chrome/common/extensions/docs/examples/api/default_command_override/background.js
new file mode 100644
index 0000000..5d31360
--- /dev/null
+++ b/chrome/common/extensions/docs/examples/api/default_command_override/background.js
@@ -0,0 +1,17 @@
+// 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.
+chrome.commands.onCommand.addListener(function(command) {
+  chrome.tabs.query({currentWindow: true}, function(tabs) {
+    // Sort tabs according to their index in the window.
+    tabs.sort((a, b) => { return a.index < b.index; });
+    let activeIndex = tabs.findIndex((tab) => { return tab.active; });
+    let lastTab = tabs.length - 1;
+    let newIndex = -1;
+    if (command === 'flip-tabs-forward')
+      newIndex = activeIndex === 0 ? lastTab : activeIndex - 1;
+    else  // 'flip-tabs-backwards'
+      newIndex = activeIndex === lastTab ? 0 : activeIndex + 1;
+    chrome.tabs.update(tabs[newIndex].id, {active: true, highlighted: true});
+  });
+});
diff --git a/chrome/common/extensions/docs/examples/api/default_command_override/images/tabFlipper128.png b/chrome/common/extensions/docs/examples/api/default_command_override/images/tabFlipper128.png
new file mode 100644
index 0000000..1d1dcb8
--- /dev/null
+++ b/chrome/common/extensions/docs/examples/api/default_command_override/images/tabFlipper128.png
Binary files differ
diff --git a/chrome/common/extensions/docs/examples/api/default_command_override/images/tabFlipper16.png b/chrome/common/extensions/docs/examples/api/default_command_override/images/tabFlipper16.png
new file mode 100644
index 0000000..695f64e
--- /dev/null
+++ b/chrome/common/extensions/docs/examples/api/default_command_override/images/tabFlipper16.png
Binary files differ
diff --git a/chrome/common/extensions/docs/examples/api/default_command_override/images/tabFlipper32.png b/chrome/common/extensions/docs/examples/api/default_command_override/images/tabFlipper32.png
new file mode 100644
index 0000000..67ef0d3
--- /dev/null
+++ b/chrome/common/extensions/docs/examples/api/default_command_override/images/tabFlipper32.png
Binary files differ
diff --git a/chrome/common/extensions/docs/examples/api/default_command_override/images/tabFlipper48.png b/chrome/common/extensions/docs/examples/api/default_command_override/images/tabFlipper48.png
new file mode 100644
index 0000000..4bf7680
--- /dev/null
+++ b/chrome/common/extensions/docs/examples/api/default_command_override/images/tabFlipper48.png
Binary files differ
diff --git a/chrome/common/extensions/docs/examples/api/default_command_override/manifest.json b/chrome/common/extensions/docs/examples/api/default_command_override/manifest.json
new file mode 100644
index 0000000..803f2b9c
--- /dev/null
+++ b/chrome/common/extensions/docs/examples/api/default_command_override/manifest.json
@@ -0,0 +1,36 @@
+{
+  "name": "Tab Flipper",
+  "description": "Press Ctrl+Shift+Right or Ctrl+Shift+Left (Command+Shift+Right or Command+Shift+Left on a Mac) to flip through window tabs",
+  "version": "1.0",
+  "manifest_version": 2,
+  "background": {
+    "scripts": ["background.js"],
+    "persistent": false
+  },
+  "browser_action": {
+    "default_icon": "images/tabFlipper16.png",
+    "default_title": "Press Ctrl(Win)/Command(Mac)+Shift+ Left or Right to Flip Tabs"
+  },
+  "commands": {
+    "flip-tabs-forward": {
+      "suggested_key": {
+        "default": "Ctrl+Shift+Right",
+        "mac": "Command+Shift+Right"
+      },
+      "description": "Flip tabs forward"
+    },
+    "flip-tabs-backwards": {
+      "suggested_key": {
+        "default": "Ctrl+Shift+Left",
+        "mac": "Command+Shift+Left"
+      },
+      "description": "Flip tabs backwards"
+    }
+  },
+  "icons": {
+    "16": "images/tabFlipper16.png",
+    "32": "images/tabFlipper32.png",
+    "48": "images/tabFlipper48.png",
+    "128": "images/tabFlipper128.png"
+  }
+}
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 187aa88..6d71244 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -568,8 +568,7 @@
       "../browser/metrics/process_memory_metrics_emitter_browsertest.cc",
       "../browser/metrics/startup_metrics_browsertest.cc",
       "../browser/metrics/tab_reactivation_tracker_browsertest.cc",
-      "../browser/metrics/tab_stats_data_store_unittests.cc",
-      "../browser/metrics/tab_stats_tracker_unittests.cc",
+      "../browser/metrics/tab_stats_tracker_browsertest.cc",
       "../browser/metrics/ukm_browsertest.cc",
       "../browser/net/chrome_mojo_proxy_resolver_factory_browsertest.cc",
       "../browser/net/chrome_network_delegate_browsertest.cc",
@@ -2743,6 +2742,8 @@
       "../browser/media_galleries/win/mtp_device_delegate_impl_win_unittest.cc",
       "../browser/media_galleries/win/mtp_device_object_enumerator_unittest.cc",
       "../browser/memory/swap_thrashing_monitor_delegate_win_unittest.cc",
+      "../browser/metrics/tab_stats_data_store_unittests.cc",
+      "../browser/metrics/tab_stats_tracker_unittests.cc",
       "../browser/page_load_metrics/observers/session_restore_page_load_metrics_observer_unittest.cc",
       "../browser/resource_coordinator/background_tab_navigation_throttle_unittest.cc",
       "../browser/resource_coordinator/lifecycle_unit_unittest.cc",
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 ba51133..72044e77 100644
--- a/chrome/test/data/webui/print_preview/native_layer_stub.js
+++ b/chrome/test/data/webui/print_preview/native_layer_stub.js
@@ -73,22 +73,20 @@
     }
 
     /** @override */
-    getPreview(
-        destination, printTicketStore, documentInfo, generateDraft, requestId) {
+    getPreview(printTicket, pageCount) {
       this.methodCalled('getPreview', {
-        destination: destination,
-        printTicketStore: printTicketStore,
-        documentInfo: documentInfo,
-        generateDraft: generateDraft,
-        requestId: requestId,
+        printTicket: printTicket,
+        pageCount: pageCount
       });
-      if (destination.id == this.badPrinterId_) {
+      const printTicketParsed = JSON.parse(printTicket);
+      if (printTicketParsed.deviceName == this.badPrinterId_) {
         let rejectString = print_preview.PreviewArea.EventType.SETTINGS_INVALID;
         rejectString = rejectString.substring(
             rejectString.lastIndexOf('.') + 1, rejectString.length);
         return Promise.reject(rejectString);
       }
-      const pageRanges = printTicketStore.pageRange.getDocumentPageRanges();
+      const pageRanges = printTicketParsed.pageRange;
+      const requestId = printTicketParsed.requestID;
       if (pageRanges.length == 0) {  // assume full length document, 1 page.
         cr.webUIListenerCallback('page-count-ready', 1, requestId, 100);
         cr.webUIListenerCallback('page-preview-ready', 0, 0, requestId);
@@ -122,20 +120,8 @@
     }
 
     /** @override */
-    print(
-      destination,
-      printTicketStore,
-      documentInfo,
-      opt_isOpenPdfInPreview,
-      opt_showSystemDialog
-    ) {
-      this.methodCalled('print', {
-        destination: destination,
-        printTicketStore: printTicketStore,
-        documentInfo: documentInfo,
-        openPdfInPreview: opt_isOpenPdfInPreview || false,
-        showSystemDialog: opt_showSystemDialog || false,
-      });
+    print(printTicket) {
+      this.methodCalled('print', printTicket);
       return Promise.resolve();
     }
 
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 a01a34b..8fe10e294 100644
--- a/chrome/test/data/webui/print_preview/print_preview_tests.js
+++ b/chrome/test/data/webui/print_preview/print_preview_tests.js
@@ -644,24 +644,22 @@
                 'ID1', printPreview.destinationStore_.selectedDestination.id);
 
             // Validate the parameters for getPreview match the app state.
-            expectEquals('CUSTOM_SQUARE',
-                         args[1].printTicketStore.mediaSize.getValue().name);
-            expectEquals('90', args[1].printTicketStore.scaling.getValue());
-            expectEquals(
-                100, args[1].printTicketStore.dpi.getValue().horizontal_dpi);
-            expectTrue(args[1].printTicketStore.headerFooter.getValue());
-            expectTrue(args[1].printTicketStore.cssBackground.getValue());
-            expectTrue(args[1].printTicketStore.fitToPage.getValue());
-            expectTrue(args[1].printTicketStore.collate.getValue());
-            expectTrue(args[1].printTicketStore.duplex.getValue());
-            expectTrue(args[1].printTicketStore.landscape.getValue());
-            expectTrue(args[1].printTicketStore.color.getValue());
+            const ticket = JSON.parse(args[1].printTicket);
+            expectEquals('CUSTOM_SQUARE', ticket.mediaSize.name);
+            expectEquals(90, ticket.scaleFactor);
+            expectEquals(100, ticket.dpiHorizontal);
+            expectTrue(ticket.headerFooterEnabled);
+            expectTrue(ticket.shouldPrintBackgrounds);
+            expectTrue(ticket.fitToPageEnabled);
+            expectTrue(ticket.collate);
+            expectEquals(ticket.duplex,
+                         print_preview.PreviewGenerator.DuplexMode.LONG_EDGE);
+            expectTrue(ticket.landscape);
+            expectEquals(ticket.color,
+                         print_preview.PreviewGenerator.ColorMode.COLOR);
             expectEquals(print_preview.ticket_items.MarginsTypeValue.CUSTOM,
-                         args[1].printTicketStore.marginsType.getValue());
-            expectEquals(
-                74,
-                args[1].printTicketStore.customMargins.getValue().get(
-                    print_preview.ticket_items.CustomMarginsOrientation.TOP));
+                         ticket.marginsType);
+            expectEquals(74, ticket.marginsCustom.marginTop);
 
             // Change scaling (a persisted ticket item value)
             expandMoreSettings();
@@ -842,8 +840,9 @@
           const fitToPageContainer =
               scalingSettings.querySelector('#fit-to-page-container');
           checkElementDisplayed(fitToPageContainer, true);
-          expectTrue(args[1].printTicketStore.fitToPage.getValue());
-          expectEquals('100', args[1].printTicketStore.scaling.getValue());
+          const ticket = JSON.parse(args[1].printTicket);
+          expectTrue(ticket.fitToPageEnabled);
+          expectEquals(100, ticket.scaleFactor);
           expectTrue(fitToPageContainer.querySelector('.checkbox').checked);
           expandMoreSettings();
           checkSectionVisible($('media-size-settings'), true);
@@ -863,10 +862,12 @@
           scalingInput.dispatchEvent(enterEvent);
 
           // Wait for the preview to refresh and verify print ticket and
-          // display.
+          // display. There will be 2 preview requests. Since we only catch
+          // the first one, only verify fit to page in print ticket.
           return nativeLayer.whenCalled('getPreview').then(function(args) {
-            expectFalse(args.printTicketStore.fitToPage.getValue());
-            expectEquals('105', args.printTicketStore.scaling.getValue());
+            console.log('args.printticket ' + args.printTicket);
+            const updatedTicket = JSON.parse(args.printTicket);
+            expectFalse(updatedTicket.fitToPageEnabled);
             expectFalse(fitToPageContainer.querySelector('.checkbox').checked);
             return whenAnimationDone('more-settings');
           });
@@ -1256,8 +1257,9 @@
           setupSettingsAndDestinationsWithCapabilities(),
           nativeLayer.whenCalled('getPreview'),
       ]).then(function(args) {
-        expectEquals(0, args[1].requestId);
-        expectEquals('FooDevice', args[1].destination.id);
+        const ticket = JSON.parse(args[1].printTicket);
+        expectEquals(0, ticket.requestID);
+        expectEquals('FooDevice', ticket.deviceName);
         nativeLayer.reset();
 
         // Setup capabilities for BarDevice.
@@ -1275,8 +1277,9 @@
         printPreview.destinationStore_.selectDestination(barDestination);
         return waitForPrinterToUpdatePreview();
       }).then(function(args) {
-        expectEquals(1, args[1].requestId);
-        expectEquals('BarDevice', args[1].destination.id);
+        const ticket = JSON.parse(args[1].printTicket);
+        expectEquals(1, ticket.requestID);
+        expectEquals('BarDevice', ticket.deviceName);
       });
     });
 
@@ -1434,8 +1437,9 @@
         expectEquals('ID1', id);
         return nativeLayer.whenCalled('getPreview');
       }).then(function(previewArgs) {
-        expectEquals(0, previewArgs.requestId);
-        expectEquals('ID1', previewArgs.destination.id);
+        const ticket = JSON.parse(previewArgs.printTicket);
+        expectEquals(0, ticket.requestID);
+        expectEquals('ID1', ticket.deviceName);
       });
     });
 
@@ -1489,30 +1493,22 @@
         return nativeLayer.whenCalled('print');
       }).then(
           /**
-           * @param {{destination: !print_preview.Destination,
-           *          printTicketStore: !print_preview.PrintTicketStore,
-           *          cloudPrintInterface: print_preview
-           *                                  .CloudPrintInterface,
-           *          documentInfo: print_preview.DocumentInfo,
-           *          openPdfInPreview: boolean,
-           *          showSystemDialog: boolean}} args
-           *      The arguments that print() was called with.
+           * @param {string} printTicket The print ticket print() was called
+           *     for.
            */
-          function(args) {
+          function(printTicket) {
             // Sanity check some printing argument values.
-            const printTicketStore = args.printTicketStore;
-            expectEquals(barDevice.printer.deviceName, args.destination.id);
+            const ticket = JSON.parse(printTicket);
+            expectEquals(barDevice.printer.deviceName, ticket.deviceName);
             expectEquals(
                 getDefaultOrientation(barDevice) == 'LANDSCAPE',
-                printTicketStore.landscape.getValue());
-            expectEquals(1, printTicketStore.copies.getValueAsNumber());
+                ticket.landscape);
+            expectEquals(1, ticket.copies);
             const mediaDefault = getDefaultMediaSize(barDevice);
             expectEquals(
-                mediaDefault.width_microns,
-                printTicketStore.mediaSize.getValue().width_microns);
+                mediaDefault.width_microns, ticket.mediaSize.width_microns);
             expectEquals(
-                mediaDefault.height_microns,
-                printTicketStore.mediaSize.getValue().height_microns);
+                mediaDefault.height_microns, ticket.mediaSize.height_microns);
             return nativeLayer.whenCalled('hidePreview');
           });
     });
@@ -1526,16 +1522,18 @@
       ]).then(function(args) {
         // The first request should generate draft because there was no
         // previous print preview draft.
-        expectTrue(args[1].generateDraft);
-        expectEquals(0, args[1].requestId);
+        const ticket = JSON.parse(args[1].printTicket);
+        expectTrue(ticket.generateDraft);
+        expectEquals(0, ticket.requestID);
         nativeLayer.resetResolver('getPreview');
 
         // Change the page range - no new draft needed.
         printPreview.printTicketStore_.pageRange.updateValue('2');
         return nativeLayer.whenCalled('getPreview');
       }).then(function(args) {
-        expectFalse(args.generateDraft);
-        expectEquals(1, args.requestId);
+        const ticket = JSON.parse(args.printTicket);
+        expectFalse(ticket.generateDraft);
+        expectEquals(1, ticket.requestID);
         nativeLayer.resetResolver('getPreview');
 
         // Change the margin type - need to regenerate again.
@@ -1543,8 +1541,9 @@
             print_preview.ticket_items.MarginsTypeValue.NO_MARGINS);
         return nativeLayer.whenCalled('getPreview');
       }).then(function(args) {
-        expectTrue(args.generateDraft);
-        expectEquals(2, args.requestId);
+        const ticket = JSON.parse(args.printTicket);
+        expectTrue(ticket.generateDraft);
+        expectEquals(2, ticket.requestID);
       });
     });
 
@@ -1604,17 +1603,11 @@
               return nativeLayer.whenCalled('print');
             }).then(
                 /**
-                 * @param {{destination: !print_preview.Destination,
-                 *          printTicketStore: !print_preview.PrintTicketStore,
-                 *          cloudPrintInterface: print_preview
-                 *                                  .CloudPrintInterface,
-                 *          documentInfo: print_preview.DocumentInfo,
-                 *          openPdfInPreview: boolean
-                 *          showSystemDialog: boolean}} args
-                 *      The arguments that print() was called with.
+                 * @param {string} printTicket The print ticket print() was
+                 *     called for.
                  */
-                function(args) {
-                  expectTrue(args.openPdfInPreview);
+                function(printTicket) {
+                  expectTrue(JSON.parse(printTicket).OpenPDFInPreview);
                   return nativeLayer.whenCalled('hidePreview');
                 });
       });
@@ -1672,17 +1665,11 @@
               return nativeLayer.whenCalled('print');
             }).then(
                 /**
-                 * @param {{destination: !print_preview.Destination,
-                 *          printTicketStore: !print_preview.PrintTicketStore,
-                 *          cloudPrintInterface: print_preview
-                 *                                  .CloudPrintInterface,
-                 *          documentInfo: print_preview.DocumentInfo,
-                 *          openPdfInPreview: boolean
-                 *          showSystemDialog: boolean}} args
-                 *      The arguments that print() was called with.
+                 * @param {string} printTicket The print ticket print() was
+                 *     called for.
                  */
-                function(args) {
-                  expectTrue(args.showSystemDialog);
+                function(printTicket) {
+                  expectTrue(JSON.parse(printTicket).showSystemDialog);
                   return nativeLayer.whenCalled('hidePreview');
                 });
       });
diff --git a/chromeos/printing/printer_translator.cc b/chromeos/printing/printer_translator.cc
index d361d6b..14b6ac42 100644
--- a/chromeos/printing/printer_translator.cc
+++ b/chromeos/printing/printer_translator.cc
@@ -30,6 +30,7 @@
 const char kUri[] = "uri";
 const char kUUID[] = "uuid";
 const char kPpdResource[] = "ppd_resource";
+const char kGuid[] = "guid";
 
 // Populates the |printer| object with corresponding fields from |value|.
 // Returns false if |value| is missing a required field.
@@ -84,7 +85,8 @@
 std::unique_ptr<Printer> RecommendedPrinterToPrinter(
     const base::DictionaryValue& pref) {
   std::string id;
-  if (!pref.GetString(kPrinterId, &id)) {
+  // Printer id comes from the id or guid field depending on the source.
+  if (!pref.GetString(kPrinterId, &id) && !pref.GetString(kGuid, &id)) {
     LOG(WARNING) << "Record id required";
     return nullptr;
   }
diff --git a/chromeos/printing/printer_translator_unittest.cc b/chromeos/printing/printer_translator_unittest.cc
index ce265586..dc152e82 100644
--- a/chromeos/printing/printer_translator_unittest.cc
+++ b/chromeos/printing/printer_translator_unittest.cc
@@ -25,6 +25,8 @@
 const char kModel[] = "Inktastic Laser Magic";
 const char kMakeAndModel[] = "Chrome Inktastic Laser Magic";
 
+const char kGUID[] = "{4d8faf22-303f-46c6-ab30-352d47d6a8b9}";
+
 // PpdReference test data
 const char kEffectiveMakeAndModel[] = "PrintBlaster LazerInker 2000";
 
@@ -148,4 +150,17 @@
   EXPECT_EQ(kMake, printer->make_and_model());
 }
 
+TEST(PrinterTranslatorTest, BulkPrinterJson) {
+  base::DictionaryValue preference;
+  preference.SetString("guid", kGUID);
+  preference.SetString("display_name", kName);
+  preference.SetString("uri", kUri);
+  preference.SetString("ppd_resource.effective_model", kEffectiveMakeAndModel);
+
+  std::unique_ptr<Printer> printer = RecommendedPrinterToPrinter(preference);
+  EXPECT_TRUE(printer);
+
+  EXPECT_EQ(kGUID, printer->id());
+}
+
 }  // namespace chromeos
diff --git a/components/favicon/ios/web_favicon_driver.h b/components/favicon/ios/web_favicon_driver.h
index a387ab1..482a954 100644
--- a/components/favicon/ios/web_favicon_driver.h
+++ b/components/favicon/ios/web_favicon_driver.h
@@ -60,8 +60,6 @@
                    history::HistoryService* history_service);
 
   // web::WebStateObserver implementation.
-  void DidStartNavigation(web::WebState* web_state,
-                          web::NavigationContext* navigation_context) override;
   void DidFinishNavigation(web::WebState* web_state,
                            web::NavigationContext* navigation_context) override;
   void FaviconUrlUpdated(
@@ -76,9 +74,6 @@
   // Image Fetcher used to fetch favicon.
   image_fetcher::IOSImageDataFetcherWrapper image_fetcher_;
 
-  // Caches the favicon URLs candidates for same-document navigations.
-  std::vector<favicon::FaviconURL> candidates_;
-
   // The WebState this instance is observing. Will be null after
   // WebStateDestroyed has been called.
   web::WebState* web_state_ = nullptr;
diff --git a/components/favicon/ios/web_favicon_driver.mm b/components/favicon/ios/web_favicon_driver.mm
index c00b18a..64537f56 100644
--- a/components/favicon/ios/web_favicon_driver.mm
+++ b/components/favicon/ios/web_favicon_driver.mm
@@ -117,13 +117,10 @@
   // On iOS, the active URL can change between calls to FetchFavicon(). For
   // instance, FetchFavicon() is not synchronously called when the active URL
   // changes as a result of CRWSessionController::goToEntry().
-  if (GetActiveURL() != page_url && !page_url.is_empty()) {
-    return;
-  }
-
   web::NavigationItem* item =
       web_state_->GetNavigationManager()->GetVisibleItem();
-  DCHECK(item);
+  if (!item || item->GetURL() != page_url)
+    return;
 
   web::FaviconStatus& favicon_status = item->GetFavicon();
   favicon_status.valid = true;
@@ -168,32 +165,11 @@
   DCHECK(!web_state_);
 }
 
-void WebFaviconDriver::DidStartNavigation(
-    web::WebState* web_state,
-    web::NavigationContext* navigation_context) {
-  DCHECK_EQ(web_state_, web_state);
-  SetFaviconOutOfDateForPage(navigation_context->GetUrl(),
-                             /*force_reload=*/false);
-}
-
 void WebFaviconDriver::DidFinishNavigation(
     web::WebState* web_state,
     web::NavigationContext* navigation_context) {
-  DCHECK_EQ(web_state_, web_state);
-  if (navigation_context->GetError())
-    return;
-
-  // Fetch the favicon for the new URL.
-  FetchFavicon(navigation_context->GetUrl(),
+  FetchFavicon(web_state->GetLastCommittedURL(),
                navigation_context->IsSameDocument());
-
-  if (navigation_context->IsSameDocument()) {
-    if (!candidates_.empty()) {
-      FaviconUrlUpdatedInternal(candidates_);
-    }
-  } else {
-    candidates_.clear();
-  }
 }
 
 void WebFaviconDriver::FaviconUrlUpdated(
@@ -201,8 +177,8 @@
     const std::vector<web::FaviconURL>& candidates) {
   DCHECK_EQ(web_state_, web_state);
   DCHECK(!candidates.empty());
-  candidates_ = FaviconURLsFromWebFaviconURLs(candidates);
-  FaviconUrlUpdatedInternal(candidates_);
+  OnUpdateCandidates(GetActiveURL(), FaviconURLsFromWebFaviconURLs(candidates),
+                     GURL());
 }
 
 void WebFaviconDriver::WebStateDestroyed(web::WebState* web_state) {
@@ -211,9 +187,4 @@
   web_state_ = nullptr;
 }
 
-void WebFaviconDriver::FaviconUrlUpdatedInternal(
-    const std::vector<favicon::FaviconURL>& candidates) {
-  OnUpdateCandidates(GetActiveURL(), candidates, GURL());
-}
-
 }  // namespace favicon
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 1f08638c..5e7a35a 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1562,6 +1562,8 @@
     "shared_worker/worker_storage_partition.h",
     "site_instance_impl.cc",
     "site_instance_impl.h",
+    "site_isolation_policy.cc",
+    "site_isolation_policy.h",
     "speech/speech_recognition_dispatcher_host.cc",
     "speech/speech_recognition_dispatcher_host.h",
     "speech/speech_recognition_manager_impl.cc",
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 2ed9c26..e9bee04d 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -84,6 +84,7 @@
 #include "content/browser/renderer_host/media/media_stream_manager.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/service_manager/service_manager_context.h"
+#include "content/browser/site_isolation_policy.h"
 #include "content/browser/speech/speech_recognition_manager_impl.h"
 #include "content/browser/startup_task_runner.h"
 #include "content/browser/tracing/background_tracing_manager_impl.h"
@@ -93,7 +94,6 @@
 #include "content/browser/webui/url_data_manager.h"
 #include "content/common/content_switches_internal.h"
 #include "content/common/service_manager/service_manager_connection_impl.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/task_scheduler.h"
 #include "content/public/browser/browser_main_parts.h"
 #include "content/public/browser/content_browser_client.h"
@@ -913,8 +913,6 @@
   ChildProcessSecurityPolicyImpl* policy =
       ChildProcessSecurityPolicyImpl::GetInstance();
   policy->AddIsolatedOrigins(SiteIsolationPolicy::GetIsolatedOrigins());
-  policy->AddIsolatedOrigins(
-      GetContentClient()->browser()->GetOriginsRequiringDedicatedProcess());
 
   return result_code_;
 }
diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc
index bc1151fc..fc152cf 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.cc
+++ b/content/browser/browser_plugin/browser_plugin_guest.cc
@@ -41,7 +41,6 @@
 #include "content/common/drag_messages.h"
 #include "content/common/input/ime_text_span_conversions.h"
 #include "content/common/input_messages.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/text_input_state.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/browser_context.h"
diff --git a/content/browser/browser_side_navigation_browsertest.cc b/content/browser/browser_side_navigation_browsertest.cc
index 4a3e30e..c0125365 100644
--- a/content/browser/browser_side_navigation_browsertest.cc
+++ b/content/browser/browser_side_navigation_browsertest.cc
@@ -13,7 +13,6 @@
 #include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/frame_messages.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/notification_types.h"
 #include "content/public/browser/web_contents.h"
diff --git a/content/browser/browsing_instance.cc b/content/browser/browsing_instance.cc
index 33f0ebc..c7ed216 100644
--- a/content/browser/browsing_instance.cc
+++ b/content/browser/browsing_instance.cc
@@ -7,7 +7,7 @@
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "content/browser/site_instance_impl.h"
-#include "content/common/site_isolation_policy.h"
+#include "content/browser/site_isolation_policy.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/common/content_switches.h"
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc
index 2356dbb2..2fbe6d58 100644
--- a/content/browser/child_process_security_policy_impl.cc
+++ b/content/browser/child_process_security_policy_impl.cc
@@ -20,7 +20,7 @@
 #include "build/build_config.h"
 #include "content/browser/isolated_origin_util.h"
 #include "content/browser/site_instance_impl.h"
-#include "content/common/site_isolation_policy.h"
+#include "content/browser/site_isolation_policy.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/child_process_data.h"
diff --git a/content/browser/devtools/shared_worker_devtools_agent_host.cc b/content/browser/devtools/shared_worker_devtools_agent_host.cc
index 4e32657..91fd27e5 100644
--- a/content/browser/devtools/shared_worker_devtools_agent_host.cc
+++ b/content/browser/devtools/shared_worker_devtools_agent_host.cc
@@ -4,31 +4,48 @@
 
 #include "content/browser/devtools/shared_worker_devtools_agent_host.h"
 
+#include "content/browser/devtools/devtools_session.h"
+#include "content/browser/devtools/protocol/inspector_handler.h"
+#include "content/browser/devtools/protocol/network_handler.h"
+#include "content/browser/devtools/protocol/protocol.h"
+#include "content/browser/devtools/protocol/schema_handler.h"
 #include "content/browser/devtools/shared_worker_devtools_manager.h"
+#include "content/browser/shared_worker/shared_worker_host.h"
 #include "content/browser/shared_worker/shared_worker_instance.h"
 #include "content/browser/shared_worker/shared_worker_service_impl.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
 
 namespace content {
 
 SharedWorkerDevToolsAgentHost::SharedWorkerDevToolsAgentHost(
-    WorkerId worker_id,
-    const SharedWorkerInstance& shared_worker)
-    : WorkerDevToolsAgentHost(shared_worker.devtools_worker_token(), worker_id),
-      shared_worker_(new SharedWorkerInstance(shared_worker)) {
+    SharedWorkerHost* worker_host)
+    : DevToolsAgentHostImpl(
+          worker_host->instance()->devtools_worker_token().ToString()),
+      worker_host_(worker_host),
+      instance_(new SharedWorkerInstance(*worker_host->instance())) {
   NotifyCreated();
 }
 
+SharedWorkerDevToolsAgentHost::~SharedWorkerDevToolsAgentHost() {
+  SharedWorkerDevToolsManager::GetInstance()->AgentHostDestroyed(this);
+}
+
+BrowserContext* SharedWorkerDevToolsAgentHost::GetBrowserContext() {
+  RenderProcessHost* rph = GetProcess();
+  return rph ? rph->GetBrowserContext() : nullptr;
+}
+
 std::string SharedWorkerDevToolsAgentHost::GetType() {
   return kTypeSharedWorker;
 }
 
 std::string SharedWorkerDevToolsAgentHost::GetTitle() {
-  return shared_worker_->name();
+  return instance_->name();
 }
 
 GURL SharedWorkerDevToolsAgentHost::GetURL() {
-  return shared_worker_->url();
+  return instance_->url();
 }
 
 bool SharedWorkerDevToolsAgentHost::Activate() {
@@ -39,19 +56,128 @@
 }
 
 bool SharedWorkerDevToolsAgentHost::Close() {
-  static_cast<SharedWorkerServiceImpl*>(SharedWorkerService::GetInstance())
-      ->TerminateWorkerById(worker_id().first, worker_id().second);
+  if (worker_host_)
+    worker_host_->TerminateWorker();
   return true;
 }
 
-bool SharedWorkerDevToolsAgentHost::Matches(
-    const SharedWorkerInstance& other) {
-  return shared_worker_->Matches(other);
+void SharedWorkerDevToolsAgentHost::AttachSession(DevToolsSession* session) {
+  if (RenderProcessHost* host = GetProcess()) {
+    if (sessions().size() == 1)
+      host->AddRoute(worker_host_->route_id(), this);
+    session->SetRenderer(host, nullptr);
+    if (!waiting_ready_for_reattach_) {
+      host->Send(new DevToolsAgentMsg_Attach(worker_host_->route_id(),
+                                             session->session_id()));
+    }
+  }
+  session->SetFallThroughForNotFound(true);
+  session->AddHandler(std::make_unique<protocol::InspectorHandler>());
+  session->AddHandler(std::make_unique<protocol::NetworkHandler>(GetId()));
+  session->AddHandler(std::make_unique<protocol::SchemaHandler>());
 }
 
-SharedWorkerDevToolsAgentHost::~SharedWorkerDevToolsAgentHost() {
-  SharedWorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData(
-      worker_id());
+void SharedWorkerDevToolsAgentHost::DetachSession(int session_id) {
+  if (RenderProcessHost* host = GetProcess()) {
+    host->Send(
+        new DevToolsAgentMsg_Detach(worker_host_->route_id(), session_id));
+    if (!sessions().size())
+      host->RemoveRoute(worker_host_->route_id());
+  }
+}
+
+bool SharedWorkerDevToolsAgentHost::DispatchProtocolMessage(
+    DevToolsSession* session,
+    const std::string& message) {
+  int call_id = 0;
+  std::string method;
+  if (session->Dispatch(message, &call_id, &method) !=
+      protocol::Response::kFallThrough) {
+    return true;
+  }
+
+  if (RenderProcessHost* host = GetProcess()) {
+    host->Send(new DevToolsAgentMsg_DispatchOnInspectorBackend(
+        worker_host_->route_id(), session->session_id(), call_id, method,
+        message));
+    session->waiting_messages()[call_id] = {method, message};
+  }
+  return true;
+}
+
+bool SharedWorkerDevToolsAgentHost::OnMessageReceived(const IPC::Message& msg) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP(SharedWorkerDevToolsAgentHost, msg)
+    IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
+                        OnDispatchOnInspectorFrontend)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+  return handled;
+}
+
+bool SharedWorkerDevToolsAgentHost::Matches(SharedWorkerHost* worker_host) {
+  return instance_->Matches(*worker_host->instance());
+}
+
+void SharedWorkerDevToolsAgentHost::WorkerReadyForInspection() {
+  DCHECK(worker_host_);
+  if (!waiting_ready_for_reattach_)
+    return;
+  waiting_ready_for_reattach_ = false;
+  if (RenderProcessHost* host = GetProcess()) {
+    for (DevToolsSession* session : sessions()) {
+      host->Send(new DevToolsAgentMsg_Reattach(worker_host_->route_id(),
+                                               session->session_id(),
+                                               session->state_cookie()));
+      for (const auto& pair : session->waiting_messages()) {
+        int call_id = pair.first;
+        const DevToolsSession::Message& message = pair.second;
+        host->Send(new DevToolsAgentMsg_DispatchOnInspectorBackend(
+            worker_host_->route_id(), session->session_id(), call_id,
+            message.method, message.message));
+      }
+    }
+  }
+}
+
+bool SharedWorkerDevToolsAgentHost::WorkerRestarted(
+    SharedWorkerHost* worker_host) {
+  DCHECK(!worker_host_);
+  worker_host_ = worker_host;
+  if (RenderProcessHost* host = GetProcess()) {
+    if (sessions().size())
+      host->AddRoute(worker_host_->route_id(), this);
+    for (DevToolsSession* session : sessions())
+      session->SetRenderer(host, nullptr);
+  }
+  waiting_ready_for_reattach_ = IsAttached();
+  return waiting_ready_for_reattach_;
+}
+
+void SharedWorkerDevToolsAgentHost::WorkerDestroyed() {
+  DCHECK(worker_host_);
+  for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
+    inspector->TargetCrashed();
+  for (DevToolsSession* session : sessions())
+    session->SetRenderer(nullptr, nullptr);
+  if (sessions().size()) {
+    if (RenderProcessHost* host = GetProcess())
+      host->RemoveRoute(worker_host_->route_id());
+  }
+  worker_host_ = nullptr;
+}
+
+RenderProcessHost* SharedWorkerDevToolsAgentHost::GetProcess() {
+  return worker_host_ ? RenderProcessHost::FromID(worker_host_->process_id())
+                      : nullptr;
+}
+
+void SharedWorkerDevToolsAgentHost::OnDispatchOnInspectorFrontend(
+    const DevToolsMessageChunk& message) {
+  DevToolsSession* session = SessionById(message.session_id);
+  if (session)
+    session->ReceiveMessageChunk(message);
 }
 
 }  // namespace content
diff --git a/content/browser/devtools/shared_worker_devtools_agent_host.h b/content/browser/devtools/shared_worker_devtools_agent_host.h
index 1566798..c445bc9 100644
--- a/content/browser/devtools/shared_worker_devtools_agent_host.h
+++ b/content/browser/devtools/shared_worker_devtools_agent_host.h
@@ -6,20 +6,24 @@
 #define CONTENT_BROWSER_DEVTOOLS_SHARED_WORKER_DEVTOOLS_AGENT_HOST_H_
 
 #include "base/macros.h"
-#include "content/browser/devtools/worker_devtools_agent_host.h"
+#include "content/browser/devtools/devtools_agent_host_impl.h"
+#include "ipc/ipc_listener.h"
 
 namespace content {
 
 class SharedWorkerInstance;
+class SharedWorkerHost;
+class RenderProcessHost;
 
-class SharedWorkerDevToolsAgentHost : public WorkerDevToolsAgentHost {
+class SharedWorkerDevToolsAgentHost : public DevToolsAgentHostImpl,
+                                      public IPC::Listener {
  public:
   using List = std::vector<scoped_refptr<SharedWorkerDevToolsAgentHost>>;
 
-  SharedWorkerDevToolsAgentHost(WorkerId worker_id,
-                                const SharedWorkerInstance& shared_worker);
+  explicit SharedWorkerDevToolsAgentHost(SharedWorkerHost* worker_host);
 
   // DevToolsAgentHost override.
+  BrowserContext* GetBrowserContext() override;
   std::string GetType() override;
   std::string GetTitle() override;
   GURL GetURL() override;
@@ -27,13 +31,31 @@
   void Reload() override;
   bool Close() override;
 
-  bool Matches(const SharedWorkerInstance& other);
+  // DevToolsAgentHostImpl overrides.
+  void AttachSession(DevToolsSession* session) override;
+  void DetachSession(int session_id) override;
+  bool DispatchProtocolMessage(DevToolsSession* session,
+                               const std::string& message) override;
+
+  // IPC::Listener implementation.
+  bool OnMessageReceived(const IPC::Message& msg) override;
+
+  bool Matches(SharedWorkerHost* worker_host);
+  void WorkerReadyForInspection();
+  // Returns whether the worker should be paused for reattach.
+  bool WorkerRestarted(SharedWorkerHost* worker_host);
+  void WorkerDestroyed();
 
  private:
   friend class SharedWorkerDevToolsManagerTest;
 
   ~SharedWorkerDevToolsAgentHost() override;
-  std::unique_ptr<SharedWorkerInstance> shared_worker_;
+  RenderProcessHost* GetProcess();
+  void OnDispatchOnInspectorFrontend(const DevToolsMessageChunk& message);
+
+  SharedWorkerHost* worker_host_;
+  std::unique_ptr<SharedWorkerInstance> instance_;
+  bool waiting_ready_for_reattach_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(SharedWorkerDevToolsAgentHost);
 };
diff --git a/content/browser/devtools/shared_worker_devtools_manager.cc b/content/browser/devtools/shared_worker_devtools_manager.cc
index 9903398..1ba4e39 100644
--- a/content/browser/devtools/shared_worker_devtools_manager.cc
+++ b/content/browser/devtools/shared_worker_devtools_manager.cc
@@ -5,7 +5,7 @@
 #include "content/browser/devtools/shared_worker_devtools_manager.h"
 
 #include "content/browser/devtools/shared_worker_devtools_agent_host.h"
-#include "content/browser/shared_worker/shared_worker_instance.h"
+#include "content/browser/shared_worker/shared_worker_host.h"
 #include "content/public/browser/browser_thread.h"
 
 namespace content {
@@ -18,80 +18,61 @@
 
 void SharedWorkerDevToolsManager::AddAllAgentHosts(
     SharedWorkerDevToolsAgentHost::List* result) {
-  for (auto& worker : workers_) {
-    if (!worker.second->IsTerminated())
-      result->push_back(worker.second);
-  }
+  for (auto& it : live_hosts_)
+    result->push_back(it.second.get());
 }
 
-bool SharedWorkerDevToolsManager::WorkerCreated(
-    int worker_process_id,
-    int worker_route_id,
-    const SharedWorkerInstance& instance) {
+bool SharedWorkerDevToolsManager::WorkerCreated(SharedWorkerHost* worker_host) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  const WorkerId id(worker_process_id, worker_route_id);
-  AgentHostMap::iterator it =
-      FindExistingWorkerAgentHost(instance);
-  if (it == workers_.end()) {
-    workers_[id] = new SharedWorkerDevToolsAgentHost(id, instance);
+  DCHECK(live_hosts_.find(worker_host) == live_hosts_.end());
+
+  auto it =
+      std::find_if(terminated_hosts_.begin(), terminated_hosts_.end(),
+                   [&worker_host](SharedWorkerDevToolsAgentHost* agent_host) {
+                     return agent_host->Matches(worker_host);
+                   });
+  if (it == terminated_hosts_.end()) {
+    live_hosts_[worker_host] = new SharedWorkerDevToolsAgentHost(worker_host);
     return false;
   }
 
-  // Worker restarted.
-  SharedWorkerDevToolsAgentHost* agent_host = it->second;
-  agent_host->WorkerRestarted(id);
-  workers_.erase(it);
-  workers_[id] = agent_host;
-  return agent_host->IsAttached();
+  SharedWorkerDevToolsAgentHost* agent_host = *it;
+  terminated_hosts_.erase(it);
+  live_hosts_[worker_host] = agent_host;
+  return agent_host->WorkerRestarted(worker_host);
 }
 
 void SharedWorkerDevToolsManager::WorkerReadyForInspection(
-    int worker_process_id,
-    int worker_route_id) {
+    SharedWorkerHost* worker_host) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  const WorkerId id(worker_process_id, worker_route_id);
-  AgentHostMap::iterator it = workers_.find(id);
-  if (it == workers_.end() || it->second->IsTerminated())
-    return;
-  it->second->WorkerReadyForInspection();
+  live_hosts_[worker_host]->WorkerReadyForInspection();
 }
 
 void SharedWorkerDevToolsManager::WorkerDestroyed(
-    int worker_process_id,
-    int worker_route_id) {
+    SharedWorkerHost* worker_host) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  const WorkerId id(worker_process_id, worker_route_id);
-  AgentHostMap::iterator it = workers_.find(id);
-  if (it == workers_.end() || it->second->IsTerminated())
-    return;
-  scoped_refptr<SharedWorkerDevToolsAgentHost> agent_host(it->second);
+  scoped_refptr<SharedWorkerDevToolsAgentHost> agent_host =
+      live_hosts_[worker_host];
+  live_hosts_.erase(worker_host);
+  terminated_hosts_.insert(agent_host.get());
   agent_host->WorkerDestroyed();
 }
 
-void SharedWorkerDevToolsManager::RemoveInspectedWorkerData(WorkerId id) {
+void SharedWorkerDevToolsManager::AgentHostDestroyed(
+    SharedWorkerDevToolsAgentHost* agent_host) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  workers_.erase(id);
+  auto it = terminated_hosts_.find(agent_host);
+  // Might be missing during shutdown due to different
+  // destruction order of this manager, shared workers
+  // and their agent hosts.
+  if (it != terminated_hosts_.end())
+    terminated_hosts_.erase(it);
 }
+
 SharedWorkerDevToolsManager::SharedWorkerDevToolsManager() {
 }
 
 SharedWorkerDevToolsManager::~SharedWorkerDevToolsManager() {
 }
 
-SharedWorkerDevToolsManager::AgentHostMap::iterator
-SharedWorkerDevToolsManager::FindExistingWorkerAgentHost(
-    const SharedWorkerInstance& instance) {
-  AgentHostMap::iterator it = workers_.begin();
-  for (; it != workers_.end(); ++it) {
-    if (it->second->Matches(instance))
-      break;
-  }
-  return it;
-}
-
-void SharedWorkerDevToolsManager::ResetForTesting() {
-  workers_.clear();
-}
-
-
 }  // namespace content
diff --git a/content/browser/devtools/shared_worker_devtools_manager.h b/content/browser/devtools/shared_worker_devtools_manager.h
index c7f4f7c..551d2ef 100644
--- a/content/browser/devtools/shared_worker_devtools_manager.h
+++ b/content/browser/devtools/shared_worker_devtools_manager.h
@@ -7,6 +7,7 @@
 
 #include <map>
 
+#include "base/containers/flat_set.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/singleton.h"
@@ -15,14 +16,12 @@
 namespace content {
 
 class SharedWorkerDevToolsAgentHost;
-class SharedWorkerInstance;
+class SharedWorkerHost;
 
 // Manages WorkerDevToolsAgentHost's for Shared Workers.
 // This class lives on UI thread.
 class CONTENT_EXPORT SharedWorkerDevToolsManager {
  public:
-  using WorkerId = std::pair<int, int>;
-
   // Returns the SharedWorkerDevToolsManager singleton.
   static SharedWorkerDevToolsManager* GetInstance();
 
@@ -31,12 +30,9 @@
 
   // Returns true when the worker must be paused on start because a DevTool
   // window for the same former SharedWorkerInstance is still opened.
-  bool WorkerCreated(int worker_process_id,
-                     int worker_route_id,
-                     const SharedWorkerInstance& instance);
-  void WorkerReadyForInspection(int worker_process_id, int worker_route_id);
-  void WorkerDestroyed(int worker_process_id, int worker_route_id);
-  void RemoveInspectedWorkerData(WorkerId id);
+  bool WorkerCreated(SharedWorkerHost* worker_host);
+  void WorkerReadyForInspection(SharedWorkerHost* worker_host);
+  void WorkerDestroyed(SharedWorkerHost* worker_host);
 
  private:
   friend struct base::DefaultSingletonTraits<SharedWorkerDevToolsManager>;
@@ -45,18 +41,17 @@
   FRIEND_TEST_ALL_PREFIXES(SharedWorkerDevToolsManagerTest, BasicTest);
   FRIEND_TEST_ALL_PREFIXES(SharedWorkerDevToolsManagerTest, AttachTest);
 
-  using AgentHostMap = std::map<WorkerId, SharedWorkerDevToolsAgentHost*>;
-
   SharedWorkerDevToolsManager();
   ~SharedWorkerDevToolsManager();
+  void AgentHostDestroyed(SharedWorkerDevToolsAgentHost* agent_host);
 
-  AgentHostMap::iterator FindExistingWorkerAgentHost(
-      const SharedWorkerInstance& instance);
+  // We retatin agent hosts as long as the shared worker is alive.
+  std::map<SharedWorkerHost*, scoped_refptr<SharedWorkerDevToolsAgentHost>>
+      live_hosts_;
+  // Clients may retain agent host for the terminated shared worker,
+  // and we reconnect them when shared worker is restarted.
+  base::flat_set<SharedWorkerDevToolsAgentHost*> terminated_hosts_;
 
-  // Resets to its initial state as if newly created.
-  void ResetForTesting();
-
-  AgentHostMap workers_;
   DISALLOW_COPY_AND_ASSIGN(SharedWorkerDevToolsManager);
 };
 
diff --git a/content/browser/devtools/shared_worker_devtools_manager_unittest.cc b/content/browser/devtools/shared_worker_devtools_manager_unittest.cc
deleted file mode 100644
index 66c5747..0000000
--- a/content/browser/devtools/shared_worker_devtools_manager_unittest.cc
+++ /dev/null
@@ -1,349 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/devtools/shared_worker_devtools_manager.h"
-
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/run_loop.h"
-#include "content/browser/browser_thread_impl.h"
-#include "content/browser/devtools/devtools_agent_host_impl.h"
-#include "content/browser/devtools/shared_worker_devtools_agent_host.h"
-#include "content/browser/devtools/worker_devtools_agent_host.h"
-#include "content/browser/shared_worker/shared_worker_instance.h"
-#include "content/browser/shared_worker/worker_storage_partition.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/storage_partition.h"
-#include "content/public/test/test_browser_context.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace content {
-namespace {
-
-class TestDevToolsClientHost : public DevToolsAgentHostClient {
- public:
-  TestDevToolsClientHost() {}
-  ~TestDevToolsClientHost() override {}
-  void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
-                               const std::string& message) override {}
-  void AgentHostClosed(DevToolsAgentHost* agent_host) override {}
-
-  void InspectAgentHost(DevToolsAgentHost* agent_host) {
-    if (agent_host_.get())
-      agent_host_->DetachClient(this);
-    agent_host_ = agent_host;
-    if (agent_host_.get())
-      agent_host_->AttachClient(this);
-  }
- private:
-  scoped_refptr<DevToolsAgentHost> agent_host_;
-  DISALLOW_COPY_AND_ASSIGN(TestDevToolsClientHost);
-};
-}  // namespace
-
-class SharedWorkerDevToolsManagerTest : public testing::Test {
- public:
-  typedef SharedWorkerDevToolsAgentHost::WorkerState WorkerState;
-
-  SharedWorkerDevToolsManagerTest()
-      : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
-        browser_context_(new TestBrowserContext()),
-        partition_(new WorkerStoragePartition(
-            BrowserContext::GetDefaultStoragePartition(browser_context_.get())
-                ->GetURLRequestContext(),
-            nullptr,
-            nullptr,
-            nullptr,
-            nullptr,
-            nullptr,
-            nullptr,
-            nullptr)),
-        partition_id_(*partition_.get()) {}
-
- protected:
-  void SetUp() override {
-    manager_ = SharedWorkerDevToolsManager::GetInstance();
-  }
-  void TearDown() override {
-    SharedWorkerDevToolsManager::GetInstance()->ResetForTesting();
-  }
-
-  void CheckWorkerState(int worker_process_id,
-                        int worker_route_id,
-                        WorkerState state) {
-    SharedWorkerDevToolsAgentHost* host =
-        GetDevToolsAgentHostForWorker(worker_process_id, worker_route_id);
-    EXPECT_TRUE(!!host);
-    EXPECT_EQ(state, host->state_);
-  }
-
-  void CheckWorkerNotExist(int worker_process_id, int worker_route_id) {
-    EXPECT_TRUE(
-        !GetDevToolsAgentHostForWorker(worker_process_id, worker_route_id));
-  }
-
-  void CheckWorkerCount(size_t size) {
-    EXPECT_EQ(size, manager_->workers_.size());
-  }
-
-  SharedWorkerDevToolsAgentHost* GetDevToolsAgentHostForWorker(
-      int worker_process_id,
-      int worker_route_id) {
-    const SharedWorkerDevToolsManager::WorkerId id(worker_process_id,
-                                                   worker_route_id);
-    auto it = manager_->workers_.find(id);
-    return it == manager_->workers_.end() ? nullptr : it->second;
-  }
-
-  TestBrowserThreadBundle browser_thread_bundle_;
-  std::unique_ptr<TestBrowserContext> browser_context_;
-  std::unique_ptr<WorkerStoragePartition> partition_;
-  const WorkerStoragePartitionId partition_id_;
-  SharedWorkerDevToolsManager* manager_;
-};
-
-TEST_F(SharedWorkerDevToolsManagerTest, BasicTest) {
-  scoped_refptr<DevToolsAgentHostImpl> agent_host;
-
-  SharedWorkerInstance instance1(
-      GURL("http://example.com/w.js"), std::string(),
-      url::Origin::Create(GURL("http://example.com/")), std::string(),
-      blink::kWebContentSecurityPolicyTypeReport, blink::kWebAddressSpacePublic,
-      browser_context_->GetResourceContext(), partition_id_,
-      blink::mojom::SharedWorkerCreationContextType::kNonsecure,
-      base::UnguessableToken::Create());
-
-  agent_host = GetDevToolsAgentHostForWorker(1, 1);
-  EXPECT_FALSE(agent_host.get());
-
-  // Created -> Started -> Destroyed
-  CheckWorkerNotExist(1, 1);
-  manager_->WorkerCreated(1, 1, instance1);
-  CheckWorkerState(1, 1, WorkerState::WORKER_UNINSPECTED);
-  manager_->WorkerReadyForInspection(1, 1);
-  CheckWorkerState(1, 1, WorkerState::WORKER_UNINSPECTED);
-  manager_->WorkerDestroyed(1, 1);
-  CheckWorkerNotExist(1, 1);
-
-  // Created -> GetDevToolsAgentHost -> Started -> Destroyed
-  CheckWorkerNotExist(1, 2);
-  manager_->WorkerCreated(1, 2, instance1);
-  CheckWorkerState(1, 2, WorkerState::WORKER_UNINSPECTED);
-  agent_host = GetDevToolsAgentHostForWorker(1, 2);
-  EXPECT_TRUE(agent_host.get());
-  CheckWorkerState(1, 2, WorkerState::WORKER_UNINSPECTED);
-  EXPECT_EQ(agent_host.get(), GetDevToolsAgentHostForWorker(1, 2));
-  manager_->WorkerReadyForInspection(1, 2);
-  CheckWorkerState(1, 2, WorkerState::WORKER_UNINSPECTED);
-  manager_->WorkerDestroyed(1, 2);
-  CheckWorkerState(1, 2, WorkerState::WORKER_TERMINATED);
-  agent_host = nullptr;
-  CheckWorkerNotExist(1, 2);
-
-  // Created -> Started -> GetDevToolsAgentHost -> Destroyed
-  CheckWorkerNotExist(1, 3);
-  manager_->WorkerCreated(1, 3, instance1);
-  CheckWorkerState(1, 3, WorkerState::WORKER_UNINSPECTED);
-  manager_->WorkerReadyForInspection(1, 3);
-  CheckWorkerState(1, 3, WorkerState::WORKER_UNINSPECTED);
-  agent_host = GetDevToolsAgentHostForWorker(1, 3);
-  EXPECT_TRUE(agent_host.get());
-  CheckWorkerState(1, 3, WorkerState::WORKER_UNINSPECTED);
-  manager_->WorkerDestroyed(1, 3);
-  CheckWorkerState(1, 3, WorkerState::WORKER_TERMINATED);
-  agent_host = nullptr;
-  CheckWorkerNotExist(1, 3);
-
-  // Created -> Destroyed
-  CheckWorkerNotExist(1, 4);
-  manager_->WorkerCreated(1, 4, instance1);
-  CheckWorkerState(1, 4, WorkerState::WORKER_UNINSPECTED);
-  manager_->WorkerDestroyed(1, 4);
-  CheckWorkerNotExist(1, 4);
-
-  // Created -> GetDevToolsAgentHost -> Destroyed
-  CheckWorkerNotExist(1, 5);
-  manager_->WorkerCreated(1, 5, instance1);
-  CheckWorkerState(1, 5, WorkerState::WORKER_UNINSPECTED);
-  agent_host = GetDevToolsAgentHostForWorker(1, 5);
-  EXPECT_TRUE(agent_host.get());
-  CheckWorkerState(1, 5, WorkerState::WORKER_UNINSPECTED);
-  manager_->WorkerDestroyed(1, 5);
-  CheckWorkerState(1, 5, WorkerState::WORKER_TERMINATED);
-  agent_host = nullptr;
-  CheckWorkerNotExist(1, 5);
-
-  // Created -> GetDevToolsAgentHost -> Free agent_host -> Destroyed
-  CheckWorkerNotExist(1, 6);
-  manager_->WorkerCreated(1, 6, instance1);
-  CheckWorkerState(1, 6, WorkerState::WORKER_UNINSPECTED);
-  agent_host = GetDevToolsAgentHostForWorker(1, 6);
-  EXPECT_TRUE(agent_host.get());
-  CheckWorkerState(1, 6, WorkerState::WORKER_UNINSPECTED);
-  agent_host = nullptr;
-  manager_->WorkerDestroyed(1, 6);
-  CheckWorkerNotExist(1, 6);
-}
-
-TEST_F(SharedWorkerDevToolsManagerTest, AttachTest) {
-  scoped_refptr<DevToolsAgentHostImpl> agent_host1;
-  scoped_refptr<DevToolsAgentHostImpl> agent_host2;
-
-  SharedWorkerInstance instance1(
-      GURL("http://example.com/w1.js"), std::string(),
-      url::Origin::Create(GURL("http://example.com/")), std::string(),
-      blink::kWebContentSecurityPolicyTypeReport, blink::kWebAddressSpacePublic,
-      browser_context_->GetResourceContext(), partition_id_,
-      blink::mojom::SharedWorkerCreationContextType::kNonsecure,
-      base::UnguessableToken::Create());
-  SharedWorkerInstance instance2(
-      GURL("http://example.com/w2.js"), std::string(),
-      url::Origin::Create(GURL("http://example.com/")), std::string(),
-      blink::kWebContentSecurityPolicyTypeReport, blink::kWebAddressSpacePublic,
-      browser_context_->GetResourceContext(), partition_id_,
-      blink::mojom::SharedWorkerCreationContextType::kNonsecure,
-      base::UnguessableToken::Create());
-
-  // Created -> GetDevToolsAgentHost -> Register -> Started -> Destroyed
-  std::unique_ptr<TestDevToolsClientHost> client_host1(
-      new TestDevToolsClientHost());
-  CheckWorkerNotExist(2, 1);
-  manager_->WorkerCreated(2, 1, instance1);
-  CheckWorkerState(2, 1, WorkerState::WORKER_UNINSPECTED);
-  agent_host1 = GetDevToolsAgentHostForWorker(2, 1);
-  EXPECT_TRUE(agent_host1.get());
-  CheckWorkerState(2, 1, WorkerState::WORKER_UNINSPECTED);
-  EXPECT_EQ(agent_host1.get(), GetDevToolsAgentHostForWorker(2, 1));
-  client_host1->InspectAgentHost(agent_host1.get());
-  CheckWorkerState(2, 1, WorkerState::WORKER_INSPECTED);
-  manager_->WorkerReadyForInspection(2, 1);
-  CheckWorkerState(2, 1, WorkerState::WORKER_INSPECTED);
-  manager_->WorkerDestroyed(2, 1);
-  CheckWorkerState(2, 1, WorkerState::WORKER_TERMINATED);
-  EXPECT_EQ(agent_host1.get(), GetDevToolsAgentHostForWorker(2, 1));
-
-  // Created -> Started -> GetDevToolsAgentHost -> Register -> Destroyed
-  std::unique_ptr<TestDevToolsClientHost> client_host2(
-      new TestDevToolsClientHost());
-  manager_->WorkerCreated(2, 2, instance2);
-  CheckWorkerState(2, 2, WorkerState::WORKER_UNINSPECTED);
-  manager_->WorkerReadyForInspection(2, 2);
-  CheckWorkerState(2, 2, WorkerState::WORKER_UNINSPECTED);
-  agent_host2 = GetDevToolsAgentHostForWorker(2, 2);
-  EXPECT_TRUE(agent_host2.get());
-  EXPECT_NE(agent_host1.get(), agent_host2.get());
-  EXPECT_EQ(agent_host2.get(), GetDevToolsAgentHostForWorker(2, 2));
-  CheckWorkerState(2, 2, WorkerState::WORKER_UNINSPECTED);
-  client_host2->InspectAgentHost(agent_host2.get());
-  CheckWorkerState(2, 2, WorkerState::WORKER_INSPECTED);
-  manager_->WorkerDestroyed(2, 2);
-  CheckWorkerState(2, 2, WorkerState::WORKER_TERMINATED);
-  EXPECT_EQ(agent_host2.get(), GetDevToolsAgentHostForWorker(2, 2));
-
-  // Re-created -> Started -> ClientHostClosing -> Destroyed
-  CheckWorkerState(2, 1, WorkerState::WORKER_TERMINATED);
-  manager_->WorkerCreated(2, 3, instance1);
-  CheckWorkerNotExist(2, 1);
-  CheckWorkerState(2, 3, WorkerState::WORKER_PAUSED_FOR_REATTACH);
-  EXPECT_EQ(agent_host1.get(), GetDevToolsAgentHostForWorker(2, 3));
-  manager_->WorkerReadyForInspection(2, 3);
-  CheckWorkerState(2, 3, WorkerState::WORKER_INSPECTED);
-  client_host1->InspectAgentHost(nullptr);
-  manager_->WorkerDestroyed(2, 3);
-  CheckWorkerState(2, 3, WorkerState::WORKER_TERMINATED);
-  agent_host1 = nullptr;
-  CheckWorkerNotExist(2, 3);
-
-  // Re-created -> Destroyed
-  CheckWorkerState(2, 2, WorkerState::WORKER_TERMINATED);
-  manager_->WorkerCreated(2, 4, instance2);
-  CheckWorkerNotExist(2, 2);
-  CheckWorkerState(2, 4, WorkerState::WORKER_PAUSED_FOR_REATTACH);
-  EXPECT_EQ(agent_host2.get(), GetDevToolsAgentHostForWorker(2, 4));
-  manager_->WorkerDestroyed(2, 4);
-  CheckWorkerNotExist(2, 2);
-  CheckWorkerState(2, 4, WorkerState::WORKER_TERMINATED);
-
-  // Re-created -> ClientHostClosing -> Destroyed
-  manager_->WorkerCreated(2, 5, instance2);
-  CheckWorkerNotExist(2, 2);
-  CheckWorkerState(2, 5, WorkerState::WORKER_PAUSED_FOR_REATTACH);
-  EXPECT_EQ(agent_host2.get(), GetDevToolsAgentHostForWorker(2, 5));
-  client_host2->InspectAgentHost(nullptr);
-  CheckWorkerCount(1);
-  agent_host2 = nullptr;
-  CheckWorkerCount(1);
-  manager_->WorkerDestroyed(2, 5);
-  CheckWorkerCount(0);
-}
-
-TEST_F(SharedWorkerDevToolsManagerTest, ReattachTest) {
-  SharedWorkerInstance instance(
-      GURL("http://example.com/w3.js"), std::string(),
-      url::Origin::Create(GURL("http://example.com/")), std::string(),
-      blink::kWebContentSecurityPolicyTypeReport, blink::kWebAddressSpacePublic,
-      browser_context_->GetResourceContext(), partition_id_,
-      blink::mojom::SharedWorkerCreationContextType::kNonsecure,
-      base::UnguessableToken::Create());
-  std::unique_ptr<TestDevToolsClientHost> client_host(
-      new TestDevToolsClientHost());
-  // Created -> GetDevToolsAgentHost -> Register -> Destroyed
-  manager_->WorkerCreated(3, 1, instance);
-  CheckWorkerState(3, 1, WorkerState::WORKER_UNINSPECTED);
-  scoped_refptr<DevToolsAgentHost> agent_host(
-      GetDevToolsAgentHostForWorker(3, 1));
-  EXPECT_TRUE(agent_host.get());
-  CheckWorkerState(3, 1, WorkerState::WORKER_UNINSPECTED);
-  client_host->InspectAgentHost(agent_host.get());
-  CheckWorkerState(3, 1, WorkerState::WORKER_INSPECTED);
-  manager_->WorkerDestroyed(3, 1);
-  CheckWorkerState(3, 1, WorkerState::WORKER_TERMINATED);
-  // ClientHostClosing -> Re-created -> release agent_host -> Destroyed
-  client_host->InspectAgentHost(nullptr);
-  CheckWorkerState(3, 1, WorkerState::WORKER_TERMINATED);
-  manager_->WorkerCreated(3, 2, instance);
-  CheckWorkerState(3, 2, WorkerState::WORKER_UNINSPECTED);
-  agent_host = nullptr;
-  CheckWorkerState(3, 2, WorkerState::WORKER_UNINSPECTED);
-  manager_->WorkerDestroyed(3, 2);
-  CheckWorkerNotExist(3, 2);
-  CheckWorkerCount(0);
-}
-
-TEST_F(SharedWorkerDevToolsManagerTest, PauseOnStartTest) {
-  SharedWorkerInstance instance(
-      GURL("http://example.com/w3.js"), std::string(),
-      url::Origin::Create(GURL("http://example.com/")), std::string(),
-      blink::kWebContentSecurityPolicyTypeReport, blink::kWebAddressSpacePublic,
-      browser_context_->GetResourceContext(), partition_id_,
-      blink::mojom::SharedWorkerCreationContextType::kNonsecure,
-      base::UnguessableToken::Create());
-  std::unique_ptr<TestDevToolsClientHost> client_host(
-      new TestDevToolsClientHost());
-  manager_->WorkerCreated(3, 1, instance);
-  CheckWorkerState(3, 1, WorkerState::WORKER_UNINSPECTED);
-  scoped_refptr<SharedWorkerDevToolsAgentHost> agent_host(
-      GetDevToolsAgentHostForWorker(3, 1));
-  EXPECT_TRUE(agent_host.get());
-  CheckWorkerState(3, 1, WorkerState::WORKER_UNINSPECTED);
-  agent_host->PauseForDebugOnStart();
-  CheckWorkerState(3, 1, WorkerState::WORKER_PAUSED_FOR_DEBUG_ON_START);
-  EXPECT_FALSE(agent_host->IsReadyForInspection());
-  manager_->WorkerReadyForInspection(3, 1);
-  CheckWorkerState(3, 1, WorkerState::WORKER_READY_FOR_DEBUG_ON_START);
-  client_host->InspectAgentHost(agent_host.get());
-  CheckWorkerState(3, 1, WorkerState::WORKER_INSPECTED);
-  client_host->InspectAgentHost(nullptr);
-  agent_host = nullptr;
-  CheckWorkerState(3, 1, WorkerState::WORKER_UNINSPECTED);
-  manager_->WorkerDestroyed(3, 1);
-  CheckWorkerNotExist(3, 1);
-  CheckWorkerCount(0);
-}
-
-}  // namespace content
diff --git a/content/browser/frame_host/frame_navigation_entry.cc b/content/browser/frame_host/frame_navigation_entry.cc
index 2ecfb5d..608c490 100644
--- a/content/browser/frame_host/frame_navigation_entry.cc
+++ b/content/browser/frame_host/frame_navigation_entry.cc
@@ -8,7 +8,6 @@
 
 #include "base/strings/utf_string_conversions.h"
 #include "content/common/page_state_serialization.h"
-#include "content/common/site_isolation_policy.h"
 
 namespace content {
 
diff --git a/content/browser/frame_host/frame_tree.cc b/content/browser/frame_host/frame_tree.cc
index f7ac86c..30f104e 100644
--- a/content/browser/frame_host/frame_tree.cc
+++ b/content/browser/frame_host/frame_tree.cc
@@ -28,7 +28,6 @@
 #include "content/common/content_switches_internal.h"
 #include "content/common/frame_owner_properties.h"
 #include "content/common/input_messages.h"
-#include "content/common/site_isolation_policy.h"
 #include "third_party/WebKit/common/frame_policy.h"
 
 namespace content {
diff --git a/content/browser/frame_host/frame_tree_node.cc b/content/browser/frame_host/frame_tree_node.cc
index 8990d03..c66ae70 100644
--- a/content/browser/frame_host/frame_tree_node.cc
+++ b/content/browser/frame_host/frame_tree_node.cc
@@ -22,7 +22,6 @@
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/common/frame_messages.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/browser_side_navigation_policy.h"
 #include "third_party/WebKit/common/sandbox_flags.h"
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index a5ac096..a289937 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -62,7 +62,6 @@
 #include "content/browser/renderer_host/render_view_host_impl.h"  // Temporary
 #include "content/browser/site_instance_impl.h"
 #include "content/common/frame_messages.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/content_browser_client.h"
@@ -687,7 +686,7 @@
     default:
       NOTREACHED();
       break;
-  };
+  }
 
   // The user initiated a load, we don't need to reload anymore.
   needs_reload_ = false;
@@ -785,7 +784,7 @@
     default:
       NOTREACHED();
       break;
-  };
+  }
 
   entry->set_started_from_context_menu(params.started_from_context_menu);
   LoadEntry(std::move(entry));
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index 69cdeea0..7a68e30 100644
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -29,7 +29,6 @@
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/frame_messages.h"
 #include "content/common/page_state_serialization.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/resource_dispatcher_host.h"
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc
index 7024ee4..c94bb2fd 100644
--- a/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -32,7 +32,6 @@
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/frame_messages.h"
 #include "content/common/frame_owner_properties.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents_delegate.h"
diff --git a/content/browser/frame_host/navigation_entry_impl.cc b/content/browser/frame_host/navigation_entry_impl.cc
index aadd134..20bb33c 100644
--- a/content/browser/frame_host/navigation_entry_impl.cc
+++ b/content/browser/frame_host/navigation_entry_impl.cc
@@ -22,7 +22,6 @@
 #include "content/common/content_constants_internal.h"
 #include "content/common/navigation_params.h"
 #include "content/common/page_state_serialization.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/public/browser/reload_type.h"
 #include "content/public/common/browser_side_navigation_policy.h"
 #include "content/public/common/content_constants.h"
diff --git a/content/browser/frame_host/navigation_handle_impl.cc b/content/browser/frame_host/navigation_handle_impl.cc
index 73afa8b3..b49aa497 100644
--- a/content/browser/frame_host/navigation_handle_impl.cc
+++ b/content/browser/frame_host/navigation_handle_impl.cc
@@ -27,7 +27,6 @@
 #include "content/browser/service_worker/service_worker_navigation_handle.h"
 #include "content/common/child_process_host_impl.h"
 #include "content/common/frame_messages.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/navigation_ui_data.h"
 #include "content/public/browser/site_instance.h"
diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc
index c6f7420..2858d64 100644
--- a/content/browser/frame_host/navigator_impl.cc
+++ b/content/browser/frame_host/navigator_impl.cc
@@ -28,7 +28,6 @@
 #include "content/common/frame_messages.h"
 #include "content/common/navigation_params.h"
 #include "content/common/page_messages.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/content_browser_client.h"
diff --git a/content/browser/frame_host/navigator_impl_unittest.cc b/content/browser/frame_host/navigator_impl_unittest.cc
index 05d8ab6..668680a 100644
--- a/content/browser/frame_host/navigator_impl_unittest.cc
+++ b/content/browser/frame_host/navigator_impl_unittest.cc
@@ -20,7 +20,6 @@
 #include "content/common/frame.mojom.h"
 #include "content/common/frame_messages.h"
 #include "content/common/navigation_params.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/public/browser/navigation_data.h"
 #include "content/public/browser/stream_handle.h"
 #include "content/public/common/content_features.h"
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 5202167e..bfd01a7 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -95,7 +95,6 @@
 #include "content/common/navigation_subresource_loader_params.h"
 #include "content/common/render_message_filter.mojom.h"
 #include "content/common/renderer.mojom.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/swapped_out_messages.h"
 #include "content/common/url_loader_factory_bundle.mojom.h"
 #include "content/common/widget.mojom.h"
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index 53b0f5b..99b3b01a 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -35,10 +35,10 @@
 #include "content/browser/renderer_host/render_view_host_factory.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/browser/site_instance_impl.h"
+#include "content/browser/site_isolation_policy.h"
 #include "content/browser/webui/web_ui_controller_factory_registry.h"
 #include "content/common/frame_messages.h"
 #include "content/common/frame_owner_properties.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/content_browser_client.h"
diff --git a/content/browser/frame_host/render_frame_host_manager_browsertest.cc b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
index a827e02..e8028dcf 100644
--- a/content/browser/frame_host/render_frame_host_manager_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
@@ -30,7 +30,6 @@
 #include "content/browser/webui/web_ui_impl.h"
 #include "content/common/content_constants_internal.h"
 #include "content/common/input_messages.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/render_process_host.h"
diff --git a/content/browser/frame_host/render_frame_host_manager_unittest.cc b/content/browser/frame_host/render_frame_host_manager_unittest.cc
index 334e84d..c38b650 100644
--- a/content/browser/frame_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc
@@ -31,7 +31,6 @@
 #include "content/common/frame_messages.h"
 #include "content/common/frame_owner_properties.h"
 #include "content/common/input_messages.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_service.h"
diff --git a/content/browser/frame_host/render_widget_host_view_guest.cc b/content/browser/frame_host/render_widget_host_view_guest.cc
index cd752be..6b134951 100644
--- a/content/browser/frame_host/render_widget_host_view_guest.cc
+++ b/content/browser/frame_host/render_widget_host_view_guest.cc
@@ -28,7 +28,6 @@
 #include "content/common/content_switches_internal.h"
 #include "content/common/frame_messages.h"
 #include "content/common/input/web_touch_event_traits.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/view_messages.h"
 #include "content/public/common/content_switches.h"
 #include "gpu/ipc/common/gpu_messages.h"
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index 0475d83..336cd11 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -68,13 +68,13 @@
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/service_worker/service_worker_navigation_handle_core.h"
 #include "content/browser/service_worker/service_worker_request_handler.h"
+#include "content/browser/site_isolation_policy.h"
 #include "content/browser/streams/stream.h"
 #include "content/browser/streams/stream_context.h"
 #include "content/browser/streams/stream_registry.h"
 #include "content/common/loader_util.h"
 #include "content/common/net/url_request_service_worker_data.h"
 #include "content/common/resource_messages.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/browser_child_process_host.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index f8c24b4..4107667 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -132,6 +132,7 @@
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/service_worker/service_worker_dispatcher_host.h"
 #include "content/browser/site_instance_impl.h"
+#include "content/browser/site_isolation_policy.h"
 #include "content/browser/speech/speech_recognition_dispatcher_host.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/browser/streams/stream_context.h"
@@ -146,7 +147,6 @@
 #include "content/common/resource_messages.h"
 #include "content/common/service_manager/child_connection.h"
 #include "content/common/service_manager/service_manager_connection_impl.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index bf19ad4..b83591e 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -50,7 +50,6 @@
 #include "content/common/inter_process_time_ticks_converter.h"
 #include "content/common/render_message_filter.mojom.h"
 #include "content/common/renderer.mojom.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/speech_recognition_messages.h"
 #include "content/common/swapped_out_messages.h"
 #include "content/common/view_messages.h"
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index b12edad..439e96d1 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -67,7 +67,6 @@
 #include "content/common/content_switches_internal.h"
 #include "content/common/gpu_stream_constants.h"
 #include "content/common/input_messages.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/android/compositor.h"
 #include "content/public/browser/android/synchronous_compositor_client.h"
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 3adf96ed..89040091 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -50,7 +50,6 @@
 #include "content/common/content_switches_internal.h"
 #include "content/common/input_messages.h"
 #include "content/common/render_widget_window_tree_client_factory.mojom.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/text_input_state.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/content_browser_client.h"
diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.cc b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
index 50f0b90..d4b41dd4e 100644
--- a/content/browser/renderer_host/render_widget_host_view_event_handler.cc
+++ b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
@@ -15,7 +15,6 @@
 #include "content/browser/renderer_host/render_widget_host_view_base.h"
 #include "content/browser/renderer_host/text_input_manager.h"
 #include "content/common/content_switches_internal.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/common/content_features.h"
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index d080dd0..dcc489c 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -56,7 +56,6 @@
 #include "content/common/accessibility_messages.h"
 #include "content/common/edit_command.h"
 #include "content/common/input_messages.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/text_input_state.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/browser_context.h"
diff --git a/content/browser/shared_worker/shared_worker_host.cc b/content/browser/shared_worker/shared_worker_host.cc
index 6b09861..5d0f4580 100644
--- a/content/browser/shared_worker/shared_worker_host.cc
+++ b/content/browser/shared_worker/shared_worker_host.cc
@@ -70,10 +70,8 @@
 SharedWorkerHost::~SharedWorkerHost() {
   UMA_HISTOGRAM_LONG_TIMES("SharedWorker.TimeToDeleted",
                            base::TimeTicks::Now() - creation_time_);
-  if (!closed_ && !termination_message_sent_) {
-    SharedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(process_id_,
-                                                                route_id_);
-  }
+  if (!closed_ && !termination_message_sent_)
+    SharedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(this);
 }
 
 void SharedWorkerHost::Start(mojom::SharedWorkerFactoryPtr factory,
@@ -127,11 +125,12 @@
 }
 
 void SharedWorkerHost::TerminateWorker() {
+  // This can be called twice in tests while cleaning up all the workers.
+  if (termination_message_sent_)
+    return;
   termination_message_sent_ = true;
-  if (!closed_) {
-    SharedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(process_id_,
-                                                                route_id_);
-  }
+  if (!closed_)
+    SharedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(this);
   worker_->Terminate();
   // Now, we wait to observe OnWorkerConnectionLost.
 }
@@ -164,15 +163,13 @@
   // being sent to the worker (messages can still be sent from the worker,
   // for exception reporting, etc).
   closed_ = true;
-  if (!termination_message_sent_) {
-    SharedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(process_id_,
-                                                                route_id_);
-  }
+  if (!termination_message_sent_)
+    SharedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(this);
 }
 
 void SharedWorkerHost::OnReadyForInspection() {
-  SharedWorkerDevToolsManager::GetInstance()->WorkerReadyForInspection(
-      process_id_, route_id_);
+  if (!closed_ && !termination_message_sent_)
+    SharedWorkerDevToolsManager::GetInstance()->WorkerReadyForInspection(this);
 }
 
 void SharedWorkerHost::OnScriptLoaded() {
diff --git a/content/browser/shared_worker/shared_worker_service_impl.cc b/content/browser/shared_worker/shared_worker_service_impl.cc
index ecf29e78..5ed5c0b 100644
--- a/content/browser/shared_worker/shared_worker_service_impl.cc
+++ b/content/browser/shared_worker/shared_worker_service_impl.cc
@@ -87,15 +87,6 @@
   return false;
 }
 
-bool SharedWorkerServiceImpl::TerminateWorkerById(int process_id,
-                                                  int route_id) {
-  SharedWorkerHost* host = FindSharedWorkerHost(process_id, route_id);
-  if (!host || !host->instance())
-    return false;
-  host->TerminateWorker();
-  return true;
-}
-
 void SharedWorkerServiceImpl::TerminateAllWorkersForTesting(
     base::OnceClosure callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -210,13 +201,12 @@
   // Dev Tools will need to be modified to use something else as an identifier.
   int worker_route_id = process_host->GetNextRoutingID();
 
-  bool pause_on_start =
-      SharedWorkerDevToolsManager::GetInstance()->WorkerCreated(
-          worker_process_id, worker_route_id, *instance);
-
   auto host = std::make_unique<SharedWorkerHost>(
       std::move(instance), worker_process_id, worker_route_id);
 
+  bool pause_on_start =
+      SharedWorkerDevToolsManager::GetInstance()->WorkerCreated(host.get());
+
   // Get the factory used to instantiate the new shared worker instance in
   // the target process.
   mojom::SharedWorkerFactoryPtr factory;
diff --git a/content/browser/shared_worker/shared_worker_service_impl.h b/content/browser/shared_worker/shared_worker_service_impl.h
index ce7d66db..e634b61 100644
--- a/content/browser/shared_worker/shared_worker_service_impl.h
+++ b/content/browser/shared_worker/shared_worker_service_impl.h
@@ -40,8 +40,6 @@
                        StoragePartition* storage_partition,
                        ResourceContext* resource_context) override;
 
-  // Terminates the given worker. Returns true if the process was found.
-  bool TerminateWorkerById(int process_id, int route_id);
   void TerminateAllWorkersForTesting(base::OnceClosure callback);
 
   // Creates the worker if necessary or connects to an already existing worker.
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc
index 827e674c..75aee7d 100644
--- a/content/browser/site_instance_impl.cc
+++ b/content/browser/site_instance_impl.cc
@@ -13,8 +13,8 @@
 #include "content/browser/frame_host/debug_urls.h"
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
+#include "content/browser/site_isolation_policy.h"
 #include "content/browser/storage_partition_impl.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/render_process_host_factory.h"
 #include "content/public/browser/web_ui_controller_factory.h"
diff --git a/content/common/site_isolation_policy.cc b/content/browser/site_isolation_policy.cc
similarity index 75%
rename from content/common/site_isolation_policy.cc
rename to content/browser/site_isolation_policy.cc
index 0d277b0..eec976c1 100644
--- a/content/common/site_isolation_policy.cc
+++ b/content/browser/site_isolation_policy.cc
@@ -2,14 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/common/site_isolation_policy.h"
+#include "content/browser/site_isolation_policy.h"
 
+#include <algorithm>
+#include <iterator>
 #include <string>
+#include <utility>
 
 #include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/strings/string_split.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/common/content_client.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "url/gurl.h"
@@ -40,7 +45,8 @@
 }
 
 // static
-std::vector<url::Origin> SiteIsolationPolicy::GetIsolatedOrigins() {
+std::vector<url::Origin>
+SiteIsolationPolicy::GetIsolatedOriginsFromEnvironment() {
   std::string cmdline_arg =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
           switches::kIsolateOrigins);
@@ -58,6 +64,20 @@
 }
 
 // static
+std::vector<url::Origin> SiteIsolationPolicy::GetIsolatedOrigins() {
+  std::vector<url::Origin> from_environment =
+      GetIsolatedOriginsFromEnvironment();
+  std::vector<url::Origin> from_embedder =
+      GetContentClient()->browser()->GetOriginsRequiringDedicatedProcess();
+
+  std::vector<url::Origin> result = std::move(from_environment);
+  result.reserve(result.size() + from_embedder.size());
+  std::move(from_embedder.begin(), from_embedder.end(),
+            std::back_inserter(result));
+  return result;
+}
+
+// static
 std::vector<url::Origin> SiteIsolationPolicy::ParseIsolatedOrigins(
     base::StringPiece arg) {
   std::vector<base::StringPiece> origin_strings = base::SplitStringPiece(
diff --git a/content/common/site_isolation_policy.h b/content/browser/site_isolation_policy.h
similarity index 82%
rename from content/common/site_isolation_policy.h
rename to content/browser/site_isolation_policy.h
index 44afe7d..5662adf 100644
--- a/content/common/site_isolation_policy.h
+++ b/content/browser/site_isolation_policy.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 CONTENT_COMMON_SITE_ISOLATION_POLICY_H_
-#define CONTENT_COMMON_SITE_ISOLATION_POLICY_H_
+#ifndef CONTENT_BROWSER_SITE_ISOLATION_POLICY_H_
+#define CONTENT_BROWSER_SITE_ISOLATION_POLICY_H_
 
 #include <vector>
 
@@ -36,19 +36,21 @@
 
   // Returns the origins to isolate.  See also AreIsolatedOriginsEnabled.
   // This list applies globally to the whole browser in all profiles.
-  // TODO(lukasza): Make sure this list also includes the origins returned by
-  // ContentBrowserClient::GetOriginsRequiringDedicatedProcess.
   static std::vector<url::Origin> GetIsolatedOrigins();
 
  private:
   SiteIsolationPolicy();  // Not instantiable.
 
-  FRIEND_TEST_ALL_PREFIXES(SiteIsolationPolicyTest, ParseIsolatedOrigins);
+  // Parses |arg| into a list of origins.
   static std::vector<url::Origin> ParseIsolatedOrigins(base::StringPiece arg);
+  FRIEND_TEST_ALL_PREFIXES(SiteIsolationPolicyTest, ParseIsolatedOrigins);
+
+  // Gets isolated origins from cmdline and/or from field trial param.
+  static std::vector<url::Origin> GetIsolatedOriginsFromEnvironment();
 
   DISALLOW_COPY_AND_ASSIGN(SiteIsolationPolicy);
 };
 
 }  // namespace content
 
-#endif  // CONTENT_COMMON_SITE_ISOLATION_POLICY_H_
+#endif  // CONTENT_BROWSER_SITE_ISOLATION_POLICY_H_
diff --git a/content/common/site_isolation_policy_unittest.cc b/content/browser/site_isolation_policy_unittest.cc
similarity index 97%
rename from content/common/site_isolation_policy_unittest.cc
rename to content/browser/site_isolation_policy_unittest.cc
index b3935e6..a60ec5d 100644
--- a/content/common/site_isolation_policy_unittest.cc
+++ b/content/browser/site_isolation_policy_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/common/site_isolation_policy.h"
+#include "content/browser/site_isolation_policy.h"
 
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index f22cf700..e9ce6b0 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -86,7 +86,6 @@
 #include "content/common/page_messages.h"
 #include "content/common/page_state_serialization.h"
 #include "content/common/render_message_filter.mojom.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/ax_event_notification_details.h"
 #include "content/public/browser/browser_context.h"
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index 3ad3a9f..ac45c08d 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -16,7 +16,6 @@
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/browser/web_contents/web_contents_view.h"
 #include "content/common/frame_messages.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/public/browser/javascript_dialog_manager.h"
 #include "content/public/browser/load_notification_details.h"
 #include "content/public/browser/navigation_controller.h"
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index 183999c9..0cc629a 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -24,7 +24,6 @@
 #include "content/common/frame_messages.h"
 #include "content/common/input/synthetic_web_input_event_builders.h"
 #include "content/common/media/media_player_delegate_messages.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/download_url_parameters.h"
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 29cc2d2..56586fc0 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -292,8 +292,6 @@
     "service_worker/service_worker_types.h",
     "service_worker/service_worker_utils.cc",
     "service_worker/service_worker_utils.h",
-    "site_isolation_policy.cc",
-    "site_isolation_policy.h",
     "speech_recognition_messages.h",
     "swapped_out_messages.cc",
     "swapped_out_messages.h",
diff --git a/content/public/test/test_utils.cc b/content/public/test/test_utils.cc
index 2784313..6efe48c 100644
--- a/content/public/test/test_utils.cc
+++ b/content/public/test/test_utils.cc
@@ -21,7 +21,6 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "components/variations/variations_params_manager.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/url_schemes.h"
 #include "content/public/browser/browser_child_process_host_iterator.h"
 #include "content/public/browser/notification_service.h"
diff --git a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
index a9bdd34..4da5dec 100644
--- a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
+++ b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
@@ -10,7 +10,6 @@
 #include "build/build_config.h"
 #include "content/common/accessibility_messages.h"
 #include "content/common/frame_messages.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/view_message_enums.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/render_view_test.h"
diff --git a/content/renderer/device_sensors/device_orientation_event_pump.cc b/content/renderer/device_sensors/device_orientation_event_pump.cc
index 0905e17..22a1428 100644
--- a/content/renderer/device_sensors/device_orientation_event_pump.cc
+++ b/content/renderer/device_sensors/device_orientation_event_pump.cc
@@ -137,7 +137,7 @@
 
 void DeviceOrientationEventPump::DidStartIfPossible() {
   if (!absolute_ && !relative_orientation_sensor_.sensor &&
-      fall_back_to_absolute_orientation_sensor_) {
+      fall_back_to_absolute_orientation_sensor_ && sensor_provider_) {
     // When relative orientation sensor is not available fall back to using
     // the absolute orientation sensor but only on the first failure.
     fall_back_to_absolute_orientation_sensor_ = false;
diff --git a/content/renderer/media/user_media_processor.cc b/content/renderer/media/user_media_processor.cc
index d5c65a2..7faa066 100644
--- a/content/renderer/media/user_media_processor.cc
+++ b/content/renderer/media/user_media_processor.cc
@@ -456,14 +456,7 @@
       GetUserMediaRequestFailed(result, failed_constraint_name);
       return;
     }
-    base::PostTaskAndReplyWithResult(
-        worker_task_runner_.get(), FROM_HERE,
-        base::Bind(&SelectSettingsVideoContentCapture,
-                   current_request_info_->web_request().VideoConstraints(),
-                   video_controls.stream_source),
-        base::Bind(&UserMediaProcessor::FinalizeSelectVideoContentSettings,
-                   weak_factory_.GetWeakPtr(),
-                   current_request_info_->web_request()));
+    SelectVideoContentSettings();
   }
 }
 
@@ -513,13 +506,12 @@
   GenerateStreamForCurrentRequestInfo();
 }
 
-void UserMediaProcessor::FinalizeSelectVideoContentSettings(
-    const blink::WebUserMediaRequest& web_request,
-    const VideoCaptureSettings& settings) {
+void UserMediaProcessor::SelectVideoContentSettings() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!IsCurrentRequestInfo(web_request))
-    return;
-
+  DCHECK(current_request_info_);
+  VideoCaptureSettings settings = SelectSettingsVideoContentCapture(
+      current_request_info_->web_request().VideoConstraints(),
+      current_request_info_->stream_controls()->video.stream_source);
   if (!settings.HasValue()) {
     blink::WebString failed_constraint_name =
         blink::WebString::FromASCII(settings.failed_constraint_name());
diff --git a/content/renderer/media/user_media_processor.h b/content/renderer/media/user_media_processor.h
index 99f844b..743b9333 100644
--- a/content/renderer/media/user_media_processor.h
+++ b/content/renderer/media/user_media_processor.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/callback_forward.h"
@@ -255,9 +256,7 @@
   void FinalizeSelectVideoDeviceSettings(
       const blink::WebUserMediaRequest& web_request,
       const VideoCaptureSettings& settings);
-  void FinalizeSelectVideoContentSettings(
-      const blink::WebUserMediaRequest& web_request,
-      const VideoCaptureSettings& settings);
+  void SelectVideoContentSettings();
 
   void GenerateStreamForCurrentRequestInfo();
 
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 609dd65..d7dfa63 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -61,7 +61,6 @@
 #include "content/common/savable_subframe.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/common/service_worker/service_worker_utils.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/swapped_out_messages.h"
 #include "content/common/view_messages.h"
 #include "content/public/common/appcache_info.h"
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index 6d74f5d7..d451dac 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -17,7 +17,6 @@
 #include "content/common/frame_replication_state.h"
 #include "content/common/input_messages.h"
 #include "content/common/page_messages.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/swapped_out_messages.h"
 #include "content/common/view_messages.h"
 #include "content/public/common/content_switches.h"
@@ -715,4 +714,4 @@
 }
 #endif
 
-}  // namespace
+}  // namespace content
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index ee79aaf2..e6f8670 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -69,7 +69,6 @@
 #include "content/common/frame_owner_properties.h"
 #include "content/common/gpu_stream_constants.h"
 #include "content/common/resource_messages.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/view_messages.h"
 #include "content/public/common/content_constants.h"
 #include "content/public/common/content_features.h"
@@ -443,7 +442,8 @@
 // thread.
 class UkmRecorderFactoryImpl : public cc::UkmRecorderFactory {
  public:
-  UkmRecorderFactoryImpl(std::unique_ptr<service_manager::Connector> connector)
+  explicit UkmRecorderFactoryImpl(
+      std::unique_ptr<service_manager::Connector> connector)
       : connector_(std::move(connector)) {
     DCHECK(connector_);
   }
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index f02e349..a82a9b3 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -27,7 +27,6 @@
 #include "content/common/frame_owner_properties.h"
 #include "content/common/frame_replication_state.h"
 #include "content/common/renderer.mojom.h"
-#include "content/common/site_isolation_policy.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/native_web_keyboard_event.h"
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 9c2f2d4..e5e29608 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1203,7 +1203,6 @@
     "../browser/devtools/devtools_http_handler_unittest.cc",
     "../browser/devtools/devtools_manager_unittest.cc",
     "../browser/devtools/protocol/tracing_handler_unittest.cc",
-    "../browser/devtools/shared_worker_devtools_manager_unittest.cc",
     "../browser/dom_storage/dom_storage_area_unittest.cc",
     "../browser/dom_storage/dom_storage_context_impl_unittest.cc",
     "../browser/dom_storage/dom_storage_database_unittest.cc",
@@ -1426,6 +1425,7 @@
     "../browser/shared_worker/shared_worker_instance_unittest.cc",
     "../browser/shared_worker/shared_worker_service_impl_unittest.cc",
     "../browser/site_instance_impl_unittest.cc",
+    "../browser/site_isolation_policy_unittest.cc",
     "../browser/startup_task_runner_unittest.cc",
     "../browser/storage_partition_impl_map_unittest.cc",
     "../browser/storage_partition_impl_unittest.cc",
@@ -1491,7 +1491,6 @@
     "../common/service_manager/service_manager_connection_impl_unittest.cc",
     "../common/service_worker/service_worker_types_unittest.cc",
     "../common/service_worker/service_worker_utils_unittest.cc",
-    "../common/site_isolation_policy_unittest.cc",
     "../common/throttling_url_loader_unittest.cc",
     "../common/unique_name_helper_unittest.cc",
     "../common/webplugininfo_unittest.cc",
diff --git a/ios/web/web_state/web_state_impl.h b/ios/web/web_state/web_state_impl.h
index da10e00a..07eed9a 100644
--- a/ios/web/web_state/web_state_impl.h
+++ b/ios/web/web_state/web_state_impl.h
@@ -79,7 +79,9 @@
   // Notifies the observers that a navigation has started.
   void OnNavigationStarted(web::NavigationContext* context);
 
-  // Notifies the observers that a navigation has finished.
+  // Notifies the observers that a navigation has finished. For same-document
+  // navigations notifies the observers about favicon URLs update using
+  // candidates received in OnFaviconUrlUpdated.
   void OnNavigationFinished(web::NavigationContext* context);
 
   // Called when page title was changed.
@@ -378,6 +380,12 @@
   // history into WKWebView when web usage is re-enabled.
   base::scoped_nsobject<CRWSessionStorage> cached_session_storage_;
 
+  // Favicons URLs received in OnFaviconUrlUpdated.
+  // WebStateObserver:FaviconUrlUpdated must be called for same-document
+  // navigations, so this cache will be used to avoid running expensive favicon
+  // fetching JavaScript.
+  std::vector<web::FaviconURL> cached_favicon_urls_;
+
   DISALLOW_COPY_AND_ASSIGN(WebStateImpl);
 };
 
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm
index 178893d..3fed0c3 100644
--- a/ios/web/web_state/web_state_impl.mm
+++ b/ios/web/web_state/web_state_impl.mm
@@ -261,6 +261,7 @@
 
 void WebStateImpl::OnFaviconUrlUpdated(
     const std::vector<FaviconURL>& candidates) {
+  cached_favicon_urls_ = candidates;
   for (auto& observer : observers_)
     observer.FaviconUrlUpdated(this, candidates);
 }
@@ -736,6 +737,19 @@
 void WebStateImpl::OnNavigationFinished(web::NavigationContext* context) {
   for (auto& observer : observers_)
     observer.DidFinishNavigation(this, context);
+
+  // Update cached_favicon_urls_.
+  if (!context->IsSameDocument()) {
+    // Favicons are not valid after document change. Favicon URLs will be
+    // refetched by CRWWebController and passed to OnFaviconUrlUpdated.
+    cached_favicon_urls_.clear();
+  } else if (!cached_favicon_urls_.empty()) {
+    // For same-document navigations favicon urls will not be refetched and
+    // WebStateObserver:FaviconUrlUpdated must use the cached results.
+    for (auto& observer : observers_) {
+      observer.FaviconUrlUpdated(this, cached_favicon_urls_);
+    }
+  }
 }
 
 #pragma mark - NavigationManagerDelegate implementation
diff --git a/ios/web/web_state/web_state_impl_unittest.mm b/ios/web/web_state/web_state_impl_unittest.mm
index d75b2fd..61324ab0 100644
--- a/ios/web/web_state/web_state_impl_unittest.mm
+++ b/ios/web/web_state/web_state_impl_unittest.mm
@@ -18,6 +18,7 @@
 #import "base/test/ios/wait_util.h"
 #import "ios/web/public/java_script_dialog_presenter.h"
 #include "ios/web/public/load_committed_details.h"
+#import "ios/web/public/test/fakes/fake_navigation_context.h"
 #include "ios/web/public/test/fakes/test_browser_state.h"
 #import "ios/web/public/test/fakes/test_web_state_delegate.h"
 #import "ios/web/public/test/fakes/test_web_state_observer.h"
@@ -772,4 +773,53 @@
   EXPECT_TRUE(web_state_with_opener->HasOpener());
 }
 
+// Tests that WebStateObserver::FaviconUrlUpdated is called for same-document
+// navigations.
+TEST_F(WebStateImplTest, FaviconUpdateForSameDocumentNavigations) {
+  auto observer = std::make_unique<TestWebStateObserver>(web_state_.get());
+
+  // No callback if icons has not been fetched yet.
+  FakeNavigationContext context;
+  context.SetIsSameDocument(true);
+  web_state_->OnNavigationFinished(&context);
+  EXPECT_FALSE(observer->update_favicon_url_candidates_info());
+
+  // Callback is called when icons were fetched.
+  observer = base::MakeUnique<TestWebStateObserver>(web_state_.get());
+  web::FaviconURL favicon_url(GURL("https://chromium.test/"),
+                              web::FaviconURL::IconType::kTouchIcon,
+                              {gfx::Size(5, 6)});
+  web_state_->OnFaviconUrlUpdated({favicon_url});
+  EXPECT_TRUE(observer->update_favicon_url_candidates_info());
+
+  // Callback is now called after same-document navigation.
+  observer = std::make_unique<TestWebStateObserver>(web_state_.get());
+  web_state_->OnNavigationFinished(&context);
+  ASSERT_TRUE(observer->update_favicon_url_candidates_info());
+  ASSERT_EQ(1U,
+            observer->update_favicon_url_candidates_info()->candidates.size());
+  const web::FaviconURL& actual_favicon_url =
+      observer->update_favicon_url_candidates_info()->candidates[0];
+  EXPECT_EQ(favicon_url.icon_url, actual_favicon_url.icon_url);
+  EXPECT_EQ(favicon_url.icon_type, actual_favicon_url.icon_type);
+  ASSERT_EQ(favicon_url.icon_sizes.size(),
+            actual_favicon_url.icon_sizes.size());
+  EXPECT_EQ(favicon_url.icon_sizes[0].width(),
+            actual_favicon_url.icon_sizes[0].width());
+  EXPECT_EQ(favicon_url.icon_sizes[0].height(),
+            actual_favicon_url.icon_sizes[0].height());
+
+  // Document change navigation does not call callback.
+  observer = std::make_unique<TestWebStateObserver>(web_state_.get());
+  context.SetIsSameDocument(false);
+  web_state_->OnNavigationFinished(&context);
+  EXPECT_FALSE(observer->update_favicon_url_candidates_info());
+
+  // Previous candidates were invalidated by the document change. No callback
+  // if icons has not been fetched yet.
+  context.SetIsSameDocument(true);
+  web_state_->OnNavigationFinished(&context);
+  EXPECT_FALSE(observer->update_favicon_url_candidates_info());
+}
+
 }  // namespace web
diff --git a/media/gpu/vaapi/vaapi_tfp_picture.cc b/media/gpu/vaapi/vaapi_tfp_picture.cc
index e9eecce..bd7823c 100644
--- a/media/gpu/vaapi/vaapi_tfp_picture.cc
+++ b/media/gpu/vaapi/vaapi_tfp_picture.cc
@@ -72,7 +72,8 @@
 bool VaapiTFPPicture::Allocate(gfx::BufferFormat format) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (format != gfx::BufferFormat::BGRX_8888 &&
-      format != gfx::BufferFormat::BGRA_8888) {
+      format != gfx::BufferFormat::BGRA_8888 &&
+      format != gfx::BufferFormat::RGBX_8888) {
     DLOG(ERROR) << "Unsupported format";
     return false;
   }
diff --git a/pdf/timer.cc b/pdf/timer.cc
index d07e2e9..cbd11a0 100644
--- a/pdf/timer.cc
+++ b/pdf/timer.cc
@@ -9,8 +9,7 @@
 
 namespace chrome_pdf {
 
-Timer::Timer(int delay_in_milliseconds)
-    : delay_(delay_in_milliseconds), callback_factory_(this) {
+Timer::Timer(base::TimeDelta delay) : delay_(delay), callback_factory_(this) {
   PostCallback();
 }
 
@@ -19,7 +18,8 @@
 void Timer::PostCallback() {
   pp::CompletionCallback callback =
       callback_factory_.NewCallback(&Timer::TimerProc);
-  pp::Module::Get()->core()->CallOnMainThread(delay_, callback, 0);
+  pp::Module::Get()->core()->CallOnMainThread(delay_.InMilliseconds(), callback,
+                                              0);
 }
 
 void Timer::TimerProc(int32_t /*result*/) {
diff --git a/pdf/timer.h b/pdf/timer.h
index a27f78f3..cff2b88 100644
--- a/pdf/timer.h
+++ b/pdf/timer.h
@@ -6,6 +6,7 @@
 #define PDF_TIMER_H_
 
 #include "base/macros.h"
+#include "base/time/time.h"
 #include "ppapi/utility/completion_callback_factory.h"
 
 namespace chrome_pdf {
@@ -15,7 +16,7 @@
 // base::MessageLoop, on which it is based.
 class Timer {
  public:
-  explicit Timer(int delay_in_milliseconds);
+  explicit Timer(base::TimeDelta delay);
   virtual ~Timer();
 
   virtual void OnTimer() = 0;
@@ -24,7 +25,7 @@
   void PostCallback();
   void TimerProc(int32_t result);
 
-  int delay_;
+  const base::TimeDelta delay_;
   pp::CompletionCallbackFactory<Timer> callback_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(Timer);
diff --git a/pdf/url_loader_wrapper_impl.cc b/pdf/url_loader_wrapper_impl.cc
index 0e3a066..6d8623b 100644
--- a/pdf/url_loader_wrapper_impl.cc
+++ b/pdf/url_loader_wrapper_impl.cc
@@ -19,8 +19,9 @@
 namespace chrome_pdf {
 
 namespace {
+
 // We should read with delay to prevent block UI thread, and reduce CPU usage.
-const int kReadDelayMs = 2;
+constexpr base::TimeDelta kReadDelayMs = base::TimeDelta::FromMilliseconds(2);
 
 pp::URLRequestInfo MakeRangeRequest(pp::Instance* plugin_instance,
                                     const std::string& url,
diff --git a/services/service_manager/sandbox/linux/bpf_broker_policy_linux.cc b/services/service_manager/sandbox/linux/bpf_broker_policy_linux.cc
index 9b368b43..86ce53a 100644
--- a/services/service_manager/sandbox/linux/bpf_broker_policy_linux.cc
+++ b/services/service_manager/sandbox/linux/bpf_broker_policy_linux.cc
@@ -21,12 +21,12 @@
 #if defined(__NR_access)
     case __NR_access:
 #endif
-#if defined(__NR_open)
-    case __NR_open:
-#endif
 #if defined(__NR_faccessat)
     case __NR_faccessat:
 #endif
+#if defined(__NR_open)
+    case __NR_open:
+#endif
 #if defined(__NR_openat)
     case __NR_openat:
 #endif
@@ -48,6 +48,12 @@
 #if defined(__NR_newfstatat)
     case __NR_newfstatat:
 #endif
+#if defined(__NR_readlink)
+    case __NR_readlink:
+#endif
+#if defined(__NR_readlinkat)
+    case __NR_readlinkat:
+#endif
       return Allow();
     default:
       return BPFBasePolicy::EvaluateSyscall(sysno);
diff --git a/services/service_manager/sandbox/linux/bpf_network_policy_linux.cc b/services/service_manager/sandbox/linux/bpf_network_policy_linux.cc
index ef1d5983..af5a894 100644
--- a/services/service_manager/sandbox/linux/bpf_network_policy_linux.cc
+++ b/services/service_manager/sandbox/linux/bpf_network_policy_linux.cc
@@ -62,6 +62,12 @@
 #if defined(__NR_rename)
     case __NR_rename:
 #endif
+#if defined(__NR_readlink)
+    case __NR_readlink:
+#endif
+#if defined(__NR_readlinkat)
+    case __NR_readlinkat:
+#endif
       return Trap(BrokerProcess::SIGSYS_Handler,
                   SandboxLinux::GetInstance()->broker_process());
     default:
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index 7a501da7..a83d191 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -231968,7 +231968,7 @@
    "support"
   ],
   "common/media.js": [
-   "a575b8135e6ddb1501fc2a082d7544ee86011b7d",
+   "33c78c9c056dc4879c2503075e6cedbf2211b6d2",
    "support"
   ],
   "common/media.js.headers": [
@@ -298256,7 +298256,7 @@
    "support"
   ],
   "feature-policy/README.md": [
-   "711c323f1690b0dbe780461241ad825cdd3cf274",
+   "67e317403163eb2f7b4d9599d21da97635fc14cf",
    "support"
   ],
   "feature-policy/payment-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html": [
@@ -300592,7 +300592,7 @@
    "testharness"
   ],
   "html/browsers/browsing-the-web/read-media/pageload-video.html": [
-   "2d8749e1d5f585ba60ce0a20367d116d126df475",
+   "2ae6e21db438a657afd934bb8fb8a21de5f5f2cf",
    "testharness"
   ],
   "html/browsers/browsing-the-web/read-multipart-x-mixed-replace/.gitkeep": [
@@ -328772,7 +328772,7 @@
    "support"
   ],
   "preload/modulepreload.html": [
-   "89d25610cb5cab74248ee78bdf5940315b0f7f70",
+   "02861e80c504a4d8de8474cb6620c10849c2a329",
    "testharness"
   ],
   "preload/onerror-event.html": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/common/media.js b/third_party/WebKit/LayoutTests/external/wpt/common/media.js
index 6bddea5..7cea1ac 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/common/media.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/common/media.js
@@ -33,3 +33,14 @@
 
     return base + extension;
 }
+
+function getMediaContentType(url) {
+    var extension = new URL(url, location).pathname.split(".").pop();
+    var map = {
+        "mp4": "video/mp4",
+        "ogv": "video/ogg",
+        "mp3": "audio/mp3",
+        "oga": "audio/ogg",
+    };
+    return map[extension];
+}
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/read-media/pageload-video.html b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/read-media/pageload-video.html
index 69ef741..1ae414ce 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/read-media/pageload-video.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/read-media/pageload-video.html
@@ -1,30 +1,27 @@
 <!DOCTYPE HTML>
-<html>
-<head>
-  <title>Media documents: video</title>
-  <link rel="author" title="Michael Ventnor" href="mailto:mventnor@mozilla.com">
-  <link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
-  <link rel="help" href="https://html.spec.whatwg.org/multipage/#read-media">
-  <script src="/resources/testharness.js"></script>
-  <script src="/resources/testharnessreport.js"></script>
-
+<meta charset="utf-8">
+<title>Media documents: video</title>
+<link rel="author" title="Michael Ventnor" href="mailto:mventnor@mozilla.com">
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#read-media">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/media.js"></script>
+<div id="log"></div>
 <script>
-  var t = async_test("The document for a standalone media file should have one child in the body.");
-
-  function frameLoaded() {
-    var testframe = document.getElementById('testframe');
-    assert_equals(testframe.contentDocument.contentType, "video/webm");
+async_test(function() {
+  var testframe = document.createElement('iframe');
+  var url = getVideoURI("/media/A4");
+  var contentType = getMediaContentType(url);
+  testframe.onload = this.step_func_done(function() {
+    assert_equals(testframe.contentDocument.contentType, contentType);
     var testframeChildren = testframe.contentDocument.body.childNodes;
     assert_equals(testframeChildren.length, 1, "Body of image document has 1 child");
     assert_equals(testframeChildren[0].nodeName, "VIDEO", "Only child of body must be an <video> element");
     assert_equals(testframeChildren[0].namespaceURI, "http://www.w3.org/1999/xhtml",
                   "Only child of body must be an HTML element");
-    t.done();
-  }
+  });
+  testframe.src = url;
+  document.body.appendChild(testframe);
+}, "The document for a standalone media file should have one child in the body.");
 </script>
-</head>
-<body>
-  <div id="log"></div>
-  <iframe id="testframe" onload="t.step(frameLoaded)" src="/media/white.webm"></iframe>
-</body>
-</html>
diff --git a/third_party/WebKit/Source/OWNERS b/third_party/WebKit/Source/OWNERS
new file mode 100644
index 0000000..99a70f4
--- /dev/null
+++ b/third_party/WebKit/Source/OWNERS
@@ -0,0 +1,3 @@
+per-file DEPS=dcheng@chromium.org
+per-file DEPS=haraken@chromium.org
+per-file DEPS=jbroman@chromium.org
diff --git a/third_party/WebKit/Source/core/dom/ClassicPendingScript.cpp b/third_party/WebKit/Source/core/dom/ClassicPendingScript.cpp
index ef81067..7be44c8 100644
--- a/third_party/WebKit/Source/core/dom/ClassicPendingScript.cpp
+++ b/third_party/WebKit/Source/core/dom/ClassicPendingScript.cpp
@@ -239,7 +239,7 @@
     ScriptSourceCode source_code(GetElement()->TextFromChildren(),
                                  source_location_type_, document_url,
                                  StartingPosition());
-    return ClassicScript::Create(source_code, options_);
+    return ClassicScript::Create(source_code, options_, kSharableCrossOrigin);
   }
 
   DCHECK(GetResource()->IsLoaded());
@@ -247,7 +247,8 @@
                         !streamer_->StreamingSuppressed();
   ScriptSourceCode source_code(streamer_ready ? streamer_ : nullptr,
                                GetResource());
-  return ClassicScript::Create(source_code, options_);
+  return ClassicScript::Create(source_code, options_,
+                               GetResource()->CalculateAccessControlStatus());
 }
 
 void ClassicPendingScript::SetStreamer(ScriptStreamer* streamer) {
diff --git a/third_party/WebKit/Source/core/dom/ClassicScript.cpp b/third_party/WebKit/Source/core/dom/ClassicScript.cpp
index 3bf4f195..bd769ef 100644
--- a/third_party/WebKit/Source/core/dom/ClassicScript.cpp
+++ b/third_party/WebKit/Source/core/dom/ClassicScript.cpp
@@ -10,7 +10,6 @@
 #include "core/frame/UseCounter.h"
 #include "core/inspector/ConsoleMessage.h"
 #include "core/loader/AllowedByNosniff.h"
-#include "platform/loader/fetch/AccessControlStatus.h"
 #include "platform/network/mime/MIMETypeRegistry.h"
 
 namespace blink {
@@ -29,19 +28,8 @@
 
 void ClassicScript::RunScript(LocalFrame* frame,
                               const SecurityOrigin* security_origin) const {
-  const bool is_external_script = GetScriptSourceCode().GetResource();
-
-  AccessControlStatus access_control_status = kNotSharableCrossOrigin;
-  if (!is_external_script) {
-    access_control_status = kSharableCrossOrigin;
-  } else {
-    CHECK(GetScriptSourceCode().GetResource());
-    access_control_status =
-        GetScriptSourceCode().GetResource()->CalculateAccessControlStatus();
-  }
-
   frame->GetScriptController().ExecuteScriptInMainWorld(
-      GetScriptSourceCode(), FetchOptions(), access_control_status);
+      GetScriptSourceCode(), FetchOptions(), access_control_status_);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/ClassicScript.h b/third_party/WebKit/Source/core/dom/ClassicScript.h
index 1582a76..59b3e690 100644
--- a/third_party/WebKit/Source/core/dom/ClassicScript.h
+++ b/third_party/WebKit/Source/core/dom/ClassicScript.h
@@ -8,6 +8,8 @@
 #include "bindings/core/v8/ScriptSourceCode.h"
 #include "core/CoreExport.h"
 #include "core/dom/Script.h"
+#include "core/loader/resource/ScriptResource.h"
+#include "platform/loader/fetch/AccessControlStatus.h"
 #include "platform/loader/fetch/ScriptFetchOptions.h"
 
 namespace blink {
@@ -15,8 +17,10 @@
 class CORE_EXPORT ClassicScript final : public Script {
  public:
   static ClassicScript* Create(const ScriptSourceCode& script_source_code,
-                               const ScriptFetchOptions& fetch_options) {
-    return new ClassicScript(script_source_code, fetch_options);
+                               const ScriptFetchOptions& fetch_options,
+                               AccessControlStatus access_control_status) {
+    return new ClassicScript(script_source_code, fetch_options,
+                             access_control_status);
   }
 
   void Trace(blink::Visitor*);
@@ -27,8 +31,11 @@
 
  private:
   ClassicScript(const ScriptSourceCode& script_source_code,
-                const ScriptFetchOptions& fetch_options)
-      : Script(fetch_options), script_source_code_(script_source_code) {}
+                const ScriptFetchOptions& fetch_options,
+                AccessControlStatus access_control_status)
+      : Script(fetch_options),
+        script_source_code_(script_source_code),
+        access_control_status_(access_control_status) {}
 
   ScriptType GetScriptType() const override { return ScriptType::kClassic; }
   bool CheckMIMETypeBeforeRunScript(Document* context_document,
@@ -39,6 +46,7 @@
   }
 
   const ScriptSourceCode script_source_code_;
+  const AccessControlStatus access_control_status_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/vr/VRDisplay.cpp b/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
index de9c6e77..18530631 100644
--- a/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
+++ b/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
@@ -1035,9 +1035,11 @@
 
 void VRDisplay::OnPresentationProviderConnectionError() {
   vr_presentation_provider_.reset();
-  ForceExitPresent();
-  pending_vsync_ = false;
-  RequestVSync();
+  if (is_presenting_) {
+    ForceExitPresent();
+    pending_vsync_ = false;
+    RequestVSync();
+  }
 }
 
 ScriptedAnimationController& VRDisplay::EnsureScriptedAnimationController(
diff --git a/third_party/WebKit/Source/platform/graphics/CanvasHeuristicParameters.h b/third_party/WebKit/Source/platform/graphics/CanvasHeuristicParameters.h
index 9f6210d..acfe4919 100644
--- a/third_party/WebKit/Source/platform/graphics/CanvasHeuristicParameters.h
+++ b/third_party/WebKit/Source/platform/graphics/CanvasHeuristicParameters.h
@@ -10,25 +10,13 @@
 namespace CanvasHeuristicParameters {
 
 enum {
-  // Layer promotion heuristic parameters
+  // Disable Deferral overdraw parameters
   //======================================
 
-  // FIXME (crbug.com/463239):
-  // The Layer promotion heuristics should go away after slimming paint
-  // is completely phased in and display list canvases are modified to
-  // use a lightweight layering primitive instead of the
-  // SkCanvas::saveLayer.
-
   // Heuristic: Canvases that are overdrawn beyond this factor in a
-  // single frame are promoted to a direct composited layer so that
-  // their contents not be re-rasterized by the compositor when the
-  // containing layer is the object of a paint invalidation.
+  // single frame will be disabled deferral.
   kExpensiveOverdrawThreshold = 10,
 
-  kComplexClipsAreExpensive = 1,
-
-  kBlurredShadowsAreExpensive = 1,
-
   // Disable Acceleration heuristic parameters
   //===========================================
 
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 173235c..844a1b9 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/w3c/test_importer.py
@@ -36,7 +36,7 @@
 TIMEOUT_SECONDS = 210 * 60
 
 # Sheriff calendar URL, used for getting the ecosystem infra sheriff to TBR.
-ROTATIONS_URL = 'http://chromium-build.appspot.com/p/chromium/all_rotations.js'
+ROTATIONS_URL = 'https://build.chromium.org/deprecated/chromium/all_rotations.js'
 
 _log = logging.getLogger(__file__)
 
diff --git a/third_party/android_platform/README.chromium b/third_party/android_platform/README.chromium
index 9685213..2346f55 100644
--- a/third_party/android_platform/README.chromium
+++ b/third_party/android_platform/README.chromium
@@ -40,10 +40,6 @@
 Fixed invalid using decl in logging header debug.h
 Only attempt to symbolize with ELF libraries.
 
-Changed the stack script to use llvm symbolizer instead of addr2line,
-objdump, etc, since llvm symbolizer is more efficient in finding
-function names, line numbers etc.
-
 Android relocation packing tool details:
     Copy sources from AOSP bionic/tools/relocation_packer
     Remove scripts that regenerate golden test data (not relevant here)
diff --git a/third_party/android_platform/development/scripts/stack b/third_party/android_platform/development/scripts/stack
index a69cec3..8d09461 100755
--- a/third_party/android_platform/development/scripts/stack
+++ b/third_party/android_platform/development/scripts/stack
@@ -31,13 +31,8 @@
 sys.path.insert(0, os.path.join(os.path.dirname(__file__),
                                 os.pardir, os.pardir, os.pardir, os.pardir,
                                 'build', 'android'))
-
 from pylib import constants
 
-sys.path.insert(0, os.path.join(os.path.dirname(__file__),
-                                os.pardir, os.pardir, os.pardir, os.pardir,
-                                'tools', 'python'))
-import llvm_symbolizer
 
 DEFAULT_SYMROOT='/tmp/symbols'
 # From: https://source.android.com/source/build-numbers.html
@@ -230,12 +225,10 @@
   print ("Reading Android symbols from: "
          + os.path.normpath(symbol.SYMBOLS_DIR))
   chrome_search_path = symbol.GetLibrarySearchPaths()
-
-  with llvm_symbolizer.LLVMSymbolizer() as symbolizer:
-    print ("Searching for Chrome symbols from within: "
-           + ':'.join((os.path.normpath(d) for d in chrome_search_path)))
-    stack_core.ConvertTrace(lines, load_vaddrs, more_info, fallback_monochrome,
-                            arch_defined, symbolizer)
+  print ("Searching for Chrome symbols from within: "
+         + ':'.join((os.path.normpath(d) for d in chrome_search_path)))
+  stack_core.ConvertTrace(lines, load_vaddrs, more_info, fallback_monochrome,
+                          arch_defined)
 
   if rootdir:
     # be a good citizen and clean up...os.rmdir and os.removedirs() don't work
diff --git a/third_party/android_platform/development/scripts/stack_core.py b/third_party/android_platform/development/scripts/stack_core.py
index aa7da9f..f206793 100755
--- a/third_party/android_platform/development/scripts/stack_core.py
+++ b/third_party/android_platform/development/scripts/stack_core.py
@@ -158,7 +158,7 @@
   print
   print '-----------------------------------------------------\n'
 
-def ConvertTrace(lines, load_vaddrs, more_info, fallback_monochrome, arch_defined, llvm_symbolizer):
+def ConvertTrace(lines, load_vaddrs, more_info, fallback_monochrome, arch_defined):
   """Convert strings containing native crash to a stack."""
   InitWidthRelatedLineMatchers()
 
@@ -189,7 +189,7 @@
       print ('Find ABI:' + arch)
       symbol.ARCH = arch
 
-  ResolveCrashSymbol(list(useful_log), more_info, llvm_symbolizer)
+  ResolveCrashSymbol(list(useful_log), more_info)
   end = time.time()
   logging.debug('Finished resolving symbols. Elapsed time: %.4fs',
                 (end - start))
@@ -304,7 +304,7 @@
         useful_log.append(line)
     return useful_log, self._so_dirs
 
-def ResolveCrashSymbol(lines, more_info, llvm_symbolizer):
+def ResolveCrashSymbol(lines, more_info):
   """Convert unicode strings which contains native crash to a stack
   """
 
@@ -313,6 +313,39 @@
   last_frame = -1
   pid = -1
 
+  # It is faster to get symbol information with a single call rather than with
+  # separate calls for each line. Since symbol.SymbolInformation caches results,
+  # we can extract all the addresses that we will want symbol information for
+  # from the log and call symbol.SymbolInformation so that the results are
+  # cached in the following lookups.
+  code_addresses = {}
+
+  # Collects all java exception lines, keyed by pid for later output during
+  # native crash handling.
+  java_stderr_by_pid = {}
+  for line in lines:
+    lib, address = None, None
+
+    match = _TRACE_LINE.match(line) or _DEBUG_TRACE_LINE.match(line)
+    if match:
+      address, lib = match.group('address', 'lib')
+
+    match = _VALUE_LINE.match(line)
+    if match and not _CODE_LINE.match(line):
+      (_0, _1, address, lib, _2, _3) = match.groups()
+
+    if lib:
+      code_addresses.setdefault(lib, set()).add(address)
+
+    java_stderr_match = _JAVA_STDERR_LINE.search(line)
+    if java_stderr_match:
+      pid, msg = java_stderr_match.groups()
+      java_stderr_by_pid.setdefault(pid, []).append(msg)
+
+  for lib in code_addresses:
+    symbol.SymbolInformationForSet(
+        symbol.TranslateLibPath(lib), code_addresses[lib], more_info)
+
   for line in lines:
     # AndroidFeedback adds zero width spaces into its crash reports. These
     # should be removed or the regular expresssions will fail to match.
@@ -377,19 +410,25 @@
         logging.debug('Identified lib: %s' % area)
         # If a calls b which further calls c and c is inlined to b, we want to
         # display "a -> b -> c" in the stack trace instead of just "a -> c"
-        # To use llvm symbolizer, the hexadecimal address has to start with 0x.
-        info = llvm_symbolizer.GetSymbolInformation(
-            os.path.join(symbol.SYMBOLS_DIR, symbol.TranslateLibPath(area)),
-            '0x' + code_addr)
+        info = symbol.SymbolInformation(area, code_addr, more_info)
         logging.debug('symbol information: %s' % info)
         nest_count = len(info) - 1
-        for source_symbol, source_location in info:
+        for (source_symbol, source_location, object_symbol_with_offset) in info:
+          if not source_symbol:
+            if symbol_present:
+              source_symbol = symbol.CallCppFilt(symbol_name)
+            else:
+              source_symbol = UNKNOWN
+          if not source_location:
+            source_location = area
           if nest_count > 0:
             nest_count = nest_count - 1
             trace_lines.append(('v------>', source_symbol, source_location))
           else:
+            if not object_symbol_with_offset:
+              object_symbol_with_offset = source_symbol
             trace_lines.append((code_addr,
-                                source_symbol,
+                                object_symbol_with_offset,
                                 source_location))
     match = _VALUE_LINE.match(line)
     if match:
@@ -397,14 +436,20 @@
       if area == UNKNOWN or area == HEAP or area == STACK or not area:
         value_lines.append((addr, value, '', area))
       else:
-        info = llvm_symbolizer.GetSymbolInformation(
-            os.path.join(symbol.SYMBOLS_DIR, symbol.TranslateLibPath(area)),
-            '0x' + code_addr)
-        source_symbol, source_location = info.pop()
-
+        info = symbol.SymbolInformation(area, value, more_info)
+        (source_symbol, source_location, object_symbol_with_offset) = info.pop()
+        if not source_symbol:
+          if symbol_present:
+            source_symbol = symbol.CallCppFilt(symbol_name)
+          else:
+            source_symbol = UNKNOWN
+        if not source_location:
+          source_location = area
+        if not object_symbol_with_offset:
+          object_symbol_with_offset = source_symbol
         value_lines.append((addr,
                             value,
-                            source_symbol,
+                            object_symbol_with_offset,
                             source_location))
 
   java_lines = []
diff --git a/third_party/android_platform/development/scripts/symbol.py b/third_party/android_platform/development/scripts/symbol.py
index 7a82c82..469446ca 100755
--- a/third_party/android_platform/development/scripts/symbol.py
+++ b/third_party/android_platform/development/scripts/symbol.py
@@ -69,6 +69,91 @@
     return "linux-x86"
   return uname
 
+def ToolPath(tool, toolchain_info=None):
+  """Return a full qualified path to the specified tool"""
+  # ToolPath looks for the tools in the completely incorrect directory.
+  # This looks in the checked in android_tools.
+  if ARCH == "arm":
+    toolchain_source = "arm-linux-androideabi-4.9"
+    toolchain_prefix = "arm-linux-androideabi"
+    ndk = "ndk"
+  elif ARCH == "arm64":
+    toolchain_source = "aarch64-linux-android-4.9"
+    toolchain_prefix = "aarch64-linux-android"
+    ndk = "ndk"
+  elif ARCH == "x86":
+    toolchain_source = "x86-4.9"
+    toolchain_prefix = "i686-linux-android"
+    ndk = "ndk"
+  elif ARCH == "x86_64" or ARCH == "x64":
+    toolchain_source = "x86_64-4.9"
+    toolchain_prefix = "x86_64-linux-android"
+    ndk = "ndk"
+  elif ARCH == "mips":
+    toolchain_source = "mipsel-linux-android-4.9"
+    toolchain_prefix = "mipsel-linux-android"
+    ndk = "ndk"
+  else:
+    raise Exception("Could not find tool chain for " + ARCH)
+
+  toolchain_subdir = (
+      "third_party/android_tools/%s/toolchains/%s/prebuilt/linux-x86_64/bin" %
+       (ndk, toolchain_source))
+
+  return os.path.join(CHROME_SRC,
+                      toolchain_subdir,
+                      toolchain_prefix + "-" + tool)
+
+def FindToolchain():
+  """Look for the latest available toolchain
+
+  Args:
+    None
+
+  Returns:
+    A pair of strings containing toolchain label and target prefix.
+  """
+  global TOOLCHAIN_INFO
+  if TOOLCHAIN_INFO is not None:
+    return TOOLCHAIN_INFO
+
+  ## Known toolchains, newer ones in the front.
+  gcc_version = "4.9"
+  if ARCH == "arm64":
+    known_toolchains = [
+      ("aarch64-linux-android-" + gcc_version, "aarch64", "aarch64-linux-android")
+    ]
+  elif ARCH == "arm":
+    known_toolchains = [
+      ("arm-linux-androideabi-" + gcc_version, "arm", "arm-linux-androideabi")
+    ]
+  elif ARCH =="x86":
+    known_toolchains = [
+      ("x86-" + gcc_version, "x86", "i686-linux-android")
+    ]
+  elif ARCH =="x86_64" or ARCH =="x64":
+    known_toolchains = [
+      ("x86_64-" + gcc_version, "x86_64", "x86_64-linux-android")
+    ]
+  elif ARCH == "mips":
+    known_toolchains = [
+      ("mipsel-linux-android-" + gcc_version, "mips", "mipsel-linux-android")
+    ]
+  else:
+    known_toolchains = []
+
+  logging.debug('FindToolcahin: known_toolchains=%s' % known_toolchains)
+  # Look for addr2line to check for valid toolchain path.
+  for (label, platform, target) in known_toolchains:
+    toolchain_info = (label, platform, target);
+    if os.path.exists(ToolPath("addr2line", toolchain_info)):
+      TOOLCHAIN_INFO = toolchain_info
+      print ("Using toolchain from: "
+             + os.path.normpath(ToolPath("", TOOLCHAIN_INFO)))
+      return toolchain_info
+
+  raise Exception("Could not find tool chain")
+
 def GetAapt():
   """Returns the path to aapt.
 
@@ -279,7 +364,260 @@
 
   library_path = os.path.relpath(candidate_libraries[0], SYMBOLS_DIR)
   logging.debug('TranslateLibPath: library_path=%s' % library_path)
-  return library_path
+  return '/' + library_path
+
+def SymbolInformation(lib, addr, get_detailed_info):
+  """Look up symbol information about an address.
+
+  Args:
+    lib: library (or executable) pathname containing symbols
+    addr: string hexidecimal address
+
+  Returns:
+    A list of the form [(source_symbol, source_location,
+    object_symbol_with_offset)].
+
+    If the function has been inlined then the list may contain
+    more than one element with the symbols for the most deeply
+    nested inlined location appearing first.  The list is
+    always non-empty, even if no information is available.
+
+    Usually you want to display the source_location and
+    object_symbol_with_offset from the last element in the list.
+  """
+  lib = TranslateLibPath(lib)
+  info = SymbolInformationForSet(lib, set([addr]), get_detailed_info)
+  return (info and info.get(addr)) or [(None, None, None)]
+
+
+def SymbolInformationForSet(lib, unique_addrs, get_detailed_info):
+  """Look up symbol information for a set of addresses from the given library.
+
+  Args:
+    lib: library (or executable) pathname containing symbols
+    unique_addrs: set of hexidecimal addresses
+
+  Returns:
+    A dictionary of the form {addr: [(source_symbol, source_location,
+    object_symbol_with_offset)]} where each address has a list of
+    associated symbols and locations.  The list is always non-empty.
+
+    If the function has been inlined then the list may contain
+    more than one element with the symbols for the most deeply
+    nested inlined location appearing first.  The list is
+    always non-empty, even if no information is available.
+
+    Usually you want to display the source_location and
+    object_symbol_with_offset from the last element in the list.
+  """
+  if not lib:
+    return None
+
+  addr_to_line = CallAddr2LineForSet(lib, unique_addrs)
+  if not addr_to_line:
+    return None
+
+  if get_detailed_info:
+    addr_to_objdump = CallObjdumpForSet(lib, unique_addrs)
+    if not addr_to_objdump:
+      return None
+  else:
+    addr_to_objdump = dict((addr, ("", 0)) for addr in unique_addrs)
+
+  result = {}
+  for addr in unique_addrs:
+    source_info = addr_to_line.get(addr)
+    if not source_info:
+      source_info = [(None, None)]
+    if addr in addr_to_objdump:
+      (object_symbol, object_offset) = addr_to_objdump.get(addr)
+      object_symbol_with_offset = FormatSymbolWithOffset(object_symbol,
+                                                         object_offset)
+    else:
+      object_symbol_with_offset = None
+    result[addr] = [(source_symbol, source_location, object_symbol_with_offset)
+        for (source_symbol, source_location) in source_info]
+
+  return result
+
+
+class MemoizedForSet(object):
+  def __init__(self, fn):
+    self.fn = fn
+    self.cache = {}
+
+  def __call__(self, lib, unique_addrs):
+    lib_cache = self.cache.setdefault(lib, {})
+
+    no_cache = filter(lambda x: x not in lib_cache, unique_addrs)
+    if no_cache:
+      lib_cache.update((k, None) for k in no_cache)
+      result = self.fn(lib, no_cache)
+      if result:
+        lib_cache.update(result)
+
+    return dict((k, lib_cache[k]) for k in unique_addrs if lib_cache[k])
+
+
+@MemoizedForSet
+def CallAddr2LineForSet(lib, unique_addrs):
+  """Look up line and symbol information for a set of addresses.
+
+  Args:
+    lib: library (or executable) pathname containing symbols
+    unique_addrs: set of string hexidecimal addresses look up.
+
+  Returns:
+    A dictionary of the form {addr: [(symbol, file:line)]} where
+    each address has a list of associated symbols and locations
+    or an empty list if no symbol information was found.
+
+    If the function has been inlined then the list may contain
+    more than one element with the symbols for the most deeply
+    nested inlined location appearing first.
+  """
+  if not lib:
+    return None
+
+  symbols = SYMBOLS_DIR + lib
+  if not os.path.splitext(symbols)[1] in ['', '.so', '.apk']:
+    return None
+
+  if not os.path.isfile(symbols):
+    return None
+
+  addrs = sorted(unique_addrs)
+  result = {}
+
+  def _Callback(sym, addr):
+    records = []
+    while sym:  # Traverse all the inlines following the |inlined_by| chain.
+      if sym.source_path and sym.source_line:
+        location = '%s:%d' % (sym.source_path, sym.source_line)
+      else:
+        location = None
+      records += [(sym.name, location)]
+      sym = sym.inlined_by
+    result[addr] = records
+
+  (label, platform, target) = FindToolchain()
+  symbolizer = elf_symbolizer.ELFSymbolizer(
+      elf_file_path=symbols,
+      addr2line_path=ToolPath("addr2line"),
+      callback=_Callback,
+      inlines=True)
+
+  for addr in addrs:
+    symbolizer.SymbolizeAsync(int(addr, 16), addr)
+  symbolizer.Join()
+  return result
+
+
+def StripPC(addr):
+  """Strips the Thumb bit a program counter address when appropriate.
+
+  Args:
+    addr: the program counter address
+
+  Returns:
+    The stripped program counter address.
+  """
+  global ARCH
+
+  if ARCH == "arm":
+    return addr & ~1
+  return addr
+
+@MemoizedForSet
+def CallObjdumpForSet(lib, unique_addrs):
+  """Use objdump to find out the names of the containing functions.
+
+  Args:
+    lib: library (or executable) pathname containing symbols
+    unique_addrs: set of string hexidecimal addresses to find the functions for.
+
+  Returns:
+    A dictionary of the form {addr: (string symbol, offset)}.
+  """
+  if not lib:
+    return None
+
+  symbols = SYMBOLS_DIR + lib
+  if not os.path.exists(symbols):
+    return None
+
+  symbols = SYMBOLS_DIR + lib
+  if not os.path.exists(symbols):
+    return None
+
+  result = {}
+
+  # Function lines look like:
+  #   000177b0 <android::IBinder::~IBinder()+0x2c>:
+  # We pull out the address and function first. Then we check for an optional
+  # offset. This is tricky due to functions that look like "operator+(..)+0x2c"
+  func_regexp = re.compile("(^[a-f0-9]*) \<(.*)\>:$")
+  offset_regexp = re.compile("(.*)\+0x([a-f0-9]*)")
+
+  # A disassembly line looks like:
+  #   177b2:  b510        push  {r4, lr}
+  asm_regexp = re.compile("(^[ a-f0-9]*):[ a-f0-0]*.*$")
+
+  for target_addr in unique_addrs:
+    start_addr_dec = str(StripPC(int(target_addr, 16)))
+    stop_addr_dec = str(StripPC(int(target_addr, 16)) + 8)
+    cmd = [ToolPath("objdump"),
+           "--section=.text",
+           "--demangle",
+           "--disassemble",
+           "--start-address=" + start_addr_dec,
+           "--stop-address=" + stop_addr_dec,
+           symbols]
+
+    current_symbol = None    # The current function symbol in the disassembly.
+    current_symbol_addr = 0  # The address of the current function.
+
+    stream = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout
+    for line in stream:
+      # Is it a function line like:
+      #   000177b0 <android::IBinder::~IBinder()>:
+      components = func_regexp.match(line)
+      if components:
+        # This is a new function, so record the current function and its address.
+        current_symbol_addr = int(components.group(1), 16)
+        current_symbol = components.group(2)
+
+        # Does it have an optional offset like: "foo(..)+0x2c"?
+        components = offset_regexp.match(current_symbol)
+        if components:
+          current_symbol = components.group(1)
+          offset = components.group(2)
+          if offset:
+            current_symbol_addr -= int(offset, 16)
+
+      # Is it an disassembly line like:
+      #   177b2:  b510        push  {r4, lr}
+      components = asm_regexp.match(line)
+      if components:
+        addr = components.group(1)
+        i_addr = int(addr, 16)
+        i_target = StripPC(int(target_addr, 16))
+        if i_addr == i_target:
+          result[target_addr] = (current_symbol, i_target - current_symbol_addr)
+    stream.close()
+
+  return result
+
+
+def CallCppFilt(mangled_symbol):
+  cmd = [ToolPath("c++filt")]
+  process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+  process.stdin.write(mangled_symbol)
+  process.stdin.write("\n")
+  process.stdin.close()
+  demangled_symbol = process.stdout.readline().strip()
+  process.stdout.close()
+  return demangled_symbol
 
 def FormatSymbolWithOffset(symbol, offset):
   if offset == 0:
diff --git a/tools/grit/grit/format/android_xml.py b/tools/grit/grit/format/android_xml.py
index 25774ab9..42ba6bc 100755
--- a/tools/grit/grit/format/android_xml.py
+++ b/tools/grit/grit/format/android_xml.py
@@ -174,13 +174,17 @@
     return None
   body_in = plural_match.group('items').strip()
   lines = []
+  quantities_so_far = set()
   for item_match in _PLURALS_ITEM_PATTERN.finditer(body_in):
     quantity_in = item_match.group('quantity')
     quantity_out = _PLURALS_QUANTITY_MAP.get(quantity_in)
     value_in = item_match.group('value')
     value_out = '"' + value_in.replace('#', '%d') + '"'
     if quantity_out:
-      lines.append(_PLURALS_ITEM_TEMPLATE % (quantity_out, value_out))
+      # only one line per quantity out (https://crbug.com/787488)
+      if quantity_out not in quantities_so_far:
+        quantities_so_far.add(quantity_out)
+        lines.append(_PLURALS_ITEM_TEMPLATE % (quantity_out, value_out))
     else:
       raise Exception('Unsupported plural quantity for android '
                       'strings.xml: %s' % quantity_in)
diff --git a/tools/grit/grit/format/android_xml_unittest.py b/tools/grit/grit/format/android_xml_unittest.py
index 5e9871ed..6f496b61 100755
--- a/tools/grit/grit/format/android_xml_unittest.py
+++ b/tools/grit/grit/format/android_xml_unittest.py
@@ -80,6 +80,34 @@
 """
     self.assertEqual(output.strip(), expected.strip())
 
+
+  def testConflictingPlurals(self):
+    root = util.ParseGrdForUnittest(ur"""
+        <messages>
+          <message name="IDS_PLURALS" desc="A string using the ICU plural format">
+            {NUM_THINGS, plural,
+            =1 {Maybe I'll get one laser.}
+            one {Maybe I'll get one laser.}
+            other {Maybe I'll get # lasers.}}
+          </message>
+        </messages>
+        """)
+
+    buf = StringIO.StringIO()
+    build.RcBuilder.ProcessNode(root, DummyOutput('android', 'en'), buf)
+    output = buf.getvalue()
+    expected = ur"""
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+<plurals name="plurals">
+  <item quantity="one">"Maybe I\'ll get one laser."</item>
+  <item quantity="other">"Maybe I\'ll get %d lasers."</item>
+</plurals>
+</resources>
+"""
+    self.assertEqual(output.strip(), expected.strip())
+
+
   def testTaggedOnly(self):
     root = util.ParseGrdForUnittest(ur"""
         <messages>
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 8108a8d..7f333f9 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -17519,6 +17519,36 @@
   <description>User unmuted site via the tab strip.</description>
 </action>
 
+<action name="SplitView_DoubleTapDividerSwapWindows">
+  <owner>xdai@chromium.org</owner>
+  <description>
+    Recorded when the user double tapped on the split divider to swap windows in
+    tablet mode on Chrome OS.
+  </description>
+</action>
+
+<action name="SplitView_EndSplitView">
+  <owner>xdai@chromium.org</owner>
+  <description>
+    Recorded when the user ended split view mode in tablet mode on Chrome OS.
+  </description>
+</action>
+
+<action name="SplitView_ResizeWindows">
+  <owner>xdai@chromium.org</owner>
+  <description>
+    Recorded when the user dragged the split divider to resize the windows in
+    tablet mode on Chrome OS.
+  </description>
+</action>
+
+<action name="SplitView_SnapWindow">
+  <owner>xdai@chromium.org</owner>
+  <description>
+    Recorded when a window was just snapped in tablet mode on Chrome OS.
+  </description>
+</action>
+
 <action name="StackedTab_DragActiveTab">
   <owner>bruthig@chromium.org</owner>
   <owner>tdanderson@chromium.org</owner>
@@ -18544,6 +18574,22 @@
   </description>
 </action>
 
+<action name="Tablet_LongPressOverviewButtonEnterSplitView">
+  <owner>xdai@chromium.org</owner>
+  <description>
+    Recorded when the user long pressed the overview button to enter split view
+    in tablet mode on Chrome OS.
+  </description>
+</action>
+
+<action name="Tablet_LongPressOverviewButtonExitSplitView">
+  <owner>xdai@chromium.org</owner>
+  <description>
+    Recorded when the user long pressed the overview button to exit split view
+    in tablet mode on Chrome OS.
+  </description>
+</action>
+
 <action name="Tablet_QuickSwitch">
   <owner>sammiequon@chromium.org</owner>
   <description>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 4cf6ed1d..4d6c2e47 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -3346,6 +3346,15 @@
   </summary>
 </histogram>
 
+<histogram name="Ash.SplitView.TimeInSplitView" units="ms">
+  <owner>xdai@chromium.org</owner>
+  <summary>
+    The amount of time that the user spent in split view mode. The time is
+    measured from the moment a window is snapped to one side of the screen to
+    when split view mode is ended.
+  </summary>
+</histogram>
+
 <histogram name="Ash.StationaryTouchDuration" units="seconds">
   <obsolete>
     Deprecated 02/2017 due to lack of usage.
@@ -10109,6 +10118,17 @@
   </summary>
 </histogram>
 
+<histogram name="Compositing.Browser.HitTestTimeToFindClosestLayer"
+    units="microseconds">
+  <owner>yigu@chromium.org</owner>
+  <summary>
+    Time spent finding the closest matching layer to a given point whenever we
+    do hit testing on LayerTreeImpl (in a browser process).
+
+    Team: animations-dev@chromium.org.
+  </summary>
+</histogram>
+
 <histogram base="true" name="Compositing.Browser.LayersUpdateTime"
     units="microseconds">
   <owner>threaded-rendering-dev@chromium.org</owner>
@@ -10465,6 +10485,17 @@
   </summary>
 </histogram>
 
+<histogram name="Compositing.Renderer.HitTestTimeToFindClosestLayer"
+    units="microseconds">
+  <owner>yigu@chromium.org</owner>
+  <summary>
+    Time spent finding the closest matching layer to a given point whenever we
+    do hit testing on LayerTreeImpl (in a renderer process).
+
+    Team: animations-dev@chromium.org.
+  </summary>
+</histogram>
+
 <histogram base="true" name="Compositing.Renderer.LayersUpdateTime"
     units="microseconds">
   <owner>threaded-rendering-dev@chromium.org</owner>
diff --git a/tools/python/llvm_symbolizer.py b/tools/python/llvm_symbolizer.py
deleted file mode 100644
index fd0df11..0000000
--- a/tools/python/llvm_symbolizer.py
+++ /dev/null
@@ -1,110 +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.
-
-import logging
-import os
-import re
-import subprocess
-import threading
-
-_CHROME_SRC = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)
-_LLVM_SYMBOLIZER_PATH = os.path.join(
-    _CHROME_SRC, 'third_party', 'llvm-build', 'Release+Asserts', 'bin',
-    'llvm-symbolizer')
-
-_BINARY = re.compile(r'0b[0,1]+')
-_HEX = re.compile(r'0x[0-9,a-e]+')
-_OCTAL = re.compile(r'0[0-7]+')
-
-_UNKNOWN = '<UNKNOWN>'
-
-
-def _CheckValidAddr(addr):
-  """
-  Check whether the addr is valid input to llvm symbolizer.
-  Valid addr has to be octal, binary, or hex number.
-
-  Args:
-    addr: addr to be entered to llvm symbolizer.
-
-  Returns:
-    whether the addr is valid input to llvm symbolizer.
-  """
-  return _HEX.match(addr) or _OCTAL.match(addr) or _BINARY.match(addr)
-
-
-class LLVMSymbolizer(object):
-  def __init__(self):
-    """Create a LLVMSymbolizer instance that interacts with the llvm symbolizer.
-
-    The purpose of the LLVMSymbolizer is to get function names and line
-    numbers of an address from the symbols library.
-    """
-    self._llvm_symbolizer_subprocess = None
-    # Allow only one thread to call GetSymbolInformation at a time.
-    self._lock = threading.Lock()
-
-  def Start(self):
-    """Start the llvm symbolizer subprocess.
-
-    Create a subprocess of the llvm symbolizer executable, which will be used
-    to retrieve function names etc.
-    """
-    if os.path.isfile(_LLVM_SYMBOLIZER_PATH):
-      self._llvm_symbolizer_subprocess = subprocess.Popen(
-        [_LLVM_SYMBOLIZER_PATH], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
-    else:
-      logging.error('Cannot find llvm_symbolizer here: %s.' %
-                    _LLVM_SYMBOLIZER_PATH)
-      self._llvm_symbolizer_subprocess = None
-
-  def Close(self):
-    """Close the llvm symbolizer subprocess.
-
-    Close the subprocess by closing stdin, stdout and killing the subprocess.
-    """
-    with self._lock:
-      if self._llvm_symbolizer_subprocess:
-        self._llvm_symbolizer_subprocess.kill()
-        self._llvm_symbolizer_subprocess = None
-
-  def __enter__(self):
-    """Start the llvm symbolizer subprocess."""
-    self.Start()
-    return self
-
-  def __exit__(self, exc_type, exc_val, exc_tb):
-    """Close the llvm symbolizer subprocess."""
-    self.Close()
-
-  def GetSymbolInformation(self, lib, addr):
-    """Return the corresponding function names and line numbers.
-
-    Args:
-      lib: library to search for info.
-      addr: address to look for info.
-
-    Returns:
-      A list of (function name, line numbers) tuple.
-    """
-    if (self._llvm_symbolizer_subprocess is None or not lib
-        or not _CheckValidAddr(addr) or not os.path.isfile(lib)):
-      return [(_UNKNOWN, lib)]
-
-    with self._lock:
-      self._llvm_symbolizer_subprocess.stdin.write('%s %s\n' % (lib, addr))
-      self._llvm_symbolizer_subprocess.stdin.flush()
-
-      result = []
-      # Read till see new line, which is a symbol of end of output.
-      # One line of function name is always followed by one line of line number.
-      while True:
-        line = self._llvm_symbolizer_subprocess.stdout.readline()
-        if line != '\n':
-          line_numbers = self._llvm_symbolizer_subprocess.stdout.readline()
-          result.append(
-            (line[:-1],
-             line_numbers[:-1]))
-        else:
-          return result