diff --git a/DEPS b/DEPS
index 24bb66c..cf6e622 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '452f86d592afdd6bebaf067bcf5edc783485d61c',
+  'v8_revision': '72a3ba4aff4bbd3fb68c22e36cc48564cfa0fb1e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -52,7 +52,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '85d624a5101da5ed9b327dba5b8186ab25bcd9dc',
+  'angle_revision': '553590a57921fd83abb814fee943c0ccc9bf45a2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -60,11 +60,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '64ed0d75e58d5b6b12bdb30ae6b83a878a4142a6',
+  'swiftshader_revision': '5bf72ee3fc17b345d88e5a75b369f9b52517ec44',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '7e805d1a6f3bf7ddd04b2c1ce857a2cf6d9ff873',
+  'pdfium_revision': '00d2ad12fa9d37581fd12d2ea5ea702c9e2e2e16',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -953,6 +953,16 @@
       ],
     },
     {
+      'name': 'android_system_sdk',
+      'pattern': '.',
+      'action': ['python',
+                 'src/build/android/update_deps/update_third_party_deps.py',
+                 'download',
+                 '-b', 'android_system_stubs',
+                 '-l', 'third_party/android_system_sdk'
+      ],
+    },
+    {
       'name': 'intellij',
       'pattern': '.',
       'action': ['python',
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java b/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java
index 0451dba..e4e2605 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentsStatics.java
@@ -115,21 +115,30 @@
 
     @SuppressWarnings("unchecked")
     @TargetApi(19)
-    public static void initSafeBrowsing(Context context, ValueCallback<Boolean> callback) {
-        if (callback == null) {
-            callback = new ValueCallback<Boolean>() {
-                @Override
-                public void onReceiveValue(Boolean b) {}
-            };
-        }
+    public static void initSafeBrowsing(Context context, final ValueCallback<Boolean> callback) {
+        // Wrap the callback to make sure we always invoke it on the UI thread, as guaranteed by the
+        // API.
+        ValueCallback<Boolean> wrapperCallback = new ValueCallback<Boolean>() {
+            @Override
+            public void onReceiveValue(Boolean b) {
+                if (callback != null) {
+                    ThreadUtils.runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            callback.onReceiveValue(b);
+                        }
+                    });
+                }
+            }
+        };
 
         try {
             Class cls = Class.forName(sSafeBrowsingWarmUpHelper);
             Method m =
                     cls.getDeclaredMethod("warmUpSafeBrowsing", Context.class, ValueCallback.class);
-            m.invoke(null, context, callback);
+            m.invoke(null, context, wrapperCallback);
         } catch (ReflectiveOperationException e) {
-            callback.onReceiveValue(false);
+            wrapperCallback.onReceiveValue(false);
         }
     }
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
index b12600a..3bbce54 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
@@ -63,6 +63,9 @@
 
     private EmbeddedTestServer mTestServer;
 
+    // Used to check which thread a callback is invoked on.
+    private volatile boolean mOnUiThread;
+
     // These colors correspond to the body.background attribute in GREEN_HTML_PATH, SAFE_HTML_PATH,
     // MALWARE_HTML_PATH, IFRAME_HTML_PATH, etc. They should only be changed if those values are
     // changed as well
@@ -963,4 +966,25 @@
         mContentsClient.getOnPageFinishedHelper().waitForCallback(pageFinishedCount);
         assertEquals(linkUrl, mAwContents.getUrl());
     }
+
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    @CommandLineFlags.Add(AwSwitches.WEBVIEW_ENABLE_SAFEBROWSING_SUPPORT)
+    public void testInitSafeBrowsingCallbackOnUIThread() throws Throwable {
+        Context ctx = getInstrumentation().getTargetContext().getApplicationContext();
+        CallbackHelper helper = new CallbackHelper();
+        int count = helper.getCallCount();
+        mOnUiThread = false;
+        AwContentsStatics.initSafeBrowsing(ctx, new ValueCallback<Boolean>() {
+            @Override
+            public void onReceiveValue(Boolean b) {
+                mOnUiThread = ThreadUtils.runningOnUiThread();
+                helper.notifyCalled();
+            }
+        });
+        helper.waitForCallback(count);
+        // Don't run the assert on the callback's thread, since the test runner loses the stack
+        // trace unless on the instrumentation thread.
+        assertTrue("Callback should run on UI Thread", mOnUiThread);
+    }
 }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index e8d2ecb4..55ee006 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -481,7 +481,6 @@
     "system/screen_security/screen_share_tray_item.h",
     "system/screen_security/screen_tray_item.cc",
     "system/screen_security/screen_tray_item.h",
-    "system/session/last_window_closed_observer.h",
     "system/session/logout_button_tray.cc",
     "system/session/logout_button_tray.h",
     "system/session/logout_confirmation_controller.cc",
diff --git a/ash/metrics/OWNERS b/ash/metrics/OWNERS
deleted file mode 100644
index 21cb7090..0000000
--- a/ash/metrics/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-tdanderson@chromium.org
diff --git a/ash/resources/OWNERS b/ash/resources/OWNERS
index 3064b12..92f3fbb9 100644
--- a/ash/resources/OWNERS
+++ b/ash/resources/OWNERS
@@ -1,2 +1 @@
 oshima@chromium.org
-tdanderson@chromium.org
diff --git a/ash/shelf/shelf_constants.h b/ash/shelf/shelf_constants.h
index 80749375..862d582d 100644
--- a/ash/shelf/shelf_constants.h
+++ b/ash/shelf/shelf_constants.h
@@ -65,8 +65,6 @@
 constexpr int kShelfOpaqueColorDarkenAlpha = 178;
 
 // The width and height of the material design overflow button.
-// TODO(tdanderson): Refactor constants which are common between the shelf
-// and the tray. See crbug.com/623987.
 constexpr int kOverflowButtonSize = 32;
 
 // The radius of the rounded corners of the overflow button.
diff --git a/ash/shell.cc b/ash/shell.cc
index 1d7b781..b656fd50 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -736,7 +736,10 @@
   for (aura::Window* root : GetAllRootWindows())
     Shelf::ForWindow(root)->ShutdownShelfWidget();
   tray_bluetooth_helper_.reset();
-  DeleteSystemTrayDelegate();
+  system_tray_delegate_.reset();
+
+  // Accesses root window containers.
+  logout_confirmation_controller_.reset();
 
   // Drag-and-drop must be canceled prior to close all windows.
   drag_drop_controller_.reset();
@@ -1111,8 +1114,11 @@
   resize_shadow_controller_.reset(new ResizeShadowController());
   shadow_controller_.reset(new ::wm::ShadowController(focus_controller_.get()));
 
-  SetSystemTrayDelegate(
-      base::WrapUnique(shell_delegate_->CreateSystemTrayDelegate()));
+  system_tray_delegate_ =
+      base::WrapUnique(shell_delegate_->CreateSystemTrayDelegate());
+
+  logout_confirmation_controller_ =
+      base::MakeUnique<LogoutConfirmationController>();
 
   // May trigger initialization of the Bluetooth adapter.
   tray_bluetooth_helper_->Initialize();
@@ -1197,24 +1203,6 @@
   root_window->AddPostTargetHandler(toplevel_window_event_handler_.get());
 }
 
-void Shell::SetSystemTrayDelegate(
-    std::unique_ptr<SystemTrayDelegate> delegate) {
-  DCHECK(delegate);
-  system_tray_delegate_ = std::move(delegate);
-  system_tray_delegate_->Initialize();
-  // Accesses ShellPort in its constructor.
-  logout_confirmation_controller_.reset(new LogoutConfirmationController(
-      base::Bind(&SystemTrayController::SignOut,
-                 base::Unretained(system_tray_controller_.get()))));
-}
-
-void Shell::DeleteSystemTrayDelegate() {
-  DCHECK(system_tray_delegate_);
-  // Accesses ShellPort in its destructor.
-  logout_confirmation_controller_.reset();
-  system_tray_delegate_.reset();
-}
-
 void Shell::CloseAllRootWindowChildWindows() {
   for (aura::Window* root : GetAllRootWindows()) {
     RootWindowController* controller = RootWindowController::ForWindow(root);
diff --git a/ash/system/OWNERS b/ash/system/OWNERS
index 6f83f3f7f..bae17c8 100644
--- a/ash/system/OWNERS
+++ b/ash/system/OWNERS
@@ -2,6 +2,5 @@
 stevenjb@chromium.org
 jennyz@chromium.org
 skuhne@chromium.org
-tdanderson@chromium.org
 
 # COMPONENT: UI>Shell>StatusArea
diff --git a/ash/system/date/date_view.cc b/ash/system/date/date_view.cc
index c6346be..fc693e6 100644
--- a/ash/system/date/date_view.cc
+++ b/ash/system/date/date_view.cc
@@ -127,7 +127,6 @@
 
 DateView::DateView(SystemTrayItem* owner)
     : BaseDateTimeView(owner), action_(DateAction::NONE) {
-  // TODO(tdanderson): Tweak spacing and layout for material design.
   views::BoxLayout* box_layout =
       new views::BoxLayout(views::BoxLayout::kHorizontal,
                            gfx::Insets(0, kTrayPopupLabelHorizontalPadding), 0);
diff --git a/ash/system/session/last_window_closed_observer.h b/ash/system/session/last_window_closed_observer.h
deleted file mode 100644
index e23e5da..0000000
--- a/ash/system/session/last_window_closed_observer.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_SYSTEM_SESSION_LAST_WINDOW_CLOSED_OBSERVER_H_
-#define ASH_SYSTEM_SESSION_LAST_WINDOW_CLOSED_OBSERVER_H_
-
-#include "ash/ash_export.h"
-
-namespace ash {
-
-class ASH_EXPORT LastWindowClosedObserver {
- public:
-  virtual void OnLastWindowClosed() = 0;
-
- protected:
-  virtual ~LastWindowClosedObserver() {}
-};
-
-}  // namespace ash
-
-#endif  // ASH_SYSTEM_SESSION_LAST_WINDOW_CLOSED_OBSERVER_H_
diff --git a/ash/system/session/logout_confirmation_controller.cc b/ash/system/session/logout_confirmation_controller.cc
index 7fab21d..68a2b46f 100644
--- a/ash/system/session/logout_confirmation_controller.cc
+++ b/ash/system/session/logout_confirmation_controller.cc
@@ -7,35 +7,114 @@
 #include <utility>
 
 #include "ash/login_status.h"
+#include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
+#include "ash/shell_observer.h"
 #include "ash/system/session/logout_confirmation_dialog.h"
-#include "ash/system/tray/system_tray_notifier.h"
+#include "ash/system/tray/system_tray_controller.h"
+#include "base/callback.h"
 #include "base/location.h"
 #include "base/time/default_tick_clock.h"
 #include "base/time/tick_clock.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_observer.h"
 #include "ui/views/widget/widget.h"
 
 namespace ash {
 namespace {
 const int kLogoutConfirmationDelayInSeconds = 20;
+
+// Shell window containers monitored for when the last window closes.
+const int kLastWindowClosedContainerIds[] = {
+    kShellWindowId_DefaultContainer, kShellWindowId_AlwaysOnTopContainer};
+
+void SignOut() {
+  Shell::Get()->system_tray_controller()->SignOut();
 }
 
-LogoutConfirmationController::LogoutConfirmationController(
-    const base::Closure& logout_closure)
-    : clock_(new base::DefaultTickClock),
-      logout_closure_(logout_closure),
-      dialog_(NULL),
+}  // namespace
+
+// Monitors window containers to detect when the last browser or app window is
+// closing and shows a logout confirmation dialog.
+class LogoutConfirmationController::LastWindowClosedObserver
+    : public ShellObserver,
+      public aura::WindowObserver {
+ public:
+  LastWindowClosedObserver() {
+    DCHECK_EQ(Shell::Get()->session_controller()->login_status(),
+              LoginStatus::PUBLIC);
+    Shell::Get()->AddShellObserver(this);
+
+    // Observe all displays.
+    for (aura::Window* root : Shell::GetAllRootWindows())
+      ObserveForLastWindowClosed(root);
+  }
+
+  ~LastWindowClosedObserver() override {
+    // Stop observing all displays.
+    for (aura::Window* root : Shell::GetAllRootWindows()) {
+      for (int id : kLastWindowClosedContainerIds)
+        root->GetChildById(id)->RemoveObserver(this);
+    }
+    Shell::Get()->RemoveShellObserver(this);
+  }
+
+ private:
+  // Observes containers in the |root| window for the last browser and/or app
+  // window being closed. The observers are removed automatically.
+  void ObserveForLastWindowClosed(aura::Window* root) {
+    for (int id : kLastWindowClosedContainerIds)
+      root->GetChildById(id)->AddObserver(this);
+  }
+
+  // Shows the logout confirmation dialog if the last window is closing in the
+  // containers we are tracking. Called before closing instead of after closed
+  // because aura::WindowObserver only provides notifications to parent windows
+  // before a child is removed, not after.
+  void ShowDialogIfLastWindowClosing() {
+    size_t window_count = 0u;
+    for (aura::Window* root : Shell::GetAllRootWindows()) {
+      for (int id : kLastWindowClosedContainerIds)
+        window_count += root->GetChildById(id)->children().size();
+    }
+
+    // Prompt if the last window is closing.
+    if (window_count == 1) {
+      Shell::Get()->logout_confirmation_controller()->ConfirmLogout(
+          base::TimeTicks::Now() +
+          base::TimeDelta::FromSeconds(kLogoutConfirmationDelayInSeconds));
+    }
+  }
+
+  // ShellObserver:
+  void OnRootWindowAdded(aura::Window* root) override {
+    ObserveForLastWindowClosed(root);
+  }
+
+  // aura::WindowObserver:
+  void OnWindowHierarchyChanging(const HierarchyChangeParams& params) override {
+    if (!params.new_parent && params.old_parent) {
+      // A window is being removed (and not moved to another container).
+      ShowDialogIfLastWindowClosing();
+    }
+  }
+
+  void OnWindowDestroying(aura::Window* window) override {
+    // Stop observing the container window when it closes.
+    window->RemoveObserver(this);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(LastWindowClosedObserver);
+};
+
+LogoutConfirmationController::LogoutConfirmationController()
+    : clock_(base::MakeUnique<base::DefaultTickClock>()),
+      logout_closure_(base::Bind(&SignOut)),
       logout_timer_(false, false),
-      scoped_session_observer_(this) {
-  if (Shell::HasInstance())
-    Shell::Get()->system_tray_notifier()->AddLastWindowClosedObserver(this);
-}
+      scoped_session_observer_(this) {}
 
 LogoutConfirmationController::~LogoutConfirmationController() {
-  if (Shell::HasInstance())
-    Shell::Get()->system_tray_notifier()->RemoveLastWindowClosedObserver(this);
-
   if (dialog_)
     dialog_->ControllerGone();
 }
@@ -61,9 +140,12 @@
                       logout_closure_);
 }
 
-void LogoutConfirmationController::SetClockForTesting(
-    std::unique_ptr<base::TickClock> clock) {
-  clock_ = std::move(clock);
+void LogoutConfirmationController::OnLoginStatusChanged(
+    LoginStatus login_status) {
+  if (login_status == LoginStatus::PUBLIC)
+    last_window_closed_observer_ = base::MakeUnique<LastWindowClosedObserver>();
+  else
+    last_window_closed_observer_.reset();
 }
 
 void LogoutConfirmationController::OnLockStateChanged(bool locked) {
@@ -89,17 +171,14 @@
   logout_timer_.Stop();
 }
 
-void LogoutConfirmationController::OnLastWindowClosed() {
-  if (Shell::Get()->session_controller()->login_status() !=
-      LoginStatus::PUBLIC) {
-    return;
-  }
+void LogoutConfirmationController::SetClockForTesting(
+    std::unique_ptr<base::TickClock> clock) {
+  clock_ = std::move(clock);
+}
 
-  // Ask the user to confirm logout if a public session is in progress and the
-  // screen is not locked.
-  ConfirmLogout(
-      base::TimeTicks::Now() +
-      base::TimeDelta::FromSeconds(kLogoutConfirmationDelayInSeconds));
+void LogoutConfirmationController::SetLogoutClosureForTesting(
+    const base::Closure& logout_closure) {
+  logout_closure_ = logout_closure;
 }
 
 }  // namespace ash
diff --git a/ash/system/session/logout_confirmation_controller.h b/ash/system/session/logout_confirmation_controller.h
index eb04e2c..b94affc 100644
--- a/ash/system/session/logout_confirmation_controller.h
+++ b/ash/system/session/logout_confirmation_controller.h
@@ -9,8 +9,7 @@
 
 #include "ash/ash_export.h"
 #include "ash/session/session_observer.h"
-#include "ash/system/session/last_window_closed_observer.h"
-#include "base/callback.h"
+#include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
@@ -33,12 +32,9 @@
 //
 // In public sessions, asks the user to end the session when the last window is
 // closed.
-class ASH_EXPORT LogoutConfirmationController
-    : public SessionObserver,
-      public LastWindowClosedObserver {
+class ASH_EXPORT LogoutConfirmationController : public SessionObserver {
  public:
-  // The |logout_closure| must be safe to call as long as |this| is alive.
-  explicit LogoutConfirmationController(const base::Closure& logout_closure);
+  LogoutConfirmationController();
   ~LogoutConfirmationController() override;
 
   base::TickClock* clock() const { return clock_.get(); }
@@ -48,9 +44,8 @@
   // the current dialog's |logout_time_|.
   void ConfirmLogout(base::TimeTicks logout_time);
 
-  void SetClockForTesting(std::unique_ptr<base::TickClock> clock);
-
   // SessionObserver:
+  void OnLoginStatusChanged(LoginStatus login_status) override;
   void OnLockStateChanged(bool locked) override;
 
   // Called by the |dialog_| when the user confirms logout.
@@ -59,17 +54,19 @@
   // Called by the |dialog_| when it is closed.
   void OnDialogClosed();
 
+  void SetClockForTesting(std::unique_ptr<base::TickClock> clock);
+  void SetLogoutClosureForTesting(const base::Closure& logout_closure);
   LogoutConfirmationDialog* dialog_for_testing() const { return dialog_; }
 
  private:
-  // LastWindowClosedObserver:
-  void OnLastWindowClosed() override;
+  class LastWindowClosedObserver;
+  std::unique_ptr<LastWindowClosedObserver> last_window_closed_observer_;
 
   std::unique_ptr<base::TickClock> clock_;
   base::Closure logout_closure_;
 
   base::TimeTicks logout_time_;
-  LogoutConfirmationDialog* dialog_;  // Owned by the Views hierarchy.
+  LogoutConfirmationDialog* dialog_ = nullptr;  // Owned by the Views hierarchy.
   base::Timer logout_timer_;
 
   ScopedSessionObserver scoped_session_observer_;
diff --git a/ash/system/session/logout_confirmation_controller_unittest.cc b/ash/system/session/logout_confirmation_controller_unittest.cc
index 70ebeb5..1b658f7 100644
--- a/ash/system/session/logout_confirmation_controller_unittest.cc
+++ b/ash/system/session/logout_confirmation_controller_unittest.cc
@@ -4,13 +4,20 @@
 
 #include "ash/system/session/logout_confirmation_controller.h"
 
+#include "ash/session/test_session_controller_client.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/memory/ref_counted.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/tick_clock.h"
+#include "components/session_manager/session_manager_types.h"
+#include "components/user_manager/user_type.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/aura/window.h"
+#include "ui/views/widget/widget.h"
 
 namespace ash {
 
@@ -35,10 +42,10 @@
 LogoutConfirmationControllerTest::LogoutConfirmationControllerTest()
     : log_out_called_(false),
       runner_(new base::TestMockTimeTaskRunner),
-      runner_handle_(runner_),
-      controller_(base::Bind(&LogoutConfirmationControllerTest::LogOut,
-                             base::Unretained(this))) {
+      runner_handle_(runner_) {
   controller_.SetClockForTesting(runner_->GetMockTickClock());
+  controller_.SetLogoutClosureForTesting(base::Bind(
+      &LogoutConfirmationControllerTest::LogOut, base::Unretained(this)));
 }
 
 LogoutConfirmationControllerTest::~LogoutConfirmationControllerTest() {}
@@ -151,4 +158,116 @@
   EXPECT_TRUE(log_out_called_);
 }
 
+class LastWindowClosedTest : public NoSessionAshTestBase {
+ public:
+  LastWindowClosedTest() = default;
+  ~LastWindowClosedTest() override = default;
+
+  // Simulate a public account signing in.
+  void StartPublicAccountSession() {
+    TestSessionControllerClient* session = GetSessionControllerClient();
+    session->Reset();
+    session->AddUserSession("user1@test.com",
+                            user_manager::USER_TYPE_PUBLIC_ACCOUNT);
+    session->SetSessionState(session_manager::SessionState::ACTIVE);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LastWindowClosedTest);
+};
+
+TEST_F(LastWindowClosedTest, RegularSession) {
+  // Dialog is not visible at startup.
+  LogoutConfirmationController* controller =
+      Shell::Get()->logout_confirmation_controller();
+  EXPECT_FALSE(controller->dialog_for_testing());
+
+  // Dialog is not visible after login.
+  SetSessionStarted(true);
+  EXPECT_FALSE(controller->dialog_for_testing());
+
+  // Creating and closing a window does not show the dialog because this is not
+  // a public account session.
+  std::unique_ptr<aura::Window> window = CreateToplevelTestWindow();
+  EXPECT_FALSE(controller->dialog_for_testing());
+  window.reset();
+  EXPECT_FALSE(controller->dialog_for_testing());
+}
+
+TEST_F(LastWindowClosedTest, PublicSession) {
+  LogoutConfirmationController* controller =
+      Shell::Get()->logout_confirmation_controller();
+
+  // Dialog is not visible after public account login.
+  StartPublicAccountSession();
+  EXPECT_FALSE(controller->dialog_for_testing());
+
+  // Opening windows does not show the dialog.
+  std::unique_ptr<views::Widget> widget1 = CreateTestWidget();
+  std::unique_ptr<views::Widget> widget2 = CreateTestWidget();
+  EXPECT_FALSE(controller->dialog_for_testing());
+
+  // Closing the last window shows the dialog.
+  widget1.reset();
+  EXPECT_FALSE(controller->dialog_for_testing());
+  widget2.reset();
+  EXPECT_TRUE(controller->dialog_for_testing());
+}
+
+TEST_F(LastWindowClosedTest, AlwaysOnTop) {
+  LogoutConfirmationController* controller =
+      Shell::Get()->logout_confirmation_controller();
+  StartPublicAccountSession();
+
+  // The new widget starts in the default window container.
+  std::unique_ptr<views::Widget> widget = CreateTestWidget();
+
+  // Moving the widget to the always-on-top container does not trigger the
+  // dialog because the window didn't close.
+  widget->SetAlwaysOnTop(true);
+  EXPECT_FALSE(controller->dialog_for_testing());
+
+  // Closing the window triggers the dialog.
+  widget.reset();
+  EXPECT_TRUE(controller->dialog_for_testing());
+}
+
+TEST_F(LastWindowClosedTest, MultipleContainers) {
+  LogoutConfirmationController* controller =
+      Shell::Get()->logout_confirmation_controller();
+  StartPublicAccountSession();
+
+  // Create two windows in different containers.
+  std::unique_ptr<views::Widget> normal_widget = CreateTestWidget();
+  std::unique_ptr<views::Widget> always_on_top_widget = CreateTestWidget();
+  always_on_top_widget->SetAlwaysOnTop(true);
+
+  // Closing the last window shows the dialog.
+  always_on_top_widget.reset();
+  EXPECT_FALSE(controller->dialog_for_testing());
+  normal_widget.reset();
+  EXPECT_TRUE(controller->dialog_for_testing());
+}
+
+TEST_F(LastWindowClosedTest, MultipleDisplays) {
+  LogoutConfirmationController* controller =
+      Shell::Get()->logout_confirmation_controller();
+  StartPublicAccountSession();
+
+  // Create two displays, each with a window.
+  UpdateDisplay("1024x768,800x600");
+  std::unique_ptr<aura::Window> window1 =
+      CreateChildWindow(Shell::GetAllRootWindows()[0]->GetChildById(
+          kShellWindowId_DefaultContainer));
+  std::unique_ptr<aura::Window> window2 =
+      CreateChildWindow(Shell::GetAllRootWindows()[1]->GetChildById(
+          kShellWindowId_DefaultContainer));
+
+  // Closing the last window shows the dialog.
+  window1.reset();
+  EXPECT_FALSE(controller->dialog_for_testing());
+  window2.reset();
+  EXPECT_TRUE(controller->dialog_for_testing());
+}
+
 }  // namespace ash
diff --git a/ash/system/tray/system_tray_delegate.cc b/ash/system/tray/system_tray_delegate.cc
index 327920c..33423ae 100644
--- a/ash/system/tray/system_tray_delegate.cc
+++ b/ash/system/tray/system_tray_delegate.cc
@@ -10,8 +10,6 @@
 
 SystemTrayDelegate::~SystemTrayDelegate() = default;
 
-void SystemTrayDelegate::Initialize() {}
-
 NetworkingConfigDelegate* SystemTrayDelegate::GetNetworkingConfigDelegate()
     const {
   return nullptr;
diff --git a/ash/system/tray/system_tray_delegate.h b/ash/system/tray/system_tray_delegate.h
index 671b5e14..cd195db 100644
--- a/ash/system/tray/system_tray_delegate.h
+++ b/ash/system/tray/system_tray_delegate.h
@@ -23,9 +23,6 @@
   SystemTrayDelegate();
   virtual ~SystemTrayDelegate();
 
-  // Called after SystemTray has been instantiated.
-  virtual void Initialize();
-
   // Returns NetworkingConfigDelegate. May return nullptr.
   virtual NetworkingConfigDelegate* GetNetworkingConfigDelegate() const;
 
diff --git a/ash/system/tray/system_tray_notifier.cc b/ash/system/tray/system_tray_notifier.cc
index 11b2c9e5..7ba5172f 100644
--- a/ash/system/tray/system_tray_notifier.cc
+++ b/ash/system/tray/system_tray_notifier.cc
@@ -13,7 +13,6 @@
 #include "ash/system/network/network_portal_detector_observer.h"
 #include "ash/system/screen_security/screen_capture_observer.h"
 #include "ash/system/screen_security/screen_share_observer.h"
-#include "ash/system/session/last_window_closed_observer.h"
 #include "ash/system/status_area_focus_observer.h"
 #include "ash/system/tray_tracing.h"
 #include "ash/system/virtual_keyboard/virtual_keyboard_observer.h"
@@ -119,21 +118,6 @@
     observer.OnIMEMenuActivationChanged(is_active);
 }
 
-void SystemTrayNotifier::AddLastWindowClosedObserver(
-    LastWindowClosedObserver* observer) {
-  last_window_closed_observers_.AddObserver(observer);
-}
-
-void SystemTrayNotifier::RemoveLastWindowClosedObserver(
-    LastWindowClosedObserver* observer) {
-  last_window_closed_observers_.RemoveObserver(observer);
-}
-
-void SystemTrayNotifier::NotifyLastWindowClosed() {
-  for (auto& observer : last_window_closed_observers_)
-    observer.OnLastWindowClosed();
-}
-
 void SystemTrayNotifier::AddNetworkObserver(NetworkObserver* observer) {
   network_observers_.AddObserver(observer);
 }
diff --git a/ash/system/tray/system_tray_notifier.h b/ash/system/tray/system_tray_notifier.h
index 3c65f35..84be8bd 100644
--- a/ash/system/tray/system_tray_notifier.h
+++ b/ash/system/tray/system_tray_notifier.h
@@ -22,7 +22,6 @@
 class ClockObserver;
 class EnterpriseDomainObserver;
 class IMEObserver;
-class LastWindowClosedObserver;
 class NetworkObserver;
 class NetworkPortalDetectorObserver;
 class ScreenCaptureObserver;
@@ -72,11 +71,6 @@
   void NotifyRefreshIME();
   void NotifyRefreshIMEMenu(bool is_active);
 
-  // Last window closed.
-  void AddLastWindowClosedObserver(LastWindowClosedObserver* observer);
-  void RemoveLastWindowClosedObserver(LastWindowClosedObserver* observer);
-  void NotifyLastWindowClosed();
-
   // Network.
   void AddNetworkObserver(NetworkObserver* observer);
   void RemoveNetworkObserver(NetworkObserver* observer);
@@ -124,7 +118,6 @@
   base::ObserverList<ClockObserver> clock_observers_;
   base::ObserverList<EnterpriseDomainObserver> enterprise_domain_observers_;
   base::ObserverList<IMEObserver> ime_observers_;
-  base::ObserverList<LastWindowClosedObserver> last_window_closed_observers_;
   base::ObserverList<NetworkObserver> network_observers_;
   base::ObserverList<NetworkPortalDetectorObserver>
       network_portal_detector_observers_;
diff --git a/ash/test/ash_test_base.h b/ash/test/ash_test_base.h
index 34125cf..b975144 100644
--- a/ash/test/ash_test_base.h
+++ b/ash/test/ash_test_base.h
@@ -88,9 +88,9 @@
   // Creates and shows a widget. See ash/public/cpp/shell_window_ids.h for
   // values for |container_id|.
   static std::unique_ptr<views::Widget> CreateTestWidget(
-      views::WidgetDelegate* delegate,
-      int container_id,
-      const gfx::Rect& bounds);
+      views::WidgetDelegate* delegate = nullptr,
+      int container_id = kShellWindowId_DefaultContainer,
+      const gfx::Rect& bounds = gfx::Rect());
 
   // Creates a visible window in the appropriate container. If
   // |bounds_in_screen| is empty the window is added to the primary root
diff --git a/ash/wm/overview/window_selector_item.cc b/ash/wm/overview/window_selector_item.cc
index 9aecca6..4083e0a 100644
--- a/ash/wm/overview/window_selector_item.cc
+++ b/ash/wm/overview/window_selector_item.cc
@@ -59,7 +59,7 @@
 // Foreground label color.
 static const SkColor kLabelColor = SK_ColorWHITE;
 
-// TODO(tdanderson): Move this to a central location.
+// Close button color.
 static const SkColor kCloseButtonColor = SK_ColorWHITE;
 
 // Label background color once in overview mode.
diff --git a/base/i18n/file_util_icu.cc b/base/i18n/file_util_icu.cc
index 08ff101..e0cf3d52 100644
--- a/base/i18n/file_util_icu.cc
+++ b/base/i18n/file_util_icu.cc
@@ -115,23 +115,16 @@
   while (cursor < static_cast<int>(file_name->size())) {
     int char_begin = cursor;
     uint32_t code_point;
-#if defined(OS_MACOSX)
-    // Mac uses UTF-8 encoding for filenames.
-    U8_NEXT(file_name->data(), cursor, static_cast<int>(file_name->length()),
-            code_point);
-#elif defined(OS_WIN)
+#if defined(OS_WIN)
     // Windows uses UTF-16 encoding for filenames.
     U16_NEXT(file_name->data(), cursor, static_cast<int>(file_name->length()),
              code_point);
-#elif defined(OS_POSIX)
-    // Linux doesn't actually define an encoding. It basically allows anything
-    // except for a few special ASCII characters.
-    unsigned char cur_char = static_cast<unsigned char>((*file_name)[cursor++]);
-    if (cur_char >= 0x80)
-      continue;
-    code_point = cur_char;
 #else
-    NOTREACHED();
+    // Mac and Chrome OS use UTF-8 encoding for filenames.
+    // Linux doesn't actually define file system encoding. Try to parse as
+    // UTF-8.
+    U8_NEXT(file_name->data(), cursor, static_cast<int>(file_name->length()),
+            code_point);
 #endif
 
     if (illegal->DisallowedEverywhere(code_point) ||
diff --git a/base/i18n/file_util_icu.h b/base/i18n/file_util_icu.h
index 6d7f2ec..f8bd9f4 100644
--- a/base/i18n/file_util_icu.h
+++ b/base/i18n/file_util_icu.h
@@ -34,6 +34,9 @@
 //   refer to devices rather than files (E.g. LPT1), and some filenames could be
 //   interpreted as shell namespace extensions (E.g. Foo.{<GUID>}).
 //
+// On Windows, Chrome OS and Mac, the file system encoding is already known and
+// parsed as UTF-8 and UTF-16 accordingly.
+// On Linux, the file name will be parsed as UTF8.
 // TODO(asanka): Move full filename sanitization logic here.
 BASE_I18N_EXPORT void ReplaceIllegalCharactersInPath(
     FilePath::StringType* file_name,
diff --git a/base/i18n/file_util_icu_unittest.cc b/base/i18n/file_util_icu_unittest.cc
index 003fcf5..2028997 100644
--- a/base/i18n/file_util_icu_unittest.cc
+++ b/base/i18n/file_util_icu_unittest.cc
@@ -23,7 +23,7 @@
 
 #if defined(OS_POSIX) && !defined(OS_MACOSX)
 
-// Linux disallows some evil ASCII characters, but passes all non-ASCII.
+// On linux, file path is parsed and filtered as UTF-8.
 static const struct GoodBadPairLinux {
   const char* bad_name;
   const char* good_name;
@@ -82,7 +82,7 @@
     {L".    ", L"-   -"}
 };
 
-#if defined(OS_WIN) || defined(OS_MACOSX)
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_POSIX)
 
 TEST_F(FileUtilICUTest, ReplaceIllegalCharactersInPathTest) {
   for (size_t i = 0; i < arraysize(kIllegalCharacterCases); ++i) {
@@ -90,7 +90,7 @@
     std::wstring bad_name(kIllegalCharacterCases[i].bad_name);
     ReplaceIllegalCharactersInPath(&bad_name, '-');
     EXPECT_EQ(kIllegalCharacterCases[i].good_name, bad_name);
-#elif defined(OS_MACOSX)
+#else
     std::string bad_name(WideToUTF8(kIllegalCharacterCases[i].bad_name));
     ReplaceIllegalCharactersInPath(&bad_name, '-');
     EXPECT_EQ(WideToUTF8(kIllegalCharacterCases[i].good_name), bad_name);
diff --git a/build/android/gyp/apk_obfuscate.py b/build/android/gyp/apk_obfuscate.py
deleted file mode 100755
index 04a04b3..0000000
--- a/build/android/gyp/apk_obfuscate.py
+++ /dev/null
@@ -1,185 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-
-"""Generates the obfuscated jar and test jar for an apk.
-
-If proguard is not enabled or 'Release' is not in the configuration name,
-obfuscation will be a no-op.
-"""
-
-import json
-import optparse
-import os
-import sys
-import tempfile
-
-from util import build_utils
-from util import proguard_util
-
-
-_PROGUARD_KEEP_CLASS = '''-keep class %s {
-  *;
-}
-'''
-
-
-def ParseArgs(argv):
-  parser = optparse.OptionParser()
-  parser.add_option('--android-sdk', help='path to the Android SDK folder')
-  parser.add_option('--android-sdk-tools',
-                    help='path to the Android SDK build tools folder')
-  parser.add_option('--android-sdk-jar',
-                    help='path to Android SDK\'s android.jar')
-  parser.add_option('--proguard-jar-path',
-                    help='Path to proguard.jar in the sdk')
-  parser.add_option('--input-jars-paths',
-                    help='Path to jars to include in obfuscated jar')
-
-  parser.add_option('--proguard-configs',
-                    help='Paths to proguard config files')
-
-  parser.add_option('--configuration-name',
-                    help='Gyp configuration name (i.e. Debug, Release)')
-
-  parser.add_option('--debug-build-proguard-enabled', action='store_true',
-                    help='--proguard-enabled takes effect on release '
-                         'build, this flag enable the proguard on debug '
-                         'build.')
-  parser.add_option('--proguard-enabled', action='store_true',
-                    help='Set if proguard is enabled for this target.')
-
-  parser.add_option('--obfuscated-jar-path',
-                    help='Output path for obfuscated jar.')
-
-  parser.add_option('--testapp', action='store_true',
-                    help='Set this if building an instrumentation test apk')
-  parser.add_option('--tested-apk-obfuscated-jar-path',
-                    help='Path to obfusctated jar of the tested apk')
-  parser.add_option('--test-jar-path',
-                    help='Output path for jar containing all the test apk\'s '
-                    'code.')
-
-  parser.add_option('--stamp', help='File to touch on success')
-
-  parser.add_option('--main-dex-list-path',
-                    help='The list of classes to retain in the main dex. '
-                         'These will not be obfuscated.')
-  parser.add_option('--multidex-configuration-path',
-                    help='A JSON file containing multidex build configuration.')
-  parser.add_option('--verbose', '-v', action='store_true',
-                    help='Print all proguard output')
-
-  (options, args) = parser.parse_args(argv)
-
-  if args:
-    parser.error('No positional arguments should be given. ' + str(args))
-
-  # Check that required options have been provided.
-  required_options = (
-      'android_sdk',
-      'android_sdk_tools',
-      'android_sdk_jar',
-      'proguard_jar_path',
-      'input_jars_paths',
-      'configuration_name',
-      'obfuscated_jar_path',
-      )
-
-  if options.testapp:
-    required_options += (
-        'test_jar_path',
-        )
-
-  build_utils.CheckOptions(options, parser, required=required_options)
-  return options, args
-
-
-def DoProguard(options):
-  proguard = proguard_util.ProguardCmdBuilder(options.proguard_jar_path)
-  proguard.outjar(options.obfuscated_jar_path)
-
-  input_jars = build_utils.ParseGnList(options.input_jars_paths)
-
-  exclude_paths = []
-  configs = build_utils.ParseGnList(options.proguard_configs)
-  if options.tested_apk_obfuscated_jar_path:
-    # configs should only contain the process_resources.py generated config.
-    assert len(configs) == 1, (
-        'test apks should not have custom proguard configs: ' + str(configs))
-    proguard.tested_apk_info(options.tested_apk_obfuscated_jar_path + '.info')
-
-  proguard.libraryjars([options.android_sdk_jar])
-  proguard_injars = [p for p in input_jars if p not in exclude_paths]
-  proguard.injars(proguard_injars)
-
-  multidex_config = _PossibleMultidexConfig(options)
-  if multidex_config:
-    configs.append(multidex_config)
-
-  proguard.configs(configs)
-  proguard.verbose(options.verbose)
-  proguard.CheckOutput()
-
-
-def _PossibleMultidexConfig(options):
-  if not options.multidex_configuration_path:
-    return None
-
-  with open(options.multidex_configuration_path) as multidex_config_file:
-    multidex_config = json.loads(multidex_config_file.read())
-
-  if not (multidex_config.get('enabled') and options.main_dex_list_path):
-    return None
-
-  main_dex_list_config = ''
-  with open(options.main_dex_list_path) as main_dex_list:
-    for clazz in (l.strip() for l in main_dex_list):
-      if clazz.endswith('.class'):
-        clazz = clazz[:-len('.class')]
-      clazz = clazz.replace('/', '.')
-      main_dex_list_config += (_PROGUARD_KEEP_CLASS % clazz)
-  with tempfile.NamedTemporaryFile(
-      delete=False,
-      dir=os.path.dirname(options.main_dex_list_path),
-      prefix='main_dex_list_proguard',
-      suffix='.flags') as main_dex_config_file:
-    main_dex_config_file.write(main_dex_list_config)
-  return main_dex_config_file.name
-
-
-def main(argv):
-  options, _ = ParseArgs(argv)
-
-  input_jars = build_utils.ParseGnList(options.input_jars_paths)
-
-  if options.testapp:
-    dependency_class_filters = [
-        '*R.class', '*R$*.class', '*Manifest.class', '*BuildConfig.class']
-    build_utils.MergeZips(
-        options.test_jar_path, input_jars, dependency_class_filters)
-
-  if ((options.configuration_name == 'Release' and options.proguard_enabled) or
-     (options.configuration_name == 'Debug' and
-      options.debug_build_proguard_enabled)):
-    DoProguard(options)
-  else:
-    output_files = [
-        options.obfuscated_jar_path,
-        options.obfuscated_jar_path + '.info',
-        options.obfuscated_jar_path + '.dump',
-        options.obfuscated_jar_path + '.seeds',
-        options.obfuscated_jar_path + '.usage',
-        options.obfuscated_jar_path + '.mapping']
-    for f in output_files:
-      if os.path.exists(f):
-        os.remove(f)
-      build_utils.Touch(f)
-
-  if options.stamp:
-    build_utils.Touch(options.stamp)
-
-if __name__ == '__main__':
-  sys.exit(main(sys.argv[1:]))
diff --git a/build/android/gyp/configure_multidex.py b/build/android/gyp/configure_multidex.py
deleted file mode 100755
index 63c74f0..0000000
--- a/build/android/gyp/configure_multidex.py
+++ /dev/null
@@ -1,87 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-
-import argparse
-import json
-import os
-import sys
-
-from util import build_utils
-
-
-_GCC_PREPROCESS_PATH = os.path.join(
-    os.path.dirname(__file__), 'gcc_preprocess.py')
-
-
-def ParseArgs():
-  parser = argparse.ArgumentParser()
-  parser.add_argument('--configuration-name', required=True,
-                      help='The build CONFIGURATION_NAME.')
-  parser.add_argument('--enable-multidex', action='store_true', default=False,
-                      help='If passed, multidex may be enabled.')
-  parser.add_argument('--enabled-configurations', default=[],
-                      help='The configuration(s) for which multidex should be '
-                           'enabled. If not specified and --enable-multidex is '
-                           'passed, multidex will be enabled for all '
-                           'configurations.')
-  parser.add_argument('--multidex-configuration-path', required=True,
-                      help='The path to which the multidex configuration JSON '
-                           'should be saved.')
-  parser.add_argument('--multidex-config-java-file', required=True)
-  parser.add_argument('--multidex-config-java-stamp', required=True)
-  parser.add_argument('--multidex-config-java-template', required=True)
-
-  args = parser.parse_args()
-
-  if args.enabled_configurations:
-    args.enabled_configurations = build_utils.ParseGnList(
-        args.enabled_configurations)
-
-  return args
-
-
-def _WriteConfigJson(multidex_enabled, multidex_configuration_path):
-  config = {
-    'enabled': multidex_enabled,
-  }
-
-  with open(multidex_configuration_path, 'w') as f:
-    f.write(json.dumps(config))
-
-
-def _GenerateMultidexConfigJava(multidex_enabled, args):
-  gcc_preprocess_cmd = [
-    sys.executable, _GCC_PREPROCESS_PATH,
-    '--include-path=',
-    '--template', args.multidex_config_java_template,
-    '--stamp', args.multidex_config_java_stamp,
-    '--output', args.multidex_config_java_file,
-  ]
-  if multidex_enabled:
-    gcc_preprocess_cmd += [
-      '--defines', 'ENABLE_MULTIDEX',
-    ]
-
-  build_utils.CheckOutput(gcc_preprocess_cmd)
-
-
-def main():
-  args = ParseArgs()
-
-  multidex_enabled = (
-      args.enable_multidex
-      and (not args.enabled_configurations
-           or args.configuration_name in args.enabled_configurations))
-
-  _WriteConfigJson(multidex_enabled, args.multidex_configuration_path)
-  _GenerateMultidexConfigJava(multidex_enabled, args)
-
-  return 0
-
-
-if __name__ == '__main__':
-  sys.exit(main())
-
diff --git a/build/android/gyp/create_device_library_links.py b/build/android/gyp/create_device_library_links.py
deleted file mode 100755
index 54203067..0000000
--- a/build/android/gyp/create_device_library_links.py
+++ /dev/null
@@ -1,121 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2013 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.
-
-"""Creates symlinks to native libraries for an APK.
-
-The native libraries should have previously been pushed to the device (in
-options.target_dir). This script then creates links in an apk's lib/ folder to
-those native libraries.
-"""
-
-import optparse
-import os
-import sys
-
-from util import build_device
-from util import build_utils
-
-BUILD_ANDROID_DIR = os.path.abspath(
-    os.path.join(os.path.dirname(__file__), '..'))
-sys.path.append(BUILD_ANDROID_DIR)
-
-import devil_chromium
-from devil.android import apk_helper
-from pylib import constants
-
-def RunShellCommand(device, cmd):
-  output = device.RunShellCommand(cmd, check_return=True)
-
-  if output:
-    raise Exception(
-        'Unexpected output running command: ' + cmd + '\n' +
-        '\n'.join(output))
-
-
-def CreateSymlinkScript(options):
-  libraries = build_utils.ParseGnList(options.libraries)
-
-  link_cmd = (
-      'rm $APK_LIBRARIES_DIR/%(lib_basename)s > /dev/null 2>&1 \n'
-      'ln -s $STRIPPED_LIBRARIES_DIR/%(lib_basename)s '
-        '$APK_LIBRARIES_DIR/%(lib_basename)s \n'
-      )
-
-  script = '#!/bin/sh \n'
-
-  for lib in libraries:
-    script += link_cmd % { 'lib_basename': lib }
-
-  with open(options.script_host_path, 'w') as scriptfile:
-    scriptfile.write(script)
-
-
-def TriggerSymlinkScript(options):
-  device = build_device.GetBuildDeviceFromPath(
-      options.build_device_configuration)
-  if not device:
-    return
-
-  apk_package = apk_helper.GetPackageName(options.apk)
-  apk_libraries_dir = '/data/data/%s/lib' % apk_package
-
-  device_dir = os.path.dirname(options.script_device_path)
-  mkdir_cmd = ('if [ ! -e %(dir)s ]; then mkdir -p %(dir)s; fi ' %
-      { 'dir': device_dir })
-  RunShellCommand(device, mkdir_cmd)
-  device.PushChangedFiles([(os.path.abspath(options.script_host_path),
-                            options.script_device_path)])
-
-  trigger_cmd = (
-      'APK_LIBRARIES_DIR=%(apk_libraries_dir)s; '
-      'STRIPPED_LIBRARIES_DIR=%(target_dir)s; '
-      '. %(script_device_path)s'
-      ) % {
-          'apk_libraries_dir': apk_libraries_dir,
-          'target_dir': options.target_dir,
-          'script_device_path': options.script_device_path
-          }
-  RunShellCommand(device, trigger_cmd)
-
-
-def main(args):
-  args = build_utils.ExpandFileArgs(args)
-  parser = optparse.OptionParser()
-  parser.add_option('--apk', help='Path to the apk.')
-  parser.add_option('--script-host-path',
-      help='Path on the host for the symlink script.')
-  parser.add_option('--script-device-path',
-      help='Path on the device to push the created symlink script.')
-  parser.add_option('--libraries',
-      help='List of native libraries.')
-  parser.add_option('--target-dir',
-      help='Device directory that contains the target libraries for symlinks.')
-  parser.add_option('--stamp', help='Path to touch on success.')
-  parser.add_option('--build-device-configuration',
-      help='Path to build device configuration.')
-  parser.add_option('--configuration-name',
-      help='The build CONFIGURATION_NAME')
-  parser.add_option('--output-directory',
-      help='The output directory')
-  options, _ = parser.parse_args(args)
-
-  required_options = ['apk', 'libraries', 'script_host_path',
-      'script_device_path', 'target_dir', 'configuration_name']
-  build_utils.CheckOptions(options, parser, required=required_options)
-  constants.SetBuildType(options.configuration_name)
-
-  devil_chromium.Initialize(
-      output_directory=os.path.abspath(options.output_directory))
-
-  CreateSymlinkScript(options)
-  TriggerSymlinkScript(options)
-
-  if options.stamp:
-    build_utils.Touch(options.stamp)
-
-
-if __name__ == '__main__':
-  sys.exit(main(sys.argv[1:]))
diff --git a/build/android/gyp/create_placeholder_files.py b/build/android/gyp/create_placeholder_files.py
deleted file mode 100755
index 103e1df..0000000
--- a/build/android/gyp/create_placeholder_files.py
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/env python
-# 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.
-
-"""Create placeholder files.
-"""
-
-import optparse
-import os
-import sys
-
-from util import build_utils
-
-def main():
-  parser = optparse.OptionParser()
-  parser.add_option(
-      '--dest-lib-dir',
-      help='Destination directory to have placeholder files.')
-  parser.add_option(
-      '--stamp',
-      help='Path to touch on success')
-
-  options, args = parser.parse_args()
-
-  for name in args:
-    target_path = os.path.join(options.dest_lib_dir, name)
-    build_utils.Touch(target_path)
-
-  if options.stamp:
-    build_utils.Touch(options.stamp)
-
-if __name__ == '__main__':
-  sys.exit(main())
-
diff --git a/build/android/gyp/create_standalone_apk.py b/build/android/gyp/create_standalone_apk.py
deleted file mode 100755
index c560599..0000000
--- a/build/android/gyp/create_standalone_apk.py
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2013 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.
-
-"""Combines stripped libraries and incomplete APK into single standalone APK.
-
-"""
-
-import optparse
-import os
-import shutil
-import sys
-import tempfile
-
-from util import build_utils
-from util import md5_check
-
-def CreateStandaloneApk(options):
-  def DoZip():
-    with tempfile.NamedTemporaryFile(suffix='.zip') as intermediate_file:
-      intermediate_path = intermediate_file.name
-      shutil.copy(options.input_apk_path, intermediate_path)
-      apk_path_abs = os.path.abspath(intermediate_path)
-      build_utils.CheckOutput(
-          ['zip', '-r', '-1', apk_path_abs, 'lib'],
-          cwd=options.libraries_top_dir)
-      shutil.copy(intermediate_path, options.output_apk_path)
-
-  input_paths = [options.input_apk_path, options.libraries_top_dir]
-  record_path = '%s.standalone.stamp' % options.input_apk_path
-  md5_check.CallAndRecordIfStale(
-      DoZip,
-      record_path=record_path,
-      input_paths=input_paths)
-
-
-def main():
-  parser = optparse.OptionParser()
-  parser.add_option('--libraries-top-dir',
-      help='Top directory that contains libraries '
-      '(i.e. library paths are like '
-      'libraries_top_dir/lib/android_app_abi/foo.so).')
-  parser.add_option('--input-apk-path', help='Path to incomplete APK.')
-  parser.add_option('--output-apk-path', help='Path for standalone APK.')
-  parser.add_option('--stamp', help='Path to touch on success.')
-  options, _ = parser.parse_args()
-
-  required_options = ['libraries_top_dir', 'input_apk_path', 'output_apk_path']
-  build_utils.CheckOptions(options, parser, required=required_options)
-
-  CreateStandaloneApk(options)
-
-  if options.stamp:
-    build_utils.Touch(options.stamp)
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/build/android/gyp/generate_copy_ex_outputs.py b/build/android/gyp/generate_copy_ex_outputs.py
deleted file mode 100755
index e425b4a6..0000000
--- a/build/android/gyp/generate_copy_ex_outputs.py
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (c) 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# Generate outputs according source files and destination path for
-# copy_ex.gypi
-
-import argparse
-import os
-import sys
-
-def DoMain(argv):
-  parser = argparse.ArgumentParser(prog='generate_copy_ex_outputs')
-  parser.add_argument('--src-files',
-                      nargs = '+',
-                      help = 'a list of files to copy')
-  parser.add_argument('--dest-path',
-                      required = True,
-                      help = 'the directory to copy file to')
-  options = parser.parse_args(argv)
-  # Quote each element so filename spaces don't mess up gyp's attempt to parse
-  # it into a list.
-  return ' '.join('"%s"' % os.path.join(options.dest_path,
-                                        os.path.basename(src))
-                  for src in options.src_files)
-
-if __name__ == '__main__':
-  results = DoMain(sys.argv[1:])
-  if results:
-    print results
-
diff --git a/build/android/gyp/get_device_configuration.py b/build/android/gyp/get_device_configuration.py
deleted file mode 100755
index 0ec08ef9..0000000
--- a/build/android/gyp/get_device_configuration.py
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2013 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.
-
-"""Gets and writes the configurations of the attached devices.
-
-This configuration is used by later build steps to determine which devices to
-install to and what needs to be installed to those devices.
-"""
-
-import optparse
-import os
-import sys
-
-from util import build_device
-from util import build_utils
-
-BUILD_ANDROID_DIR = os.path.abspath(
-    os.path.join(os.path.dirname(__file__), '..'))
-sys.path.append(BUILD_ANDROID_DIR)
-
-import devil_chromium
-
-
-def main(argv):
-  parser = optparse.OptionParser()
-  parser.add_option('--stamp', action='store')
-  parser.add_option('--output', action='store')
-  parser.add_option('--output-directory', action='store')
-  options, _ = parser.parse_args(argv)
-
-  devil_chromium.Initialize(
-      output_directory=os.path.abspath(options.output_directory))
-
-  devices = build_device.GetAttachedDevices()
-
-  device_configurations = []
-  for d in devices:
-    configuration, is_online, has_root = (
-        build_device.GetConfigurationForDevice(d))
-
-    if not is_online:
-      build_utils.PrintBigWarning(
-          '%s is not online. Skipping managed install for this device. '
-          'Try rebooting the device to fix this warning.' % d)
-      continue
-
-    if not has_root:
-      build_utils.PrintBigWarning(
-          '"adb root" failed on device: %s\n'
-          'Skipping managed install for this device.'
-          % configuration['description'])
-      continue
-
-    device_configurations.append(configuration)
-
-  if len(device_configurations) == 0:
-    build_utils.PrintBigWarning(
-        'No valid devices attached. Skipping managed install steps.')
-  elif len(devices) > 1:
-    # Note that this checks len(devices) and not len(device_configurations).
-    # This way, any time there are multiple devices attached it is
-    # explicitly stated which device we will install things to even if all but
-    # one device were rejected for other reasons (e.g. two devices attached,
-    # one w/o root).
-    build_utils.PrintBigWarning(
-        'Multiple devices attached. '
-        'Installing to the preferred device: '
-        '%(id)s (%(description)s)' % (device_configurations[0]))
-
-
-  build_device.WriteConfigurations(device_configurations, options.output)
-
-
-if __name__ == '__main__':
-  sys.exit(main(sys.argv))
diff --git a/build/android/gyp/jar_toc.py b/build/android/gyp/jar_toc.py
deleted file mode 100755
index 540a3439..0000000
--- a/build/android/gyp/jar_toc.py
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2013 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.
-
-"""Creates a TOC file from a Java jar.
-
-The TOC file contains the non-package API of the jar. This includes all
-public/protected/package classes/functions/members and the values of static
-final variables (members with package access are kept because in some cases we
-have multiple libraries with the same package, particularly test+non-test). Some
-other information (major/minor javac version) is also included.
-
-This TOC file then can be used to determine if a dependent library should be
-rebuilt when this jar changes. I.e. any change to the jar that would require a
-rebuild, will have a corresponding change in the TOC file.
-"""
-
-import optparse
-import os
-import re
-import sys
-import zipfile
-
-from util import build_utils
-from util import md5_check
-
-
-def GetClassesInZipFile(zip_file):
-  classes = []
-  files = zip_file.namelist()
-  for f in files:
-    if f.endswith('.class'):
-      # f is of the form org/chromium/base/Class$Inner.class
-      classes.append(f.replace('/', '.')[:-6])
-  return classes
-
-
-def CallJavap(classpath, classes):
-  javap_cmd = [
-      'javap',
-      '-package',  # Show public/protected/package.
-      # -verbose is required to get constant values (which can be inlined in
-      # dependents).
-      '-verbose',
-      '-J-XX:NewSize=4m',
-      '-classpath', classpath
-      ] + classes
-  return build_utils.CheckOutput(javap_cmd)
-
-
-def ExtractToc(disassembled_classes):
-  # javap output is structured by indent (2-space) levels.
-  good_patterns = [
-      '^[^ ]', # This includes all class signatures.
-      '^  SourceFile:',
-      '^  minor version:',
-      '^  major version:',
-      '^  Constant value:',
-      '^  public ',
-      '^  protected ',
-      ]
-  bad_patterns = [
-      '^const #', # Matches the constant pool (i.e. literals used in the class).
-    ]
-
-  def JavapFilter(line):
-    return (re.match('|'.join(good_patterns), line) and
-        not re.match('|'.join(bad_patterns), line))
-  toc = filter(JavapFilter, disassembled_classes.split('\n'))
-
-  return '\n'.join(toc)
-
-
-def UpdateToc(jar_path, toc_path):
-  classes = GetClassesInZipFile(zipfile.ZipFile(jar_path))
-  toc = ''
-  if len(classes) != 0:
-    javap_output = CallJavap(classpath=jar_path, classes=classes)
-    toc = ExtractToc(javap_output)
-
-  with open(toc_path, 'w') as tocfile:
-    tocfile.write(toc)
-
-
-def DoJarToc(options):
-  jar_path = options.jar_path
-  toc_path = options.toc_path
-  record_path = '%s.md5.stamp' % toc_path
-  md5_check.CallAndRecordIfStale(
-      lambda: UpdateToc(jar_path, toc_path),
-      record_path=record_path,
-      input_paths=[jar_path],
-      force=not os.path.exists(toc_path),
-      )
-  build_utils.Touch(toc_path, fail_if_missing=True)
-
-
-def main():
-  parser = optparse.OptionParser()
-  build_utils.AddDepfileOption(parser)
-
-  parser.add_option('--jar-path', help='Input .jar path.')
-  parser.add_option('--toc-path', help='Output .jar.TOC path.')
-  parser.add_option('--stamp', help='Path to touch on success.')
-
-  options, _ = parser.parse_args()
-
-  DoJarToc(options)
-
-  if options.depfile:
-    build_utils.WriteDepfile(options.depfile, options.toc_path)
-
-  if options.stamp:
-    build_utils.Touch(options.stamp)
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/build/android/gyp/push_libraries.py b/build/android/gyp/push_libraries.py
deleted file mode 100755
index 1a64f3d..0000000
--- a/build/android/gyp/push_libraries.py
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2013 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.
-
-"""Pushes native libraries to a device.
-
-"""
-
-import optparse
-import os
-import sys
-
-from util import build_device
-from util import build_utils
-from util import md5_check
-
-BUILD_ANDROID_DIR = os.path.abspath(
-    os.path.join(os.path.dirname(__file__), os.pardir))
-sys.path.append(BUILD_ANDROID_DIR)
-
-import devil_chromium
-from pylib import constants
-
-def DoPush(options):
-  libraries = build_utils.ParseGnList(options.libraries)
-
-  device = build_device.GetBuildDeviceFromPath(
-      options.build_device_configuration)
-  if not device:
-    return
-
-  serial_number = device.GetSerialNumber()
-  # A list so that it is modifiable in Push below.
-  needs_directory = [True]
-  for lib in libraries:
-    device_path = os.path.join(options.device_dir, lib)
-    host_path = os.path.join(options.libraries_dir, lib)
-
-    def Push():
-      if needs_directory:
-        device.RunShellCommand(
-            ['mkdir', '-p', options.device_dir], check_return=True)
-        needs_directory[:] = [] # = False
-      device.PushChangedFiles([(os.path.abspath(host_path), device_path)])
-
-    record_path = '%s.%s.push.md5.stamp' % (host_path, serial_number)
-    md5_check.CallAndRecordIfStale(
-        Push,
-        record_path=record_path,
-        input_paths=[host_path],
-        input_strings=[device_path])
-
-
-def main(args):
-  args = build_utils.ExpandFileArgs(args)
-  parser = optparse.OptionParser()
-  parser.add_option('--libraries-dir',
-      help='Directory that contains stripped libraries.')
-  parser.add_option('--device-dir',
-      help='Device directory to push the libraries to.')
-  parser.add_option('--libraries',
-      help='List of native libraries.')
-  parser.add_option('--stamp', help='Path to touch on success.')
-  parser.add_option('--build-device-configuration',
-      help='Path to build device configuration.')
-  parser.add_option('--output-directory',
-      help='The output directory.')
-  options, _ = parser.parse_args(args)
-
-  required_options = ['libraries', 'device_dir', 'libraries']
-  build_utils.CheckOptions(options, parser, required=required_options)
-
-  devil_chromium.Initialize(
-      output_directory=os.path.abspath(options.output_directory))
-
-  DoPush(options)
-
-  if options.stamp:
-    build_utils.Touch(options.stamp)
-
-
-if __name__ == '__main__':
-  sys.exit(main(sys.argv[1:]))
diff --git a/build/android/gyp/touch.py b/build/android/gyp/touch.py
deleted file mode 100755
index 7b4375e..0000000
--- a/build/android/gyp/touch.py
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-
-import sys
-
-from util import build_utils
-
-def main(argv):
-  for f in argv[1:]:
-    build_utils.Touch(f)
-
-if __name__ == '__main__':
-  sys.exit(main(sys.argv))
diff --git a/build/android/gyp/util/build_device.py b/build/android/gyp/util/build_device.py
deleted file mode 100644
index 6a703c6..0000000
--- a/build/android/gyp/util/build_device.py
+++ /dev/null
@@ -1,102 +0,0 @@
-# Copyright 2013 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.
-
-""" A simple device interface for build steps.
-
-"""
-
-import logging
-import os
-import re
-import sys
-
-from util import build_utils
-
-from devil.android import device_errors
-from devil.android import device_utils
-from devil.android.sdk import adb_wrapper
-
-
-def GetAttachedDevices():
-  return [a.GetDeviceSerial()
-          for a in adb_wrapper.AdbWrapper.Devices()]
-
-
-class BuildDevice(object):
-  def __init__(self, configuration):
-    self.id = configuration['id']
-    self.description = configuration['description']
-    self.install_metadata = configuration['install_metadata']
-    assert all(isinstance(entry, dict) for entry in self.install_metadata), (
-        'Invalid BuildDevice configuration')
-    self.device = device_utils.DeviceUtils(self.id)
-
-  def RunShellCommand(self, *args, **kwargs):
-    return self.device.RunShellCommand(*args, **kwargs)
-
-  def PushChangedFiles(self, *args, **kwargs):
-    return self.device.PushChangedFiles(*args, **kwargs)
-
-  def GetSerialNumber(self):
-    return self.id
-
-  def Install(self, *args, **kwargs):
-    return self.device.Install(*args, **kwargs)
-
-  def InstallSplitApk(self, *args, **kwargs):
-    return self.device.InstallSplitApk(*args, **kwargs)
-
-  def GetInstallMetadata(self, apk_package, refresh=False):
-    """Gets the metadata on the device for a given apk.
-
-    Args:
-      apk_package: A string with the package name for which to get metadata.
-      refresh: A boolean indicating whether to re-read package metadata from
-        the device, or use the values from the current configuration.
-    """
-    if refresh:
-      self.install_metadata = self.device.StatDirectory(
-          '/data/app/', as_root=True)
-    # Matches names like: org.chromium.chrome.apk, org.chromium.chrome-1.apk
-    apk_pattern = re.compile('%s(-[0-9]*)?(.apk)?$' % re.escape(apk_package))
-    return next(
-        (entry for entry in self.install_metadata
-         if apk_pattern.match(entry['filename'])),
-        None)
-
-
-def GetConfigurationForDevice(device_id):
-  device = device_utils.DeviceUtils(device_id)
-  configuration = None
-  has_root = False
-  is_online = device.IsOnline()
-  if is_online:
-    has_root = device.HasRoot()
-    configuration = {
-        'id': device_id,
-        'description': device.build_description,
-        'install_metadata': device.StatDirectory('/data/app/', as_root=True),
-      }
-  return configuration, is_online, has_root
-
-
-def WriteConfigurations(configurations, path):
-  # Currently we only support installing to the first device.
-  build_utils.WriteJson(configurations[:1], path, only_if_changed=True)
-
-
-def ReadConfigurations(path):
-  return build_utils.ReadJson(path)
-
-
-def GetBuildDevice(configurations):
-  assert len(configurations) == 1
-  return BuildDevice(configurations[0])
-
-
-def GetBuildDeviceFromPath(path):
-  configurations = ReadConfigurations(path)
-  if len(configurations) > 0:
-    return GetBuildDevice(ReadConfigurations(path))
-  return None
diff --git a/build/android/gyp/zip.py b/build/android/gyp/zip.py
deleted file mode 100755
index 51322dfd..0000000
--- a/build/android/gyp/zip.py
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-
-"""Archives a set of files.
-"""
-
-import optparse
-import sys
-
-from util import build_utils
-
-def main():
-  parser = optparse.OptionParser()
-  parser.add_option('--input-dir', help='Directory of files to archive.')
-  parser.add_option('--output', help='Path to output archive.')
-  options, _ = parser.parse_args()
-
-  inputs = build_utils.FindInDirectory(options.input_dir, '*')
-  build_utils.DoZip(inputs, options.output, options.input_dir)
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/build/config/fuchsia/rules.gni b/build/config/fuchsia/rules.gni
index b3e7f3b..2b6f99d 100644
--- a/build/config/fuchsia/rules.gni
+++ b/build/config/fuchsia/rules.gni
@@ -4,12 +4,7 @@
 
 assert(is_fuchsia)
 
-# This template is used to generate a runner script for test binaries into the
-# build dir for Fuchsia. It's generally used from the "test" template.
-template("test_runner_script") {
-  testonly = true
-  _test_name = invoker.test_name
-
+template("generate_runner_script") {
   # This runtime_deps file is used at runtime and thus cannot go in
   # target_gen_dir.
   _target_dir_name = get_label_info(":$target_name", "dir")
@@ -23,6 +18,7 @@
                              "data_deps",
                              "deps",
                              "public_deps",
+                             "testonly",
                            ])
     write_runtime_deps = _runtime_deps_file
   }
@@ -32,6 +28,9 @@
                            [
                              "data_deps",
                              "deps",
+                             "runner_script",
+                             "target",
+                             "testonly",
                            ])
     if (!defined(deps)) {
       deps = []
@@ -40,39 +39,67 @@
       data_deps = []
     }
 
-    script = "//build/fuchsia/create_test_runner_script.py"
+    script = "//build/fuchsia/create_runner_script.py"
     depfile = "$target_gen_dir/$target_name.d"
 
     data = []
-    test_runner_args = []
+    runner_args = []
 
-    generated_script = "$root_build_dir/bin/run_${_test_name}"
+    generated_script = "$root_build_dir/bin/run_${target}"
     outputs = [
       generated_script,
     ]
     data += [ generated_script ]
 
-    test_runner_args += [
+    runner_args += [
+      "--runner-script",
+      runner_script,
       "--output-directory",
       rebase_path(root_build_dir, root_build_dir),
     ]
 
     deps += [ ":$_runtime_deps_target" ]
     data += [ _runtime_deps_file ]
-    test_runner_args += [
+    runner_args += [
       "--runtime-deps-path",
       rebase_path(_runtime_deps_file, root_build_dir),
     ]
 
+    if (defined(args)) {
+      args = []
+    }
     args = [
       "--depfile",
       rebase_path(depfile, root_build_dir),
       "--script-output-path",
       rebase_path(generated_script, root_build_dir),
-      "--test-name",
-      _test_name,
+      "--exe-name",
+      target,
     ]
 
-    args += test_runner_args
+    args += runner_args
+  }
+}
+
+# This template is used to generate a runner script for test binaries into the
+# build dir for Fuchsia. It's generally used from the "test" template.
+template("test_runner_script") {
+  generate_runner_script(target_name) {
+    testonly = true
+    runner_script = "test_runner.py"
+    target = invoker.test_name
+    forward_variables_from(invoker, "*")
+  }
+}
+
+# This template is used to generate a runner script for arbitrary executables
+# into the build dir for Fuchsia. The template should be instantiated alongside
+# an "executable" target, and referenced from the executable via its "deps"
+# attribute.
+template("fuchsia_executable_runner") {
+  generate_runner_script(target_name) {
+    runner_script = "exe_runner.py"
+    target = invoker.exe_name
+    forward_variables_from(invoker, "*")
   }
 }
diff --git a/build/fuchsia/create_test_runner_script.py b/build/fuchsia/create_runner_script.py
similarity index 62%
rename from build/fuchsia/create_test_runner_script.py
rename to build/fuchsia/create_runner_script.py
index c85e7ce..f50064b 100755
--- a/build/fuchsia/create_test_runner_script.py
+++ b/build/fuchsia/create_runner_script.py
@@ -4,8 +4,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Creates a script to run a Fushsia test (typically on QEMU) by delegating to
-build/fuchsia/test_runner.py.
+"""Creates a script to run a Fushsia executable by delegating to
+build/fuchsia/(exe|test)_runner.py.
 """
 
 import argparse
@@ -17,7 +17,7 @@
 SCRIPT_TEMPLATE = """\
 #!/usr/bin/env python
 #
-# This file was generated by build/fuchsia/create_test_runner_script.py
+# This file was generated by build/fuchsia/create_runner_script.py
 
 import os
 import sys
@@ -30,14 +30,14 @@
     \"\"\"
     return os.path.abspath(os.path.join(script_directory, path))
 
-  test_runner_path = ResolvePath('{test_runner_path}')
-  test_runner_args = {test_runner_args}
-  test_runner_path_args = {test_runner_path_args}
-  for arg, path in test_runner_path_args:
-    test_runner_args.extend([arg, ResolvePath(path)])
+  runner_path = ResolvePath('{runner_path}')
+  runner_args = {runner_args}
+  runner_path_args = {runner_path_args}
+  for arg, path in runner_path_args:
+    runner_args.extend([arg, ResolvePath(path)])
 
-  os.execv(test_runner_path,
-           [test_runner_path] + test_runner_args + sys.argv[1:])
+  os.execv(runner_path,
+           [runner_path] + runner_args + sys.argv[1:])
 
 if __name__ == '__main__':
   sys.exit(main())
@@ -65,6 +65,8 @@
 
 def main(args):
   parser = argparse.ArgumentParser()
+  parser.add_argument('--runner-script',
+                      help='Name of the runner script to use.')
   parser.add_argument('--script-output-path',
                       help='Output path for executable script.')
   parser.add_argument('--depfile',
@@ -75,33 +77,30 @@
   group = parser.add_argument_group('Test runner path arguments.')
   group.add_argument('--output-directory')
   group.add_argument('--runtime-deps-path')
-  group.add_argument('--test-name')
-  args, test_runner_args = parser.parse_known_args(args)
+  group.add_argument('--exe-name')
+  args, runner_args = parser.parse_known_args(args)
 
   def RelativizePathToScript(path):
     """Returns the path relative to the output script directory."""
     return os.path.relpath(path, os.path.dirname(args.script_output_path))
 
-  test_runner_path = args.test_runner_path or os.path.join(
-      os.path.dirname(__file__), 'test_runner.py')
-  test_runner_path = RelativizePathToScript(test_runner_path)
+  runner_path = args.test_runner_path or os.path.join(
+      os.path.dirname(__file__), args.runner_script)
+  runner_path = RelativizePathToScript(runner_path)
 
-  test_runner_path_args = []
-  if args.output_directory:
-    test_runner_path_args.append(
-        ('--output-directory', RelativizePathToScript(args.output_directory)))
-  if args.runtime_deps_path:
-    test_runner_path_args.append(
-        ('--runtime-deps-path', RelativizePathToScript(args.runtime_deps_path)))
-  if args.test_name:
-    test_runner_path_args.append(
-        ('--test-name', RelativizePathToScript(args.test_name)))
+  runner_path_args = []
+  runner_path_args.append(
+      ('--output-directory', RelativizePathToScript(args.output_directory)))
+  runner_path_args.append(
+      ('--runtime-deps-path', RelativizePathToScript(args.runtime_deps_path)))
+  runner_path_args.append(
+      ('--exe-name', RelativizePathToScript(args.exe_name)))
 
   with open(args.script_output_path, 'w') as script:
     script.write(SCRIPT_TEMPLATE.format(
-        test_runner_path=str(test_runner_path),
-        test_runner_args=str(test_runner_args),
-        test_runner_path_args=str(test_runner_path_args)))
+        runner_path=str(runner_path),
+        runner_args=str(runner_args),
+        runner_path_args=str(runner_path_args)))
 
   os.chmod(args.script_output_path, 0750)
 
diff --git a/build/fuchsia/exe_runner.py b/build/fuchsia/exe_runner.py
new file mode 100755
index 0000000..1eb0fb6
--- /dev/null
+++ b/build/fuchsia/exe_runner.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+#
+# 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.
+
+"""Packages a user.bootfs for a Fuchsia boot image, pulling in the runtime
+dependencies of a binary, and then uses either QEMU from the Fuchsia SDK
+to run, or starts the bootserver to allow running on a hardware device."""
+
+import argparse
+import os
+import sys
+
+from runner_common import RunFuchsia, BuildBootfs, ReadRuntimeDeps
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--dry-run', '-n', action='store_true', default=False,
+                      help='Just print commands, don\'t execute them.')
+  parser.add_argument('--output-directory',
+                      type=os.path.realpath,
+                      help=('Path to the directory in which build files are'
+                            ' located (must include build type).'))
+  parser.add_argument('--runtime-deps-path',
+                      type=os.path.realpath,
+                      help='Runtime data dependency file from GN.')
+  parser.add_argument('--exe-name',
+                      type=os.path.realpath,
+                      help='Name of the the binary executable.')
+  parser.add_argument('-d', '--device', action='store_true', default=False,
+                      help='Run on hardware device instead of QEMU.')
+  args, child_args = parser.parse_known_args()
+
+  bootfs = BuildBootfs(
+      args.output_directory,
+      ReadRuntimeDeps(args.runtime_deps_path, args.output_directory),
+      args.exe_name, child_args, args.device, args.dry_run)
+  if not bootfs:
+    return 2
+
+  return RunFuchsia(bootfs, args.exe_name, args.device, args.dry_run)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/build/fuchsia/runner_common.py b/build/fuchsia/runner_common.py
new file mode 100755
index 0000000..e170f03
--- /dev/null
+++ b/build/fuchsia/runner_common.py
@@ -0,0 +1,327 @@
+#!/usr/bin/env python
+#
+# 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.
+
+"""Packages a user.bootfs for a Fuchsia boot image, pulling in the runtime
+dependencies of a  binary, and then uses either QEMU from the Fuchsia SDK
+to run, or starts the bootserver to allow running on a hardware device."""
+
+import argparse
+import multiprocessing
+import os
+import re
+import shutil
+import signal
+import subprocess
+import sys
+import tempfile
+
+
+DIR_SOURCE_ROOT = os.path.abspath(
+    os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
+SDK_ROOT = os.path.join(DIR_SOURCE_ROOT, 'third_party', 'fuchsia-sdk')
+SYMBOLIZATION_TIMEOUT_SECS = 10
+
+
+def _RunAndCheck(dry_run, args):
+  if dry_run:
+    print 'Run:', ' '.join(args)
+    return 0
+  else:
+    try:
+      subprocess.check_call(args)
+      return 0
+    except subprocess.CalledProcessError as e:
+      return e.returncode
+
+
+def _DumpFile(dry_run, name, description):
+  """Prints out the contents of |name| if |dry_run|."""
+  if not dry_run:
+    return
+  print
+  print 'Contents of %s (for %s)' % (name, description)
+  print '-' * 80
+  with open(name) as f:
+    sys.stdout.write(f.read())
+  print '-' * 80
+
+
+def MakeTargetImageName(common_prefix, output_directory, location):
+  """Generates the relative path name to be used in the file system image.
+  common_prefix: a prefix of both output_directory and location that
+                 be removed.
+  output_directory: an optional prefix on location that will also be removed.
+  location: the file path to relativize.
+
+  .so files will be stored into the lib subdirectory to be able to be found by
+  default by the loader.
+
+  Examples:
+
+  >>> MakeTargetImageName(common_prefix='/work/cr/src/',
+  ...                     output_directory='/work/cr/src/out/fuch',
+  ...                     location='/work/cr/src/base/test/data/xyz.json')
+  'base/test/data/xyz.json'
+
+  >>> MakeTargetImageName(common_prefix='/work/cr/src/',
+  ...                     output_directory='/work/cr/src/out/fuch',
+  ...                     location='/work/cr/src/out/fuch/icudtl.dat')
+  'icudtl.dat'
+
+  >>> MakeTargetImageName(common_prefix='/work/cr/src/',
+  ...                     output_directory='/work/cr/src/out/fuch',
+  ...                     location='/work/cr/src/out/fuch/libbase.so')
+  'lib/libbase.so'
+  """
+  assert output_directory.startswith(common_prefix)
+  output_dir_no_common_prefix = output_directory[len(common_prefix):]
+  assert location.startswith(common_prefix)
+  loc = location[len(common_prefix):]
+  if loc.startswith(output_dir_no_common_prefix):
+    loc = loc[len(output_dir_no_common_prefix)+1:]
+  # TODO(fuchsia): The requirements for finding/loading .so are in flux, so this
+  # ought to be reconsidered at some point. See https://crbug.com/732897.
+  if location.endswith('.so'):
+    loc = 'lib/' + loc
+  return loc
+
+
+def _AddToManifest(manifest_file, target_name, source, mapper):
+  """Appends |source| to the given |manifest_file| (a file object) in a format
+  suitable for consumption by mkbootfs.
+
+  If |source| is a file it's directly added. If |source| is a directory, its
+  contents are recursively added.
+
+  |source| must exist on disk at the time this function is called.
+  """
+  if os.path.isdir(source):
+    files = [os.path.join(dp, f) for dp, dn, fn in os.walk(source) for f in fn]
+    for f in files:
+      # We pass None as the mapper because this should never recurse a 2nd time.
+      _AddToManifest(manifest_file, mapper(f), f, None)
+  elif os.path.exists(source):
+    manifest_file.write('%s=%s\n' % (target_name, source))
+  else:
+    raise Exception('%s does not exist' % source)
+
+
+def ReadRuntimeDeps(deps_path, output_dir):
+  return [os.path.abspath(os.path.join(output_dir, x.strip()))
+          for x in open(deps_path)]
+
+
+def _StripBinary(dry_run, bin_path):
+  strip_path = bin_path + '_stripped';
+  shutil.copyfile(bin_path, strip_path)
+  _RunAndCheck(dry_run, ['/usr/bin/strip', strip_path])
+  return strip_path
+
+
+def BuildBootfs(output_directory, runtime_deps, bin_name, child_args,
+                device, dry_run):
+  locations_to_add = [os.path.abspath(os.path.join(output_directory, x.strip()))
+                      for x in runtime_deps]
+
+  common_prefix = '/'
+  if len(locations_to_add) > 1:
+    common_prefix = os.path.commonprefix(locations_to_add)
+  target_source_pairs = zip(
+      [MakeTargetImageName(common_prefix, output_directory, loc)
+       for loc in locations_to_add],
+      locations_to_add)
+
+  # Stage the stripped binary in the boot image, keeping the original binary's
+  # name for symbolization purposes.
+  bin_path = os.path.abspath(os.path.join(output_directory, bin_name))
+  stripped_bin_path = _StripBinary(dry_run, bin_path)
+  target_source_pairs.append(
+      [MakeTargetImageName(common_prefix, output_directory, bin_path),
+       stripped_bin_path])
+
+  # Generate a script that runs the binaries and shuts down QEMU (if used).
+  autorun_file = tempfile.NamedTemporaryFile()
+  autorun_file.write('#!/bin/sh\n')
+  autorun_file.write('echo Executing ' + os.path.basename(bin_name) + ' ' +
+                     ' '.join(child_args) + '\n')
+  autorun_file.write('/system/' + os.path.basename(bin_name))
+  for arg in child_args:
+    autorun_file.write(' "%s"' % arg);
+  autorun_file.write('\n')
+  autorun_file.write('echo Process terminated.\n')
+
+  if not device:
+    # If shutdown of QEMU happens too soon after the program finishes, log
+    # statements from the end of the run will be lost, so sleep for a bit before
+    # shutting down. When running on device don't power off so the output and
+    # system can be inspected.
+    autorun_file.write('msleep 3000\n')
+    autorun_file.write('dm poweroff\n')
+
+  autorun_file.flush()
+  os.chmod(autorun_file.name, 0750)
+  _DumpFile(dry_run, autorun_file.name, 'autorun')
+  target_source_pairs.append(('autorun', autorun_file.name))
+
+  manifest_file = tempfile.NamedTemporaryFile()
+  bootfs_name = bin_name + '.bootfs'
+
+  for target, source in target_source_pairs:
+    _AddToManifest(manifest_file.file, target, source,
+                   lambda x: MakeTargetImageName(
+                                 common_prefix, output_directory, x))
+
+  mkbootfs_path = os.path.join(SDK_ROOT, 'tools', 'mkbootfs')
+
+  manifest_file.flush()
+  _DumpFile(dry_run, manifest_file.name, 'manifest')
+  if _RunAndCheck(
+      dry_run,
+      [mkbootfs_path, '-o', bootfs_name,
+       '--target=boot', os.path.join(SDK_ROOT, 'bootdata.bin'),
+       '--target=system', manifest_file.name]) != 0:
+    return None
+
+  return bootfs_name
+
+
+def _SymbolizeEntry(entry):
+  addr2line_output = subprocess.check_output(
+      ['addr2line', '-Cipf', '--exe=' + entry[1], entry[2]])
+  prefix = '#%s: ' % entry[0]
+  # addr2line outputs a second line for inlining information, offset
+  # that to align it properly after the frame index.
+  addr2line_filtered = addr2line_output.strip().replace(
+      '(inlined', ' ' * len(prefix) + '(inlined')
+  if '??' in addr2line_filtered:
+    addr2line_filtered = "%s+%s" % (os.path.basename(entry[1]), entry[2])
+  return '%s%s' % (prefix, addr2line_filtered)
+
+
+def _ParallelSymbolizeBacktrace(backtrace):
+  # Disable handling of SIGINT during sub-process creation, to prevent
+  # sub-processes from consuming Ctrl-C signals, rather than the parent
+  # process doing so.
+  saved_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
+  p = multiprocessing.Pool(multiprocessing.cpu_count())
+
+  # Restore the signal handler for the parent process.
+  signal.signal(signal.SIGINT, saved_sigint_handler)
+
+  symbolized = []
+  try:
+    result = p.map_async(_SymbolizeEntry, backtrace)
+    symbolized = result.get(SYMBOLIZATION_TIMEOUT_SECS)
+    if not symbolized:
+      return []
+  except multiprocessing.TimeoutError:
+    return ['(timeout error occurred during symbolization)']
+  except KeyboardInterrupt:  # SIGINT
+    p.terminate()
+
+  return symbolized
+
+
+def RunFuchsia(bootfs, exe_name, use_device, dry_run):
+  kernel_path = os.path.join(SDK_ROOT, 'kernel', 'magenta.bin')
+
+  if use_device:
+    # TODO(fuchsia): This doesn't capture stdout as there's no way to do so
+    # currently. See https://crbug.com/749242.
+    bootserver_path = os.path.join(SDK_ROOT, 'tools', 'bootserver')
+    bootserver_command = [bootserver_path, '-1', kernel_path, bootfs]
+    return _RunAndCheck(dry_run, bootserver_command)
+
+  qemu_path = os.path.join(SDK_ROOT, 'qemu', 'bin', 'qemu-system-x86_64')
+  qemu_command = [qemu_path,
+      '-m', '2048',
+      '-nographic',
+      '-net', 'none',
+      '-smp', '4',
+      '-machine', 'q35',
+      '-kernel', kernel_path,
+      '-initrd', bootfs,
+
+      # Use stdio for the guest OS only; don't attach the QEMU interactive
+      # monitor.
+      '-serial', 'stdio',
+      '-monitor', 'none',
+
+      # TERM=dumb tells the guest OS to not emit ANSI commands that trigger
+      # noisy ANSI spew from the user's terminal emulator.
+      '-append', 'TERM=dumb kernel.halt_on_panic=true']
+
+  if int(os.environ.get('CHROME_HEADLESS', 0)) == 0:
+    qemu_command += ['-enable-kvm', '-cpu', 'host,migratable=no']
+  else:
+    qemu_command += ['-cpu', 'Haswell,+smap,-check']
+
+  if dry_run:
+    print 'Run:', ' '.join(qemu_command)
+    return 0
+
+  # Set up backtrace-parsing regexps.
+  prefix = r'^.*> '
+  bt_end_re = re.compile(prefix + '(bt)?#(\d+):? end')
+  bt_with_offset_re = re.compile(
+      prefix + 'bt#(\d+): pc 0x[0-9a-f]+ sp (0x[0-9a-f]+) ' +
+               '\((\S+),(0x[0-9a-f]+)\)$')
+  in_process_re = re.compile(prefix +
+                             '#(\d+) 0x[0-9a-f]+ \S+\+(0x[0-9a-f]+)$')
+
+  # We pass a separate stdin stream to qemu. Sharing stdin across processes
+  # leads to flakiness due to the OS prematurely killing the stream and the
+  # Python script panicking and aborting.
+  # The precise root cause is still nebulous, but this fix works.
+  # See crbug.com/741194 .
+  qemu_popen = subprocess.Popen(
+      qemu_command, stdout=subprocess.PIPE, stdin=open(os.devnull))
+
+  # A buffer of backtrace entries awaiting symbolization, stored as tuples.
+  # Element #0: backtrace frame number (starting at 0).
+  # Element #1: path to executable code corresponding to the current frame.
+  # Element #2: memory offset within the executable.
+  bt_entries = []
+
+  success = False
+  while True:
+    line = qemu_popen.stdout.readline().strip()
+    if not line:
+      break
+    if 'SUCCESS: all tests passed.' in line:
+      success = True
+
+    # Check for an end-of-backtrace marker.
+    if bt_end_re.match(line):
+      if bt_entries:
+        print '----- start symbolized stack'
+        for processed in _ParallelSymbolizeBacktrace(bt_entries):
+          print processed
+        print '----- end symbolized stack'
+      bt_entries = []
+      continue
+
+    # Try to parse this as a Fuchsia system backtrace.
+    m = bt_with_offset_re.match(line)
+    if m:
+      bt_entries.append((m.group(1), exe_name, m.group(4)))
+      continue
+
+    # Try to parse the line as an in-process backtrace entry.
+    m = in_process_re.match(line)
+    if m:
+      bt_entries.append((m.group(1), exe_name, m.group(2)))
+      continue
+
+    # Some other line, so print it. Back-traces should not be interleaved with
+    # other output, so while this may re-order lines we see, it should actually
+    # make things more readable.
+    print line
+
+  qemu_popen.wait()
+
+  return 0 if success else 1
+
diff --git a/build/fuchsia/test_runner.py b/build/fuchsia/test_runner.py
index 0ba1b4c..a9f72d5a 100755
--- a/build/fuchsia/test_runner.py
+++ b/build/fuchsia/test_runner.py
@@ -9,207 +9,11 @@
 to run, or starts the bootserver to allow running on a hardware device."""
 
 import argparse
-import multiprocessing
 import os
-import re
-import signal
-import subprocess
 import sys
-import tempfile
 
-
-DIR_SOURCE_ROOT = os.path.abspath(
-    os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
-SDK_ROOT = os.path.join(DIR_SOURCE_ROOT, 'third_party', 'fuchsia-sdk')
-SYMBOLIZATION_TIMEOUT_SECS = 10
-
-
-def RunAndCheck(dry_run, args):
-  if dry_run:
-    print 'Run:', args
-  else:
-    try:
-      subprocess.check_call(args)
-      return 0
-    except subprocess.CalledProcessError as e:
-      return e.returncode
-
-
-def DumpFile(dry_run, name, description):
-  """Prints out the contents of |name| if |dry_run|."""
-  if not dry_run:
-    return
-  print
-  print 'Contents of %s (for %s)' % (name, description)
-  print '-' * 80
-  with open(name) as f:
-    sys.stdout.write(f.read())
-  print '-' * 80
-
-
-def MakeTargetImageName(common_prefix, output_directory, location):
-  """Generates the relative path name to be used in the file system image.
-  common_prefix: a prefix of both output_directory and location that
-                 be removed.
-  output_directory: an optional prefix on location that will also be removed.
-  location: the file path to relativize.
-
-  .so files will be stored into the lib subdirectory to be able to be found by
-  default by the loader.
-
-  Examples:
-
-  >>> MakeTargetImageName(common_prefix='/work/cr/src/',
-  ...                     output_directory='/work/cr/src/out/fuch',
-  ...                     location='/work/cr/src/base/test/data/xyz.json')
-  'base/test/data/xyz.json'
-
-  >>> MakeTargetImageName(common_prefix='/work/cr/src/',
-  ...                     output_directory='/work/cr/src/out/fuch',
-  ...                     location='/work/cr/src/out/fuch/icudtl.dat')
-  'icudtl.dat'
-
-  >>> MakeTargetImageName(common_prefix='/work/cr/src/',
-  ...                     output_directory='/work/cr/src/out/fuch',
-  ...                     location='/work/cr/src/out/fuch/libbase.so')
-  'lib/libbase.so'
-  """
-  assert output_directory.startswith(common_prefix)
-  output_dir_no_common_prefix = output_directory[len(common_prefix):]
-  assert location.startswith(common_prefix)
-  loc = location[len(common_prefix):]
-  if loc.startswith(output_dir_no_common_prefix):
-    loc = loc[len(output_dir_no_common_prefix)+1:]
-  # TODO(fuchsia): The requirements for finding/loading .so are in flux, so this
-  # ought to be reconsidered at some point. See https://crbug.com/732897.
-  if location.endswith('.so'):
-    loc = 'lib/' + loc
-  return loc
-
-
-def AddToManifest(manifest_file, target_name, source, mapper):
-  """Appends |source| to the given |manifest_file| (a file object) in a format
-  suitable for consumption by mkbootfs.
-
-  If |source| is a file it's directly added. If |source| is a directory, its
-  contents are recursively added.
-
-  |source| must exist on disk at the time this function is called.
-  """
-  if os.path.isdir(source):
-    files = [os.path.join(dp, f) for dp, dn, fn in os.walk(source) for f in fn]
-    for f in files:
-      # We pass None as the mapper because this should never recurse a 2nd time.
-      AddToManifest(manifest_file, mapper(f), f, None)
-  elif os.path.exists(source):
-    manifest_file.write('%s=%s\n' % (target_name, source))
-  else:
-    raise Exception('%s does not exist' % source)
-
-
-def BuildBootfs(output_directory, runtime_deps_path, test_name, child_args,
-                test_launcher_filter_file, device, dry_run):
-  with open(runtime_deps_path) as f:
-    lines = f.readlines()
-
-  locations_to_add = [os.path.abspath(os.path.join(output_directory, x.strip()))
-                      for x in lines]
-  locations_to_add.append(
-      os.path.abspath(os.path.join(output_directory, test_name)))
-
-  common_prefix = os.path.commonprefix(locations_to_add)
-  target_source_pairs = zip(
-      [MakeTargetImageName(common_prefix, output_directory, loc)
-       for loc in locations_to_add],
-      locations_to_add)
-
-  if test_launcher_filter_file:
-    test_launcher_filter_file = os.path.normpath(
-            os.path.join(output_directory, test_launcher_filter_file))
-    filter_file_on_device = MakeTargetImageName(
-          common_prefix, output_directory, test_launcher_filter_file)
-    child_args.append('--test-launcher-filter-file=/system/' +
-                       filter_file_on_device)
-    target_source_pairs.append(
-        [filter_file_on_device, test_launcher_filter_file])
-
-  # Generate a little script that runs the test binaries and then shuts down
-  # QEMU.
-  autorun_file = tempfile.NamedTemporaryFile()
-  autorun_file.write('#!/bin/sh\n')
-  autorun_file.write('/system/' + os.path.basename(test_name))
-
-  for arg in child_args:
-    autorun_file.write(' "%s"' % arg);
-
-  autorun_file.write('\n')
-  if not device:
-    # If shutdown of QEMU happens too soon after the test completion, log
-    # statements from the end of the run will be lost, so sleep for a bit before
-    # shutting down. When running on device don't power off so the output and
-    # system can be inspected.
-    autorun_file.write('msleep 3000\n')
-    autorun_file.write('dm poweroff\n')
-  autorun_file.flush()
-  os.chmod(autorun_file.name, 0750)
-  DumpFile(dry_run, autorun_file.name, 'autorun')
-  target_source_pairs.append(('autorun', autorun_file.name))
-
-  manifest_file = tempfile.NamedTemporaryFile()
-  bootfs_name = runtime_deps_path + '.bootfs'
-
-  for target, source in target_source_pairs:
-    AddToManifest(manifest_file.file, target, source,
-                  lambda x: MakeTargetImageName(
-                                common_prefix, output_directory, x))
-
-  mkbootfs_path = os.path.join(SDK_ROOT, 'tools', 'mkbootfs')
-
-  manifest_file.flush()
-  DumpFile(dry_run, manifest_file.name, 'manifest')
-  RunAndCheck(dry_run,
-              [mkbootfs_path, '-o', bootfs_name,
-               '--target=boot', os.path.join(SDK_ROOT, 'bootdata.bin'),
-               '--target=system', manifest_file.name,
-              ])
-  return bootfs_name
-
-
-def SymbolizeEntry(entry):
-  addr2line_output = subprocess.check_output(
-      ['addr2line', '-Cipf', '--exe=' + entry[1], entry[2]])
-  prefix = '#%s: ' % entry[0]
-  # addr2line outputs a second line for inlining information, offset
-  # that to align it properly after the frame index.
-  addr2line_filtered = addr2line_output.strip().replace(
-      '(inlined', ' ' * len(prefix) + '(inlined')
-  if '??' in addr2line_filtered:
-    addr2line_filtered = "%s+%s" % (os.path.basename(entry[1]), entry[2])
-  return '%s%s' % (prefix, addr2line_filtered)
-
-
-def ParallelSymbolizeBacktrace(backtrace):
-  # Disable handling of SIGINT during sub-process creation, to prevent
-  # sub-processes from consuming Ctrl-C signals, rather than the parent
-  # process doing so.
-  saved_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
-  p = multiprocessing.Pool(multiprocessing.cpu_count())
-
-  # Restore the signal handler for the parent process.
-  signal.signal(signal.SIGINT, saved_sigint_handler)
-
-  symbolized = []
-  try:
-    result = p.map_async(SymbolizeEntry, backtrace)
-    symbolized = result.get(SYMBOLIZATION_TIMEOUT_SECS)
-    if not symbolized:
-      return []
-  except multiprocessing.TimeoutError:
-    return ['(timeout error occurred during symbolization)']
-  except KeyboardInterrupt:  # SIGINT
-    p.terminate()
-
-  return symbolized
+from runner_common import MakeTargetImageName, RunFuchsia, BuildBootfs, \
+    ReadRuntimeDeps
 
 
 def main():
@@ -223,7 +27,7 @@
   parser.add_argument('--runtime-deps-path',
                       type=os.path.realpath,
                       help='Runtime data dependency file from GN.')
-  parser.add_argument('--test-name',
+  parser.add_argument('--exe-name',
                       type=os.path.realpath,
                       help='Name of the the test')
   parser.add_argument('--gtest_filter',
@@ -277,109 +81,27 @@
   if args.child_args:
     child_args.extend(args.child_args)
 
-  bootfs = BuildBootfs(args.output_directory, args.runtime_deps_path,
-                       args.test_name, child_args,
-                       args.test_launcher_filter_file, args.device,
-                       args.dry_run)
+  runtime_deps = ReadRuntimeDeps(args.runtime_deps_path, args.output_directory)
 
-  kernel_path = os.path.join(SDK_ROOT, 'kernel', 'magenta.bin')
+  if args.test_launcher_filter_file:
+    # Bundle the filter file in the runtime deps and compose the command-line
+    # flag which references it.
+    test_launcher_filter_file = os.path.normpath(
+        os.path.join(args.output_directory, args.test_launcher_filter_file))
+    common_prefix = os.path.commonprefix(runtime_deps)
+    filter_device_path = MakeTargetImageName(common_prefix,
+                                             args.output_directory,
+                                             test_launcher_filter_file)
+    runtime_deps.append(args.test_launcher_filter_file)
+    child_args.append('--test-launcher-filter-file=/system/' +
+                      filter_device_path)
 
-  if args.device:
-    # TODO(fuchsia): This doesn't capture stdout as there's no way to do so
-    # currently. See https://crbug.com/749242.
-    bootserver_path = os.path.join(SDK_ROOT, 'tools', 'bootserver')
-    bootserver_command = [bootserver_path, '-1', kernel_path, bootfs]
-    return RunAndCheck(args.dry_run, bootserver_command)
+  bootfs = BuildBootfs(args.output_directory, runtime_deps, args.exe_name,
+                       child_args, args.device, args.dry_run)
+  if not bootfs:
+    return 2
 
-  qemu_path = os.path.join(SDK_ROOT, 'qemu', 'bin', 'qemu-system-x86_64')
-  qemu_command = [qemu_path,
-      '-m', '2048',
-      '-nographic',
-      '-net', 'none',
-      '-smp', '4',
-      '-machine', 'q35',
-      '-kernel', kernel_path,
-      '-initrd', bootfs,
-
-      # Use stdio for the guest OS only; don't attach the QEMU interactive
-      # monitor.
-      '-serial', 'stdio',
-      '-monitor', 'none',
-
-      # TERM=dumb tells the guest OS to not emit ANSI commands that trigger
-      # noisy ANSI spew from the user's terminal emulator.
-      '-append', 'TERM=dumb kernel.halt_on_panic=true']
-
-  if int(os.environ.get('CHROME_HEADLESS', 0)) == 0:
-    qemu_command += ['-enable-kvm', '-cpu', 'host,migratable=no']
-  else:
-    qemu_command += ['-cpu', 'Haswell,+smap,-check']
-
-  if args.dry_run:
-    print 'Run:', qemu_command
-    return 0
-
-  # Set up backtrace-parsing regexps.
-  prefix = r'^.*> '
-  bt_end_re = re.compile(prefix + '(bt)?#(\d+):? end')
-  bt_with_offset_re = re.compile(
-      prefix + 'bt#(\d+): pc 0x[0-9a-f]+ sp (0x[0-9a-f]+) ' +
-               '\((\S+),(0x[0-9a-f]+)\)$')
-  in_process_re = re.compile(prefix +
-                             '#(\d+) 0x[0-9a-f]+ \S+\+(0x[0-9a-f]+)$')
-
-  # We pass a separate stdin stream to qemu. Sharing stdin across processes
-  # leads to flakiness due to the OS prematurely killing the stream and the
-  # Python script panicking and aborting.
-  # The precise root cause is still nebulous, but this fix works.
-  # See crbug.com/741194 .
-  qemu_popen = subprocess.Popen(
-      qemu_command, stdout=subprocess.PIPE, stdin=open(os.devnull))
-
-  # A buffer of backtrace entries awaiting symbolization, stored as tuples.
-  # Element #0: backtrace frame number (starting at 0).
-  # Element #1: path to executable code corresponding to the current frame.
-  # Element #2: memory offset within the executable.
-  bt_entries = []
-
-  success = False
-  while True:
-    line = qemu_popen.stdout.readline().strip()
-    if not line:
-      break
-    if 'SUCCESS: all tests passed.' in line:
-      success = True
-
-    # Check for an end-of-backtrace marker.
-    if bt_end_re.match(line):
-      if bt_entries:
-        print '----- start symbolized stack'
-        for processed in ParallelSymbolizeBacktrace(bt_entries):
-          print processed
-        print '----- end symbolized stack'
-      bt_entries = []
-      continue
-
-    # Try to parse this as a Fuchsia system backtrace.
-    m = bt_with_offset_re.match(line)
-    if m:
-      bt_entries.append((m.group(1), args.test_name, m.group(4)))
-      continue
-
-    # Try to parse the line as an in-process backtrace entry.
-    m = in_process_re.match(line)
-    if m:
-      bt_entries.append((m.group(1), args.test_name, m.group(2)))
-      continue
-
-    # Some other line, so print it. Back-traces should not be interleaved with
-    # other output, so while this may re-order lines we see, it should actually
-    # make things more readable.
-    print line
-
-  qemu_popen.wait()
-
-  return 0 if success else 1
+  return RunFuchsia(bootfs, args.exe_name, args.device, args.dry_run)
 
 
 if __name__ == '__main__':
diff --git a/cc/blink/web_layer_impl.cc b/cc/blink/web_layer_impl.cc
index c726fe6..fe1a88eb 100644
--- a/cc/blink/web_layer_impl.cc
+++ b/cc/blink/web_layer_impl.cc
@@ -405,10 +405,10 @@
       constraint.scroll_container_relative_sticky_box_rect;
   web_constraint.scroll_container_relative_containing_block_rect =
       constraint.scroll_container_relative_containing_block_rect;
-  web_constraint.nearest_layer_shifting_sticky_box =
-      constraint.nearest_layer_shifting_sticky_box;
-  web_constraint.nearest_layer_shifting_containing_block =
-      constraint.nearest_layer_shifting_containing_block;
+  web_constraint.nearest_element_shifting_sticky_box =
+      constraint.nearest_element_shifting_sticky_box;
+  web_constraint.nearest_element_shifting_containing_block =
+      constraint.nearest_element_shifting_containing_block;
   return web_constraint;
 }
 static cc::LayerStickyPositionConstraint ToStickyPositionConstraint(
@@ -427,10 +427,10 @@
       web_constraint.scroll_container_relative_sticky_box_rect;
   constraint.scroll_container_relative_containing_block_rect =
       web_constraint.scroll_container_relative_containing_block_rect;
-  constraint.nearest_layer_shifting_sticky_box =
-      web_constraint.nearest_layer_shifting_sticky_box;
-  constraint.nearest_layer_shifting_containing_block =
-      web_constraint.nearest_layer_shifting_containing_block;
+  constraint.nearest_element_shifting_sticky_box =
+      web_constraint.nearest_element_shifting_sticky_box;
+  constraint.nearest_element_shifting_containing_block =
+      web_constraint.nearest_element_shifting_containing_block;
   return constraint;
 }
 void WebLayerImpl::SetStickyPositionConstraint(
diff --git a/cc/layers/layer_sticky_position_constraint.cc b/cc/layers/layer_sticky_position_constraint.cc
index 1f80e137..641f5986 100644
--- a/cc/layers/layer_sticky_position_constraint.cc
+++ b/cc/layers/layer_sticky_position_constraint.cc
@@ -18,8 +18,8 @@
       right_offset(0.f),
       top_offset(0.f),
       bottom_offset(0.f),
-      nearest_layer_shifting_sticky_box(Layer::INVALID_ID),
-      nearest_layer_shifting_containing_block(Layer::INVALID_ID) {}
+      nearest_element_shifting_sticky_box(kInvalidElementId),
+      nearest_element_shifting_containing_block(kInvalidElementId) {}
 
 LayerStickyPositionConstraint::LayerStickyPositionConstraint(
     const LayerStickyPositionConstraint& other)
@@ -36,10 +36,10 @@
           other.scroll_container_relative_sticky_box_rect),
       scroll_container_relative_containing_block_rect(
           other.scroll_container_relative_containing_block_rect),
-      nearest_layer_shifting_sticky_box(
-          other.nearest_layer_shifting_sticky_box),
-      nearest_layer_shifting_containing_block(
-          other.nearest_layer_shifting_containing_block) {}
+      nearest_element_shifting_sticky_box(
+          other.nearest_element_shifting_sticky_box),
+      nearest_element_shifting_containing_block(
+          other.nearest_element_shifting_containing_block) {}
 
 bool LayerStickyPositionConstraint::operator==(
     const LayerStickyPositionConstraint& other) const {
@@ -57,10 +57,10 @@
              other.scroll_container_relative_sticky_box_rect &&
          scroll_container_relative_containing_block_rect ==
              other.scroll_container_relative_containing_block_rect &&
-         nearest_layer_shifting_sticky_box ==
-             other.nearest_layer_shifting_sticky_box &&
-         nearest_layer_shifting_containing_block ==
-             other.nearest_layer_shifting_containing_block;
+         nearest_element_shifting_sticky_box ==
+             other.nearest_element_shifting_sticky_box &&
+         nearest_element_shifting_containing_block ==
+             other.nearest_element_shifting_containing_block;
 }
 
 bool LayerStickyPositionConstraint::operator!=(
@@ -68,10 +68,10 @@
   return !(*this == other);
 }
 
-int LayerStickyPositionConstraint::NearestStickyAncestor() {
-  return (nearest_layer_shifting_sticky_box != Layer::INVALID_ID)
-             ? nearest_layer_shifting_sticky_box
-             : nearest_layer_shifting_containing_block;
+ElementId LayerStickyPositionConstraint::NearestStickyAncestor() {
+  return nearest_element_shifting_sticky_box
+             ? nearest_element_shifting_sticky_box
+             : nearest_element_shifting_containing_block;
 }
 
 }  // namespace cc
diff --git a/cc/layers/layer_sticky_position_constraint.h b/cc/layers/layer_sticky_position_constraint.h
index 782fa942..736355e0 100644
--- a/cc/layers/layer_sticky_position_constraint.h
+++ b/cc/layers/layer_sticky_position_constraint.h
@@ -7,6 +7,7 @@
 
 #include "cc/cc_export.h"
 
+#include "cc/trees/element_id.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -40,15 +41,14 @@
   // block boundary.
   gfx::Rect scroll_container_relative_containing_block_rect;
 
-  // The nearest ancestor sticky layer ids that affect the sticky box constraint
-  // rect and the containing block constraint rect respectively. If no such
-  // layer exists, these are set to Layer::INVALID_ID.
-  int nearest_layer_shifting_sticky_box;
-  int nearest_layer_shifting_containing_block;
+  // The nearest ancestor sticky element ids that affect the sticky box
+  // constraint rect and the containing block constraint rect respectively.
+  ElementId nearest_element_shifting_sticky_box;
+  ElementId nearest_element_shifting_containing_block;
 
-  // Returns the nearest sticky ancestor layer, or Layer::INVALID_ID if no such
-  // layer exists.
-  int NearestStickyAncestor();
+  // Returns the nearest sticky ancestor element id or the default element id if
+  // none exists.
+  ElementId NearestStickyAncestor();
 
   bool operator==(const LayerStickyPositionConstraint&) const;
   bool operator!=(const LayerStickyPositionConstraint&) const;
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index 79f77ad..2af1ab53 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -7110,6 +7110,7 @@
   outer_sticky->AddChild(inner_sticky);
   host()->SetRootLayer(root);
   scroller->SetElementId(LayerIdToElementIdForTesting(scroller->id()));
+  outer_sticky->SetElementId(LayerIdToElementIdForTesting(outer_sticky->id()));
 
   root->SetBounds(gfx::Size(100, 100));
   container->SetBounds(gfx::Size(100, 100));
@@ -7138,7 +7139,8 @@
       gfx::Rect(0, 50, 10, 10);
   inner_sticky_pos.scroll_container_relative_containing_block_rect =
       gfx::Rect(0, 50, 10, 50);
-  inner_sticky_pos.nearest_layer_shifting_containing_block = outer_sticky->id();
+  inner_sticky_pos.nearest_element_shifting_containing_block =
+      outer_sticky->element_id();
   inner_sticky->SetStickyPositionConstraint(inner_sticky_pos);
 
   ExecuteCalculateDrawProperties(root.get());
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index 359bb1d..ac367b9 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -284,14 +284,6 @@
   layer->SetClipTreeIndex(data_for_children->clip_tree_parent);
 }
 
-static Layer* LayerById(Layer* layer, int id) {
-  return layer->layer_tree_host()->LayerById(id);
-}
-
-static LayerImpl* LayerById(LayerImpl* layer, int id) {
-  return layer->layer_tree_impl()->LayerById(id);
-}
-
 template <typename LayerType>
 static inline bool IsAtBoundaryOf3dRenderingContext(LayerType* layer) {
   return Parent(layer)
@@ -530,26 +522,24 @@
             .AddNodeAffectedByOuterViewportBoundsDelta(node->id);
       }
     }
-    // Copy the ancestor nodes for later use. These layers are guaranteed to
+    // Copy the ancestor nodes for later use. These elements are guaranteed to
     // have transform nodes at this point because they are our ancestors (so
     // have already been processed) and are sticky (so have transform nodes).
-    int shifting_sticky_box_layer_id =
-        sticky_data->constraints.nearest_layer_shifting_sticky_box;
-    if (shifting_sticky_box_layer_id != Layer::INVALID_ID) {
+    ElementId shifting_sticky_box_element_id =
+        sticky_data->constraints.nearest_element_shifting_sticky_box;
+    if (shifting_sticky_box_element_id) {
       sticky_data->nearest_node_shifting_sticky_box =
-          LayerById(layer, shifting_sticky_box_layer_id)
-              ->transform_tree_index();
-      DCHECK(sticky_data->nearest_node_shifting_sticky_box !=
-             TransformTree::kInvalidNodeId);
+          data_for_children->property_trees->transform_tree
+              .FindNodeFromElementId(shifting_sticky_box_element_id)
+              ->id;
     }
-    int shifting_containing_block_layer_id =
-        sticky_data->constraints.nearest_layer_shifting_containing_block;
-    if (shifting_containing_block_layer_id != Layer::INVALID_ID) {
+    ElementId shifting_containing_block_element_id =
+        sticky_data->constraints.nearest_element_shifting_containing_block;
+    if (shifting_containing_block_element_id) {
       sticky_data->nearest_node_shifting_containing_block =
-          LayerById(layer, shifting_containing_block_layer_id)
-              ->transform_tree_index();
-      DCHECK(sticky_data->nearest_node_shifting_containing_block !=
-             TransformTree::kInvalidNodeId);
+          data_for_children->property_trees->transform_tree
+              .FindNodeFromElementId(shifting_containing_block_element_id)
+              ->id;
     }
   }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
index 5f38661b..94c8e73 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
@@ -568,27 +568,25 @@
     /**
      * Handles the click gesture.
      *
-     * @param time The timestamp of the gesture.
      * @param x The x coordinate of the gesture.
      * @param y The y coordinate of the gesture.
      */
-    public void handleClick(long time, float x, float y) {
+    public void handleClick(float x, float y) {
         mHasDetectedTouchGesture = true;
         if (isCoordinateInsideBasePage(x, y)) {
             closePanel(StateChangeReason.BASE_PAGE_TAP, true);
         } else if (isCoordinateInsideBar(x, y) && !onInterceptBarClick()) {
-            handleBarClick(time, x, y);
+            handleBarClick(x, y);
         }
     }
 
     /**
      * Handles the click gesture specifically on the bar.
      *
-     * @param time The timestamp of the gesture.
      * @param x The x coordinate of the gesture.
      * @param y The y coordinate of the gesture.
      */
-    protected void handleBarClick(long time, float x, float y) {
+    protected void handleBarClick(float x, float y) {
         if (isPeeking()) {
             expandPanel(StateChangeReason.SEARCH_BAR_TAP);
         }
@@ -700,8 +698,7 @@
 
     @Override
     public void click(float x, float y, boolean fromMouse, int buttons) {
-        // TODO(mdjones): The time param for handleClick is not used anywhere, remove it.
-        handleClick(0, x, y);
+        handleClick(x, y);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
index ca02f02..bc4b592 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
@@ -282,7 +282,7 @@
      * Handles a bar click. The position is given in dp.
      */
     @Override
-    public void handleBarClick(long time, float x, float y) {
+    public void handleBarClick(float x, float y) {
         getSearchBarControl().onSearchBarClick(x);
 
         if (isPeeking()) {
@@ -293,7 +293,7 @@
                         mActivity.getActivityTab());
             } else {
                 // super takes care of expanding the Panel when peeking.
-                super.handleBarClick(time, x, y);
+                super.handleBarClick(x, y);
             }
         } else if (isExpanded() || isMaximized()) {
             if (isCoordinateInsideCloseButton(x)) {
@@ -895,6 +895,6 @@
         float yPosition = getOffsetY() + (getHeight() / 2);
 
         // Simulate the tap.
-        handleClick(System.currentTimeMillis(), xPosition, yPosition);
+        handleClick(xPosition, yPosition);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/OverlayPanelEventFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/OverlayPanelEventFilter.java
index 2fab8f4..4bdeac5f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/OverlayPanelEventFilter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/OverlayPanelEventFilter.java
@@ -121,8 +121,7 @@
 
         @Override
         public boolean onSingleTapUp(MotionEvent event) {
-            mPanel.handleClick(event.getEventTime(), event.getX() * mPxToDp,
-                    event.getY() * mPxToDp);
+            mPanel.handleClick(event.getX() * mPxToDp, event.getY() * mPxToDp);
             return true;
         }
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index b701801..005a8e32 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -1114,7 +1114,7 @@
             @Override
             public void run() {
                 // TODO(donnd): provide better time and x,y data to make this more broadly useful.
-                mPanel.handleBarClick(0, 0, 0);
+                mPanel.handleBarClick(0, 0);
             }
         });
     }
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 9ab005c..509d1d97 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -1896,16 +1896,14 @@
   </message>
 
   <!-- Search Page -->
+  <message name="IDS_SETTINGS_SEARCH" desc="Name of the settings page which displays search engine preferences.">
+    Search engine
+  </message>
   <if expr="chromeos">
-    <message name="IDS_SETTINGS_SEARCH" desc="Name of the settings page which displays search engine and assistant preferences on Chrome OS.">
+    <message name="IDS_SETTINGS_SEARCH_AND_ASSISTANT" desc="Name of the settings page which displays search engine and assistant preferences on Chrome OS.">
       Search engine and Google Assistant
     </message>
   </if>
-  <if expr="not chromeos">
-    <message name="IDS_SETTINGS_SEARCH" desc="Name of the settings page which displays search engine preferences.">
-      Search engine
-    </message>
-  </if>
   <message name="IDS_SETTINGS_SEARCH_EXPLANATION" desc="Explanation for the search engine dropdown setting.">
     Search engine used in the <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>address bar<ph name="END_LINK">&lt;/a&gt;</ph>
   </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 8239e60..889fe37 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3354,6 +3354,8 @@
       "obsolete_system/obsolete_system_linux.cc",
       "obsolete_system/obsolete_system_mac.cc",
       "obsolete_system/obsolete_system_win.cc",
+      "page_load_metrics/observers/session_restore_page_load_metrics_observer.cc",
+      "page_load_metrics/observers/session_restore_page_load_metrics_observer.h",
       "pdf/pdf_extension_util.cc",
       "pdf/pdf_extension_util.h",
       "policy/local_sync_policy_handler.cc",
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index fc35719..5bd6cdb7 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -677,7 +677,7 @@
         <include name="IDR_WELCOME_WIN10_PIN_LARGE_WEBP" file="resources\welcome\win10\pin-large.webp" type="BINDATA" />
         <include name="IDR_WELCOME_WIN10_PIN_SMALL_WEBP" file="resources\welcome\win10\pin-small.webp" type="BINDATA" />
       </if>
-      <if expr="not is_android and not is_ios">
+      <if expr="not is_ios">
         <include name="IDR_SSL_ERROR_ASSISTANT_PB" file="${root_gen_dir}/chrome/browser/resources/ssl/ssl_error_assistant/ssl_error_assistant.pb" use_base_dir="false" type="BINDATA" />
       </if>
       <if expr="is_android or is_linux">
diff --git a/chrome/browser/extensions/activity_log/activity_log_unittest.cc b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
index a853633..1e27917f 100644
--- a/chrome/browser/extensions/activity_log/activity_log_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/prerender/prerender_handle.h"
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/prerender/prerender_manager_factory.h"
+#include "chrome/browser/prerender/prerender_test_utils.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
@@ -242,6 +243,9 @@
   ASSERT_TRUE(GetDatabaseEnabled());
   GURL url("http://www.google.com");
 
+  prerender::test_utils::RestorePrerenderMode restore_prerender_mode;
+  prerender::PrerenderManager::SetOmniboxMode(
+      prerender::PrerenderManager::PRERENDER_MODE_ENABLED);
   prerender::PrerenderManager* prerender_manager =
       prerender::PrerenderManagerFactory::GetForBrowserContext(profile());
 
diff --git a/chrome/browser/file_select_helper_unittest.cc b/chrome/browser/file_select_helper_unittest.cc
index 67cc39c2..93443945 100644
--- a/chrome/browser/file_select_helper_unittest.cc
+++ b/chrome/browser/file_select_helper_unittest.cc
@@ -96,10 +96,10 @@
   EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("ascii.txt")),
             FileSelectHelper::GetSanitizedFileName(
                 base::FilePath(FILE_PATH_LITERAL("ascii.txt"))));
-  EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("trailing-spaces-")),
+  EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("trailing-spaces_")),
             FileSelectHelper::GetSanitizedFileName(
                 base::FilePath(FILE_PATH_LITERAL("trailing-spaces "))));
-  EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("path-components-in-name")),
+  EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("path_components_in_name")),
             FileSelectHelper::GetSanitizedFileName(
                 base::FilePath(FILE_PATH_LITERAL("path/components/in/name"))));
 
diff --git a/chrome/browser/offline_pages/prerender_adapter_unittest.cc b/chrome/browser/offline_pages/prerender_adapter_unittest.cc
index 3fa1076..239c78b 100644
--- a/chrome/browser/offline_pages/prerender_adapter_unittest.cc
+++ b/chrome/browser/offline_pages/prerender_adapter_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/sys_info.h"
 #include "chrome/browser/prerender/prerender_manager_factory.h"
+#include "chrome/browser/prerender/prerender_test_utils.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
@@ -116,6 +117,7 @@
   void OnPrerenderNetworkBytesChanged(int64_t bytes) override;
 
   void SetUp() override;
+  void TearDown() override;
 
   // Returns the PrerenderLoader to test.
   PrerenderAdapter* adapter() const { return adapter_.get(); }
@@ -136,6 +138,8 @@
   }
 
  private:
+  using RestorePrerenderMode = prerender::test_utils::RestorePrerenderMode;
+
   content::TestBrowserThreadBundle thread_bundle_;
   TestingProfile profile_;
   std::unique_ptr<PrerenderAdapter> adapter_;
@@ -145,6 +149,7 @@
   bool observer_dom_content_loaded_called_;
   bool observer_stop_called_;
   int64_t observer_network_bytes_changed_;
+  std::unique_ptr<RestorePrerenderMode> restore_prerender_mode_;
 
   DISALLOW_COPY_AND_ASSIGN(PrerenderAdapterTest);
 };
@@ -181,13 +186,18 @@
 void PrerenderAdapterTest::SetUp() {
   if (base::SysInfo::IsLowEndDevice())
     return;
+
+  // Prerender mode is stored in a few static variables. Remember the default
+  // mode to restore it later in TearDown() to avoid affecting other tests.
+  restore_prerender_mode_ = base::MakeUnique<RestorePrerenderMode>();
+  PrerenderManager::SetMode(PrerenderManager::PRERENDER_MODE_ENABLED);
+
   adapter_.reset(new PrerenderAdapter(this));
   prerender_contents_factory_ = new StubPrerenderContentsFactory();
   prerender_manager_ = PrerenderManagerFactory::GetForBrowserContext(profile());
   if (prerender_manager_) {
     prerender_manager_->SetPrerenderContentsFactoryForTest(
         prerender_contents_factory_);
-    prerender_manager_->SetMode(PrerenderManager::PRERENDER_MODE_ENABLED);
   }
   observer_stop_loading_called_ = false;
   observer_dom_content_loaded_called_ = false;
@@ -195,6 +205,10 @@
   ASSERT_TRUE(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
 }
 
+void PrerenderAdapterTest::TearDown() {
+  restore_prerender_mode_.reset();
+}
+
 TEST_F(PrerenderAdapterTest, StartPrerenderFailsForUnsupportedScheme) {
   // Skip test on low end device until supported.
   if (base::SysInfo::IsLowEndDevice())
diff --git a/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.cc
new file mode 100644
index 0000000..c285d6c0
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.cc
@@ -0,0 +1,79 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.h"
+
+#include "base/logging.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
+#include "chrome/browser/resource_coordinator/tab_manager.h"
+#include "chrome/common/page_load_metrics/page_load_metrics.mojom.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents.h"
+
+namespace internal {
+
+const char kHistogramSessionRestoreForegroundTabFirstPaint[] =
+    "TabManager.Experimental.SessionRestore.ForegroundTab.FirstPaint";
+const char kHistogramSessionRestoreForegroundTabFirstContentfulPaint[] =
+    "TabManager.Experimental.SessionRestore.ForegroundTab.FirstContentfulPaint";
+const char kHistogramSessionRestoreForegroundTabFirstMeaningfulPaint[] =
+    "TabManager.Experimental.SessionRestore.ForegroundTab.FirstMeaningfulPaint";
+
+}  // namespace internal
+
+SessionRestorePageLoadMetricsObserver::SessionRestorePageLoadMetricsObserver() {
+}
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+SessionRestorePageLoadMetricsObserver::OnStart(
+    content::NavigationHandle* navigation_handle,
+    const GURL& currently_committed_url,
+    bool started_in_foreground) {
+  content::WebContents* contents = navigation_handle->GetWebContents();
+  resource_coordinator::TabManager* tab_manager =
+      g_browser_process->GetTabManager();
+  // Should not be null because this is used only on supported platforms.
+  DCHECK(tab_manager);
+
+  return (started_in_foreground &&
+          tab_manager->IsTabInSessionRestore(contents) &&
+          tab_manager->IsTabRestoredInForeground(contents))
+             ? CONTINUE_OBSERVING
+             : STOP_OBSERVING;
+}
+
+void SessionRestorePageLoadMetricsObserver::OnFirstPaintInPage(
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+  if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
+          timing.paint_timing->first_paint.value(), extra_info)) {
+    PAGE_LOAD_HISTOGRAM(
+        internal::kHistogramSessionRestoreForegroundTabFirstPaint,
+        timing.paint_timing->first_paint.value());
+  }
+}
+
+void SessionRestorePageLoadMetricsObserver::OnFirstContentfulPaintInPage(
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+  if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
+          timing.paint_timing->first_contentful_paint.value(), extra_info)) {
+    PAGE_LOAD_HISTOGRAM(
+        internal::kHistogramSessionRestoreForegroundTabFirstContentfulPaint,
+        timing.paint_timing->first_contentful_paint.value());
+  }
+}
+
+void SessionRestorePageLoadMetricsObserver::
+    OnFirstMeaningfulPaintInMainFrameDocument(
+        const page_load_metrics::mojom::PageLoadTiming& timing,
+        const page_load_metrics::PageLoadExtraInfo& extra_info) {
+  if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
+          timing.paint_timing->first_meaningful_paint.value(), extra_info)) {
+    PAGE_LOAD_HISTOGRAM(
+        internal::kHistogramSessionRestoreForegroundTabFirstMeaningfulPaint,
+        timing.paint_timing->first_meaningful_paint.value());
+  }
+}
diff --git a/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.h
new file mode 100644
index 0000000..f8c50118
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.h
@@ -0,0 +1,45 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_SESSION_RESTORE_PAGE_LOAD_METRICS_OBSERVER_H_
+#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_SESSION_RESTORE_PAGE_LOAD_METRICS_OBSERVER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
+
+namespace internal {
+
+extern const char kHistogramSessionRestoreForegroundTabFirstPaint[];
+extern const char kHistogramSessionRestoreForegroundTabFirstContentfulPaint[];
+extern const char kHistogramSessionRestoreForegroundTabFirstMeaningfulPaint[];
+
+}  // namespace internal
+
+// Record page load metrics of foreground tabs during session restore. This
+// observer observes foreground tabs created by session restores only. It will
+// stop observing if the tab gets hidden, reloaded, or navigated away.
+class SessionRestorePageLoadMetricsObserver
+    : public page_load_metrics::PageLoadMetricsObserver {
+ public:
+  SessionRestorePageLoadMetricsObserver();
+
+  // page_load_metrics::PageLoadMetricsObserver:
+  ObservePolicy OnStart(content::NavigationHandle* navigation_handle,
+                        const GURL& currently_committed_url,
+                        bool started_in_foreground) override;
+  void OnFirstPaintInPage(
+      const page_load_metrics::mojom::PageLoadTiming& timing,
+      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+  void OnFirstContentfulPaintInPage(
+      const page_load_metrics::mojom::PageLoadTiming& timing,
+      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+  void OnFirstMeaningfulPaintInMainFrameDocument(
+      const page_load_metrics::mojom::PageLoadTiming& timing,
+      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SessionRestorePageLoadMetricsObserver);
+};
+
+#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_SESSION_RESTORE_PAGE_LOAD_METRICS_OBSERVER_H_
diff --git a/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer_unittest.cc
new file mode 100644
index 0000000..724832d4
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer_unittest.cc
@@ -0,0 +1,263 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.h"
+
+#include <memory>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "base/test/histogram_tester.h"
+#include "base/time/time.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.h"
+#include "chrome/browser/page_load_metrics/page_load_tracker.h"
+#include "chrome/browser/resource_coordinator/tab_manager.h"
+#include "chrome/browser/sessions/session_restore.h"
+#include "chrome/browser/sessions/tab_loader.h"
+#include "chrome/common/page_load_metrics/test/page_load_metrics_test_util.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/browser_url_handler.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/navigation_simulator.h"
+#include "content/public/test/web_contents_tester.h"
+#include "ui/base/page_transition_types.h"
+#include "url/gurl.h"
+
+using RestoredTab = SessionRestoreDelegate::RestoredTab;
+using WebContents = content::WebContents;
+
+class SessionRestorePageLoadMetricsObserverTest
+    : public ChromeRenderViewHostTestHarness {
+ public:
+  static void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) {
+    tracker->AddObserver(
+        base::MakeUnique<SessionRestorePageLoadMetricsObserver>());
+  }
+
+ protected:
+  SessionRestorePageLoadMetricsObserverTest() {}
+
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+
+    // Add a default web contents.
+    AddForegroundTabWithTester();
+
+    // Create the tab manager to register its SessionRestoreObserver before
+    // session restore starts.
+    g_browser_process->GetTabManager();
+
+    PopulateFirstPaintTimings();
+  }
+
+  void TearDown() override {
+    // Must be delete tabs before calling TearDown() which cleans up all the
+    // testing environment.
+    tabs_.clear();
+    ChromeRenderViewHostTestHarness::TearDown();
+  }
+
+  // Populate first paint and first [contentful,meaningful] paint timings.
+  void PopulateFirstPaintTimings() {
+    page_load_metrics::InitPageLoadTimingForTest(&timing_);
+    timing_.navigation_start = base::Time::FromDoubleT(1);
+    // Should be large enough (e.g., >20 ms) for some tests to be able to hide
+    // foreground tabs before the first pains.
+    timing_.paint_timing->first_meaningful_paint =
+        base::TimeDelta::FromSeconds(1);
+    PopulateRequiredTimingFields(&timing_);
+  }
+
+  WebContents* AddForegroundTabWithTester() {
+    tabs_.emplace_back(CreateTestWebContents());
+    WebContents* contents = tabs_.back().get();
+    auto tester =
+        base::MakeUnique<page_load_metrics::PageLoadMetricsObserverTester>(
+            contents,
+            base::BindRepeating(
+                &SessionRestorePageLoadMetricsObserverTest::RegisterObservers));
+    testers_[contents] = std::move(tester);
+    restored_tabs_.emplace_back(contents, false /* is_active */,
+                                false /* is_app */, false /* is_pinned */);
+    NavigateAndCommit(contents, GetTestURL());
+    contents->WasShown();
+    StopLoading(contents);
+    return contents;
+  }
+
+  // Return the default tab.
+  WebContents* web_contents() { return tabs_.front().get(); }
+
+  std::vector<std::unique_ptr<WebContents>>& tabs() { return tabs_; }
+
+  void ExpectFirstPaintMetricsTotalCount(int expected_total_count) const {
+    histogram_tester_.ExpectTotalCount(
+        internal::kHistogramSessionRestoreForegroundTabFirstPaint,
+        expected_total_count);
+    histogram_tester_.ExpectTotalCount(
+        internal::kHistogramSessionRestoreForegroundTabFirstContentfulPaint,
+        expected_total_count);
+    histogram_tester_.ExpectTotalCount(
+        internal::kHistogramSessionRestoreForegroundTabFirstMeaningfulPaint,
+        expected_total_count);
+  }
+
+  void RestoreTabs() {
+    TabLoader::RestoreTabs(restored_tabs_, base::TimeTicks());
+  }
+
+  void SimulateTimingUpdateForTab(WebContents* contents) {
+    ASSERT_TRUE(base::ContainsKey(testers_, contents));
+    testers_[contents]->SimulateTimingUpdate(timing_);
+  }
+
+  void StopLoading(WebContents* contents) const {
+    contents->Stop();
+    content::WebContentsTester::For(contents)->TestSetIsLoading(false);
+  }
+
+  void NavigateAndCommit(WebContents* contents, const GURL& url) const {
+    content::NavigationSimulator::NavigateAndCommitFromDocument(
+        GetTestURL(), contents->GetMainFrame());
+  }
+
+  GURL GetTestURL() const { return GURL("https://google.com"); }
+
+  const page_load_metrics::mojom::PageLoadTiming& timing() const {
+    return timing_;
+  }
+
+  std::vector<RestoredTab>& restored_tabs() { return restored_tabs_; }
+
+ private:
+  base::HistogramTester histogram_tester_;
+
+  page_load_metrics::mojom::PageLoadTiming timing_;
+  std::vector<RestoredTab> restored_tabs_;
+  std::vector<std::unique_ptr<WebContents>> tabs_;
+  std::unordered_map<
+      WebContents*,
+      std::unique_ptr<page_load_metrics::PageLoadMetricsObserverTester>>
+      testers_;
+
+  DISALLOW_COPY_AND_ASSIGN(SessionRestorePageLoadMetricsObserverTest);
+};
+
+TEST_F(SessionRestorePageLoadMetricsObserverTest, NoMetrics) {
+  ExpectFirstPaintMetricsTotalCount(0);
+}
+
+TEST_F(SessionRestorePageLoadMetricsObserverTest,
+       FirstPaintsOutOfSessionRestore) {
+  NavigateAndCommit(web_contents(), GetTestURL());
+  ASSERT_NO_FATAL_FAILURE(SimulateTimingUpdateForTab(web_contents()));
+  ExpectFirstPaintMetricsTotalCount(0);
+}
+
+TEST_F(SessionRestorePageLoadMetricsObserverTest, RestoreSingleForegroundTab) {
+  // Restore one tab which finishes loading in foreground.
+  SessionRestore::OnWillRestoreTab(web_contents());
+  RestoreTabs();
+  NavigateAndCommit(web_contents(), GetTestURL());
+  ASSERT_NO_FATAL_FAILURE(SimulateTimingUpdateForTab(web_contents()));
+  ExpectFirstPaintMetricsTotalCount(1);
+}
+
+TEST_F(SessionRestorePageLoadMetricsObserverTest,
+       RestoreMultipleForegroundTabs) {
+  WebContents* second_contents = AddForegroundTabWithTester();
+  // Restore each tab separately.
+  std::vector<std::vector<RestoredTab>> restored_tabs_list{
+      std::vector<RestoredTab>{
+          RestoredTab(web_contents(), false, false, false)},
+      std::vector<RestoredTab>{
+          RestoredTab(second_contents, false, false, false)}};
+
+  for (size_t i = 0; i < tabs().size(); ++i) {
+    WebContents* contents = tabs()[i].get();
+    SessionRestore::OnWillRestoreTab(contents);
+    TabLoader::RestoreTabs(restored_tabs_list[i], base::TimeTicks());
+    NavigateAndCommit(contents, GetTestURL());
+    ASSERT_NO_FATAL_FAILURE(SimulateTimingUpdateForTab(contents));
+    ExpectFirstPaintMetricsTotalCount(i + 1);
+  }
+}
+
+TEST_F(SessionRestorePageLoadMetricsObserverTest, RestoreBackgroundTab) {
+  // Set the tab to background before the PageLoadMetricsObserver was created.
+  web_contents()->WasHidden();
+
+  // Load the restored tab in background.
+  SessionRestore::OnWillRestoreTab(web_contents());
+  RestoreTabs();
+  NavigateAndCommit(web_contents(), GetTestURL());
+  ASSERT_NO_FATAL_FAILURE(SimulateTimingUpdateForTab(web_contents()));
+
+  // No paint timings recorded for tabs restored in background.
+  ExpectFirstPaintMetricsTotalCount(0);
+}
+
+TEST_F(SessionRestorePageLoadMetricsObserverTest, HideTabBeforeFirstPaints) {
+  // Start loading the tab.
+  SessionRestore::OnWillRestoreTab(web_contents());
+  RestoreTabs();
+  NavigateAndCommit(web_contents(), GetTestURL());
+
+  // Hide the tab before any paints.
+  web_contents()->WasHidden();
+  ASSERT_NO_FATAL_FAILURE(SimulateTimingUpdateForTab(web_contents()));
+
+  // No paint timings recorded because tab was hidden before the first paints.
+  ExpectFirstPaintMetricsTotalCount(0);
+}
+
+TEST_F(SessionRestorePageLoadMetricsObserverTest,
+       SwitchInitialRestoredForegroundTab) {
+  // Create 2 tabs: tab 0 is foreground, tab 1 is background.
+  AddForegroundTabWithTester();
+  restored_tabs()[0].contents()->WasShown();
+  restored_tabs()[1].contents()->WasHidden();
+
+  // Restore both tabs.
+  for (size_t i = 0; i < tabs().size(); ++i)
+    SessionRestore::OnWillRestoreTab(tabs()[i].get());
+  TabLoader::RestoreTabs(restored_tabs(), base::TimeTicks());
+
+  for (size_t i = 0; i < tabs().size(); ++i)
+    NavigateAndCommit(tabs()[i].get(), GetTestURL());
+
+  // Switch to tab 1 before any paint events occur.
+  restored_tabs()[0].contents()->WasHidden();
+  restored_tabs()[1].contents()->WasShown();
+  ASSERT_NO_FATAL_FAILURE(SimulateTimingUpdateForTab(web_contents()));
+
+  // No paint timings recorded because the initial foreground tab was hidden.
+  ExpectFirstPaintMetricsTotalCount(0);
+}
+
+TEST_F(SessionRestorePageLoadMetricsObserverTest, MultipleSessionRestores) {
+  size_t number_of_session_restores = 3;
+  for (size_t i = 1; i <= number_of_session_restores; ++i) {
+    // Restore session.
+    SessionRestore::OnWillRestoreTab(web_contents());
+    RestoreTabs();
+    NavigateAndCommit(web_contents(), GetTestURL());
+    ASSERT_NO_FATAL_FAILURE(SimulateTimingUpdateForTab(web_contents()));
+
+    // Number of paint timings should match the number of session restores.
+    ExpectFirstPaintMetricsTotalCount(i);
+
+    // Clear committed URL for the next restore starts from an empty URL.
+    NavigateAndCommit(web_contents(), GURL());
+    StopLoading(web_contents());
+  }
+}
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
index d162b0e..7caedaf 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_browsertest.cc
@@ -2,10 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <memory>
+#include <string>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
 #include "base/command_line.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
 #include "base/test/histogram_tester.h"
@@ -19,12 +26,16 @@
 #include "chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/document_write_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/no_state_prefetch_page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_initialize.h"
 #include "chrome/browser/page_load_metrics/page_load_tracker.h"
 #include "chrome/browser/prefs/session_startup_pref.h"
 #include "chrome/browser/prerender/prerender_histograms.h"
 #include "chrome/browser/prerender/prerender_origin.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sessions/session_restore.h"
+#include "chrome/browser/sessions/session_restore_test_helper.h"
 #include "chrome/browser/sessions/session_service_factory.h"
 #include "chrome/browser/sessions/session_service_test_helper.h"
 #include "chrome/browser/ui/browser.h"
@@ -37,11 +48,14 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/prefs/pref_service.h"
+#include "components/sessions/core/serialized_navigation_entry.h"
+#include "components/sessions/core/serialized_navigation_entry_test_helper.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
+#include "content/public/common/referrer.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/download_test_observer.h"
 #include "net/base/net_errors.h"
@@ -53,6 +67,7 @@
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "third_party/WebKit/public/platform/web_feature.mojom.h"
+#include "url/gurl.h"
 
 namespace {
 
@@ -1119,20 +1134,21 @@
   // PageLoadMetricsBrowserTest:
   void SetUpOnMainThread() override {
     PageLoadMetricsBrowserTest::SetUpOnMainThread();
-    SessionStartupPref::SetStartupPref(
-        browser()->profile(), SessionStartupPref(SessionStartupPref::LAST));
     ASSERT_TRUE(embedded_test_server()->Start());
-#if defined(OS_CHROMEOS)
-    SessionServiceTestHelper helper(
-        SessionServiceFactory::GetForProfile(browser()->profile()));
-    helper.SetForceBrowserNotAliveWithNoWindows(true);
-    helper.ReleaseService();
-#endif
   }
 
   Browser* QuitBrowserAndRestore(Browser* browser) {
     Profile* profile = browser->profile();
 
+    SessionStartupPref::SetStartupPref(
+        profile, SessionStartupPref(SessionStartupPref::LAST));
+#if defined(OS_CHROMEOS)
+    SessionServiceTestHelper helper(
+        SessionServiceFactory::GetForProfile(profile));
+    helper.SetForceBrowserNotAliveWithNoWindows(true);
+    helper.ReleaseService();
+#endif
+
     std::unique_ptr<ScopedKeepAlive> keep_alive(new ScopedKeepAlive(
         KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED));
     CloseBrowserSynchronously(browser);
@@ -1140,7 +1156,11 @@
     // Create a new window, which should trigger session restore.
     chrome::NewEmptyWindow(profile);
     ui_test_utils::BrowserAddedObserver window_observer;
-    return window_observer.WaitForSingleNewBrowser();
+    SessionRestoreTestHelper restore_observer;
+
+    Browser* new_browser = window_observer.WaitForSingleNewBrowser();
+    restore_observer.Wait();
+    return new_browser;
   }
 
   void WaitForTabsToLoad(Browser* browser) {
@@ -1148,18 +1168,72 @@
       content::WebContents* contents =
           browser->tab_strip_model()->GetWebContentsAt(i);
       contents->GetController().LoadIfNecessary();
-      content::WaitForLoadStop(contents);
+      ASSERT_TRUE(content::WaitForLoadStop(contents));
     }
   }
 
+  // The PageLoadMetricsWaiter can observe first meaningful paints on these test
+  // pages while not on other simple pages such as /title1.html.
   GURL GetTestURL() const {
-    return embedded_test_server()->GetURL("/title1.html");
+    return embedded_test_server()->GetURL(
+        "/page_load_metrics/main_frame_with_iframe.html");
+  }
+
+  GURL GetTestURL2() const {
+    return embedded_test_server()->GetURL("/title2.html");
+  }
+
+  void ExpectFirstPaintMetricsTotalCount(int expected_total_count) const {
+    histogram_tester_.ExpectTotalCount(
+        internal::kHistogramSessionRestoreForegroundTabFirstPaint,
+        expected_total_count);
+    histogram_tester_.ExpectTotalCount(
+        internal::kHistogramSessionRestoreForegroundTabFirstContentfulPaint,
+        expected_total_count);
+    histogram_tester_.ExpectTotalCount(
+        internal::kHistogramSessionRestoreForegroundTabFirstMeaningfulPaint,
+        expected_total_count);
   }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(SessionRestorePageLoadMetricsBrowserTest);
 };
 
+class SessionRestorePaintWaiter : public SessionRestoreObserver {
+ public:
+  SessionRestorePaintWaiter() { SessionRestore::AddObserver(this); }
+  ~SessionRestorePaintWaiter() { SessionRestore::RemoveObserver(this); }
+
+  // SessionRestoreObserver implementation:
+  void OnWillRestoreTab(content::WebContents* contents) override {
+    chrome::InitializePageLoadMetricsForWebContents(contents);
+    auto waiter = base::MakeUnique<PageLoadMetricsWaiter>(contents);
+    waiter->AddPageExpectation(TimingField::FIRST_PAINT);
+    waiter->AddPageExpectation(TimingField::FIRST_CONTENTFUL_PAINT);
+    waiter->AddPageExpectation(TimingField::FIRST_MEANINGFUL_PAINT);
+    waiters_[contents] = std::move(waiter);
+  }
+
+  // First meaningful paints occur only on foreground tabs.
+  void WaitForForegroundTabs(size_t num_expected_foreground_tabs) {
+    size_t num_actual_foreground_tabs = 0;
+    for (auto iter = waiters_.begin(); iter != waiters_.end(); ++iter) {
+      if (!iter->first->IsVisible())
+        continue;
+      iter->second->Wait();
+      ++num_actual_foreground_tabs;
+    }
+    EXPECT_EQ(num_expected_foreground_tabs, num_actual_foreground_tabs);
+  }
+
+ private:
+  std::unordered_map<content::WebContents*,
+                     std::unique_ptr<PageLoadMetricsWaiter>>
+      waiters_;
+
+  DISALLOW_COPY_AND_ASSIGN(SessionRestorePaintWaiter);
+};
+
 IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
                        InitialVisibilityOfSingleRestoredTab) {
   ui_test_utils::NavigateToURL(browser(), GetTestURL());
@@ -1169,7 +1243,7 @@
       page_load_metrics::internal::kPageLoadStartedInForeground, true, 1);
 
   Browser* new_browser = QuitBrowserAndRestore(browser());
-  WaitForTabsToLoad(new_browser);
+  ASSERT_NO_FATAL_FAILURE(WaitForTabsToLoad(new_browser));
 
   histogram_tester_.ExpectTotalCount(
       page_load_metrics::internal::kPageLoadStartedInForeground, 2);
@@ -1189,7 +1263,7 @@
       page_load_metrics::internal::kPageLoadStartedInForeground, false, 1);
 
   Browser* new_browser = QuitBrowserAndRestore(browser());
-  WaitForTabsToLoad(new_browser);
+  ASSERT_NO_FATAL_FAILURE(WaitForTabsToLoad(new_browser));
 
   TabStripModel* tab_strip = new_browser->tab_strip_model();
   ASSERT_TRUE(tab_strip);
@@ -1202,3 +1276,229 @@
   histogram_tester_.ExpectBucketCount(
       page_load_metrics::internal::kPageLoadStartedInForeground, false, 2);
 }
+
+IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
+                       NoSessionRestore) {
+  ui_test_utils::NavigateToURL(browser(), GetTestURL());
+  ExpectFirstPaintMetricsTotalCount(0);
+}
+
+IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
+                       SingleTabSessionRestore) {
+  ui_test_utils::NavigateToURL(browser(), GetTestURL());
+
+  SessionRestorePaintWaiter session_restore_paint_waiter;
+  QuitBrowserAndRestore(browser());
+
+  session_restore_paint_waiter.WaitForForegroundTabs(1);
+  ExpectFirstPaintMetricsTotalCount(1);
+}
+
+IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
+                       MultipleTabsSessionRestore) {
+  ui_test_utils::NavigateToURL(browser(), GetTestURL());
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GetTestURL(), WindowOpenDisposition::NEW_BACKGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+
+  SessionRestorePaintWaiter session_restore_paint_waiter;
+  Browser* new_browser = QuitBrowserAndRestore(browser());
+
+  TabStripModel* tab_strip = new_browser->tab_strip_model();
+  ASSERT_TRUE(tab_strip);
+  ASSERT_EQ(2, tab_strip->count());
+
+  // Only metrics of the initial foreground tab are recorded.
+  session_restore_paint_waiter.WaitForForegroundTabs(1);
+  ExpectFirstPaintMetricsTotalCount(1);
+}
+
+IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
+                       NavigationDuringSessionRestore) {
+  NavigateToUntrackedUrl();
+  Browser* new_browser = QuitBrowserAndRestore(browser());
+
+  auto waiter = base::MakeUnique<PageLoadMetricsWaiter>(
+      new_browser->tab_strip_model()->GetActiveWebContents());
+  waiter->AddPageExpectation(TimingField::FIRST_MEANINGFUL_PAINT);
+  ui_test_utils::NavigateToURL(new_browser, GetTestURL());
+  waiter->Wait();
+
+  // No metrics recorded for the second navigation because the tab navigated
+  // away during session restore.
+  ExpectFirstPaintMetricsTotalCount(0);
+}
+
+IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
+                       LoadingAfterSessionRestore) {
+  ui_test_utils::NavigateToURL(browser(), GetTestURL());
+
+  Browser* new_browser = nullptr;
+  {
+    SessionRestorePaintWaiter session_restore_paint_waiter;
+    new_browser = QuitBrowserAndRestore(browser());
+
+    session_restore_paint_waiter.WaitForForegroundTabs(1);
+    ExpectFirstPaintMetricsTotalCount(1);
+  }
+
+  // Load a new page after session restore.
+  auto waiter = base::MakeUnique<PageLoadMetricsWaiter>(
+      new_browser->tab_strip_model()->GetActiveWebContents());
+  waiter->AddPageExpectation(TimingField::FIRST_MEANINGFUL_PAINT);
+  ui_test_utils::NavigateToURL(new_browser, GetTestURL());
+  waiter->Wait();
+
+  // No more metrics because the navigation is after session restore.
+  ExpectFirstPaintMetricsTotalCount(1);
+}
+
+IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
+                       InitialForegroundTabChanged) {
+  ui_test_utils::NavigateToURL(browser(), GetTestURL());
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GetTestURL(), WindowOpenDisposition::NEW_BACKGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+
+  SessionRestorePaintWaiter session_restore_paint_waiter;
+  Browser* new_browser = QuitBrowserAndRestore(browser());
+
+  // Change the foreground tab before the first meaningful paint.
+  TabStripModel* tab_strip = new_browser->tab_strip_model();
+  ASSERT_TRUE(tab_strip);
+  ASSERT_EQ(2, tab_strip->count());
+  ASSERT_EQ(0, tab_strip->active_index());
+  tab_strip->ActivateTabAt(1, true);
+
+  session_restore_paint_waiter.WaitForForegroundTabs(1);
+
+  // No metrics were recorded because initial foreground tab was switched away.
+  ExpectFirstPaintMetricsTotalCount(0);
+}
+
+IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
+                       MultipleSessionRestores) {
+  ui_test_utils::NavigateToURL(browser(), GetTestURL());
+
+  Browser* current_browser = browser();
+  const int num_session_restores = 3;
+  for (int i = 1; i <= num_session_restores; ++i) {
+    SessionRestorePaintWaiter session_restore_paint_waiter;
+    current_browser = QuitBrowserAndRestore(current_browser);
+    session_restore_paint_waiter.WaitForForegroundTabs(1);
+    ExpectFirstPaintMetricsTotalCount(i);
+  }
+}
+
+IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
+                       RestoreForeignTab) {
+  sessions::SerializedNavigationEntry nav =
+      sessions::SerializedNavigationEntryTestHelper::CreateNavigation(
+          GetTestURL().spec(), "one");
+
+  // Set up the restore data.
+  sync_pb::SessionTab sync_data;
+  sync_data.set_tab_visual_index(0);
+  sync_data.set_current_navigation_index(1);
+  sync_data.set_pinned(false);
+  sync_data.add_navigation()->CopyFrom(nav.ToSyncData());
+
+  sessions::SessionTab tab;
+  tab.SetFromSyncData(sync_data, base::Time::Now());
+  ASSERT_EQ(1, browser()->tab_strip_model()->count());
+
+  // Restore in the current tab.
+  content::WebContents* tab_contents = nullptr;
+  {
+    SessionRestorePaintWaiter session_restore_paint_waiter;
+    tab_contents = SessionRestore::RestoreForeignSessionTab(
+        browser()->tab_strip_model()->GetActiveWebContents(), tab,
+        WindowOpenDisposition::CURRENT_TAB);
+    ASSERT_EQ(1, browser()->tab_strip_model()->count());
+    ASSERT_TRUE(tab_contents);
+    ASSERT_EQ(GetTestURL(), tab_contents->GetURL());
+
+    session_restore_paint_waiter.WaitForForegroundTabs(1);
+    ExpectFirstPaintMetricsTotalCount(1);
+  }
+
+  // Restore in a new foreground tab.
+  {
+    SessionRestorePaintWaiter session_restore_paint_waiter;
+    tab_contents = SessionRestore::RestoreForeignSessionTab(
+        browser()->tab_strip_model()->GetActiveWebContents(), tab,
+        WindowOpenDisposition::NEW_FOREGROUND_TAB);
+    ASSERT_EQ(2, browser()->tab_strip_model()->count());
+    ASSERT_EQ(1, browser()->tab_strip_model()->active_index());
+    ASSERT_TRUE(tab_contents);
+    ASSERT_EQ(GetTestURL(), tab_contents->GetURL());
+
+    session_restore_paint_waiter.WaitForForegroundTabs(1);
+    ExpectFirstPaintMetricsTotalCount(2);
+  }
+
+  // Restore in a new background tab.
+  {
+    tab_contents = SessionRestore::RestoreForeignSessionTab(
+        browser()->tab_strip_model()->GetActiveWebContents(), tab,
+        WindowOpenDisposition::NEW_BACKGROUND_TAB);
+    ASSERT_EQ(3, browser()->tab_strip_model()->count());
+    ASSERT_EQ(1, browser()->tab_strip_model()->active_index());
+    ASSERT_TRUE(tab_contents);
+    ASSERT_EQ(GetTestURL(), tab_contents->GetURL());
+    ASSERT_NO_FATAL_FAILURE(WaitForTabsToLoad(browser()));
+
+    // Do not record timings of initially background tabs.
+    ExpectFirstPaintMetricsTotalCount(2);
+  }
+}
+
+IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
+                       RestoreForeignSession) {
+  Profile* profile = browser()->profile();
+
+  sessions::SerializedNavigationEntry nav1 =
+      sessions::SerializedNavigationEntryTestHelper::CreateNavigation(
+          GetTestURL().spec(), "one");
+  sessions::SerializedNavigationEntry nav2 =
+      sessions::SerializedNavigationEntryTestHelper::CreateNavigation(
+          GetTestURL2().spec(), "two");
+
+  // Set up the restore data: one window with two tabs.
+  std::vector<const sessions::SessionWindow*> session;
+  sessions::SessionWindow window;
+  auto tab1 = base::MakeUnique<sessions::SessionTab>();
+  {
+    sync_pb::SessionTab sync_data;
+    sync_data.set_tab_visual_index(0);
+    sync_data.set_current_navigation_index(0);
+    sync_data.set_pinned(true);
+    sync_data.add_navigation()->CopyFrom(nav1.ToSyncData());
+    tab1->SetFromSyncData(sync_data, base::Time::Now());
+  }
+  window.tabs.push_back(std::move(tab1));
+
+  auto tab2 = base::MakeUnique<sessions::SessionTab>();
+  {
+    sync_pb::SessionTab sync_data;
+    sync_data.set_tab_visual_index(1);
+    sync_data.set_current_navigation_index(0);
+    sync_data.set_pinned(false);
+    sync_data.add_navigation()->CopyFrom(nav2.ToSyncData());
+    tab2->SetFromSyncData(sync_data, base::Time::Now());
+  }
+  window.tabs.push_back(std::move(tab2));
+
+  // Restore the session window with 2 tabs.
+  session.push_back(static_cast<const sessions::SessionWindow*>(&window));
+  ui_test_utils::BrowserAddedObserver window_observer;
+  SessionRestorePaintWaiter session_restore_paint_waiter;
+  SessionRestore::RestoreForeignSessionWindows(profile, session.begin(),
+                                               session.end());
+  Browser* new_browser = window_observer.WaitForSingleNewBrowser();
+  ASSERT_TRUE(new_browser);
+  ASSERT_EQ(2, new_browser->tab_strip_model()->count());
+
+  session_restore_paint_waiter.WaitForForegroundTabs(1);
+  ExpectFirstPaintMetricsTotalCount(1);
+}
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
index 32b78352..774715f5 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -10,10 +10,8 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/timer/timer.h"
+#include "build/build_config.h"
 #include "chrome/browser/page_load_metrics/metrics_web_contents_observer.h"
-#if defined(OS_ANDROID)
-#include "chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.h"
-#endif  // OS_ANDROID
 #include "chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.h"
@@ -50,6 +48,12 @@
 #include "content/public/browser/web_contents.h"
 #include "url/gurl.h"
 
+#if defined(OS_ANDROID)
+#include "chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.h"
+#else
+#include "chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.h"
+#endif
+
 namespace chrome {
 
 namespace {
@@ -137,6 +141,10 @@
                 web_contents_);
     if (loading_predictor_observer)
       tracker->AddObserver(std::move(loading_predictor_observer));
+#if !defined(OS_ANDROID)
+    tracker->AddObserver(
+        base::MakeUnique<SessionRestorePageLoadMetricsObserver>());
+#endif
     tracker->AddObserver(
         base::MakeUnique<LocalNetworkRequestsPageLoadMetricsObserver>());
   } else {
diff --git a/chrome/browser/predictors/autocomplete_action_predictor_unittest.cc b/chrome/browser/predictors/autocomplete_action_predictor_unittest.cc
index 49cef54..7c9bed4dd 100644
--- a/chrome/browser/predictors/autocomplete_action_predictor_unittest.cc
+++ b/chrome/browser/predictors/autocomplete_action_predictor_unittest.cc
@@ -18,6 +18,8 @@
 #include "base/time/time.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/prerender/prerender_field_trial.h"
+#include "chrome/browser/prerender/prerender_manager.h"
+#include "chrome/browser/prerender/prerender_test_utils.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/history/core/browser/history_service.h"
@@ -348,6 +350,9 @@
 
   AutocompleteMatch match;
   match.type = AutocompleteMatchType::HISTORY_URL;
+  prerender::test_utils::RestorePrerenderMode restore_prerender_mode;
+  prerender::PrerenderManager::SetMode(
+      prerender::PrerenderManager::PRERENDER_MODE_NOSTATE_PREFETCH);
 
   for (size_t i = 0; i < arraysize(test_url_db); ++i) {
     match.destination_url = GURL(test_url_db[i].url);
@@ -362,6 +367,9 @@
 
   AutocompleteMatch match;
   match.type = AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED;
+  prerender::test_utils::RestorePrerenderMode restore_prerender_mode;
+  prerender::PrerenderManager::SetMode(
+      prerender::PrerenderManager::PRERENDER_MODE_NOSTATE_PREFETCH);
 
   for (size_t i = 0; i < arraysize(test_url_db); ++i) {
     match.destination_url = GURL(test_url_db[i].url);
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index 4d584d25..3b75da09 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -599,6 +599,10 @@
     test_utils::PrerenderInProcessBrowserTest::SetUpOnMainThread();
     prerender::PrerenderManager::SetMode(
         prerender::PrerenderManager::PRERENDER_MODE_ENABLED);
+    prerender::PrerenderManager::SetInstantMode(
+        prerender::PrerenderManager::PRERENDER_MODE_ENABLED);
+    prerender::PrerenderManager::SetOmniboxMode(
+        prerender::PrerenderManager::PRERENDER_MODE_ENABLED);
     const testing::TestInfo* const test_info =
         testing::UnitTest::GetInstance()->current_test_info();
     // This one test fails with the host resolver redirecting all hosts.
diff --git a/chrome/browser/prerender/prerender_field_trial.cc b/chrome/browser/prerender/prerender_field_trial.cc
index 56835cf..6d4892c9 100644
--- a/chrome/browser/prerender/prerender_field_trial.cc
+++ b/chrome/browser/prerender/prerender_field_trial.cc
@@ -69,9 +69,9 @@
                                             base::FEATURE_ENABLED_BY_DEFAULT};
 
 void ConfigurePrerender() {
-  PrerenderManager::PrerenderManagerMode overall_mode =
-      ParsePrerenderMode(kNoStatePrefetchFeatureModeParameterName,
-                         PrerenderManager::PRERENDER_MODE_ENABLED);
+  PrerenderManager::PrerenderManagerMode overall_mode = ParsePrerenderMode(
+      kNoStatePrefetchFeatureModeParameterName,
+      PrerenderManager::PRERENDER_MODE_SIMPLE_LOAD_EXPERIMENT);
 
   PrerenderManager::SetMode(overall_mode);
   PrerenderManager::SetInstantMode(ParsePrerenderMode(
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc
index f0b3455..c3a03a2 100644
--- a/chrome/browser/prerender/prerender_manager.cc
+++ b/chrome/browser/prerender/prerender_manager.cc
@@ -166,11 +166,11 @@
 
 // static
 PrerenderManager::PrerenderManagerMode PrerenderManager::mode_ =
-    PRERENDER_MODE_ENABLED;
+    PRERENDER_MODE_SIMPLE_LOAD_EXPERIMENT;
 PrerenderManager::PrerenderManagerMode PrerenderManager::instant_mode_ =
-    PRERENDER_MODE_ENABLED;
+    PRERENDER_MODE_SIMPLE_LOAD_EXPERIMENT;
 PrerenderManager::PrerenderManagerMode PrerenderManager::omnibox_mode_ =
-    PRERENDER_MODE_ENABLED;
+    PRERENDER_MODE_SIMPLE_LOAD_EXPERIMENT;
 
 struct PrerenderManager::NavigationRecord {
   NavigationRecord(const GURL& url, base::TimeTicks time, Origin origin)
diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h
index 518c8d2..87bebe4 100644
--- a/chrome/browser/prerender/prerender_manager.h
+++ b/chrome/browser/prerender/prerender_manager.h
@@ -83,10 +83,19 @@
                          public MediaCaptureDevicesDispatcher::Observer {
  public:
   enum PrerenderManagerMode {
+    // WARNING: Legacy code, not for use. Disables prerendering and avoids
+    // creating an instance of PrerenderManager. This mode overrides forced
+    // prerenders which breaks the assumptions of the CustomTabActivityTest.
     PRERENDER_MODE_DISABLED,
+
+    // Enables all types of prerendering for any origin.
     PRERENDER_MODE_ENABLED,
+
+    // For each request to prerender performs a NoStatePrefetch for the same URL
+    // instead.
     PRERENDER_MODE_NOSTATE_PREFETCH,
-    // Like PRERENDER_MODE_DISABLED, but keeps track of pages that would have
+
+    // Ignores requests to prerender, but keeps track of pages that would have
     // been prerendered and records metrics for comparison with other modes.
     PRERENDER_MODE_SIMPLE_LOAD_EXPERIMENT
   };
diff --git a/chrome/browser/prerender/prerender_test_utils.cc b/chrome/browser/prerender/prerender_test_utils.cc
index 8471c97..1f6f5fdf 100644
--- a/chrome/browser/prerender/prerender_test_utils.cc
+++ b/chrome/browser/prerender/prerender_test_utils.cc
@@ -701,28 +701,34 @@
 }
 
 void PrerenderInProcessBrowserTest::SetUpOnMainThread() {
-  // Increase the memory allowed in a prerendered page above normal settings.
-  // Debug build bots occasionally run against the default limit, and tests
-  // were failing because the prerender was canceled due to memory exhaustion.
-  // http://crbug.com/93076
-  GetPrerenderManager()->mutable_config().max_bytes = 2000 * 1024 * 1024;
-
   current_browser()->profile()->GetPrefs()->SetBoolean(
       prefs::kPromptForDownload, false);
   if (autostart_test_server_)
-    ASSERT_TRUE(embedded_test_server()->Start());
+    CHECK(embedded_test_server()->Start());
   ChromeResourceDispatcherHostDelegate::
       SetExternalProtocolHandlerDelegateForTesting(
           external_protocol_handler_delegate_.get());
 
+  // Check that PrerenderManager exists, which is necessary to make sure
+  // NoStatePrefetch can be enabled and perceived FCP metrics can be recorded.
   PrerenderManager* prerender_manager = GetPrerenderManager();
-  ASSERT_TRUE(prerender_manager);
+  // Use CHECK to fail fast. The ASSERT_* macros in this context are not useful
+  // because they only silently exit and make the tests crash later with more
+  // complicated symptoms.
+  CHECK(prerender_manager);
+
+  // Increase the memory allowed in a prerendered page above normal settings.
+  // Debug build bots occasionally run against the default limit, and tests
+  // were failing because the prerender was canceled due to memory exhaustion.
+  // http://crbug.com/93076
+  prerender_manager->mutable_config().max_bytes = 2000 * 1024 * 1024;
+
   prerender_manager->mutable_config().rate_limit_enabled = false;
-  ASSERT_FALSE(prerender_contents_factory_);
+  CHECK(!prerender_contents_factory_);
   prerender_contents_factory_ = new TestPrerenderContentsFactory;
   prerender_manager->SetPrerenderContentsFactoryForTest(
       prerender_contents_factory_);
-  ASSERT_TRUE(safe_browsing_factory_->test_safe_browsing_service());
+  CHECK(safe_browsing_factory_->test_safe_browsing_service());
 }
 
 void PrerenderInProcessBrowserTest::UseHttpsSrcServer() {
diff --git a/chrome/browser/prerender/prerender_unittest.cc b/chrome/browser/prerender/prerender_unittest.cc
index 9eac021b..a6fb221 100644
--- a/chrome/browser/prerender/prerender_unittest.cc
+++ b/chrome/browser/prerender/prerender_unittest.cc
@@ -332,6 +332,12 @@
             new PrerenderLinkManager(prerender_manager_.get())),
         last_prerender_id_(0),
         field_trial_list_(nullptr) {
+    prerender::PrerenderManager::SetMode(
+        prerender::PrerenderManager::PRERENDER_MODE_ENABLED);
+    prerender::PrerenderManager::SetInstantMode(
+        prerender::PrerenderManager::PRERENDER_MODE_ENABLED);
+    prerender::PrerenderManager::SetOmniboxMode(
+        prerender::PrerenderManager::PRERENDER_MODE_ENABLED);
     prerender_manager()->SetIsLowEndDevice(false);
 
     // Enable omnibox prerendering.
@@ -458,26 +464,23 @@
   // An instance of base::FieldTrialList is necessary in order to initialize
   // global state.
   base::FieldTrialList field_trial_list_;
+
+  // Restore prerender mode after this test finishes running.
+  test_utils::RestorePrerenderMode restore_prerender_mode_;
 };
 
 TEST_F(PrerenderTest, PrerenderRespectsDisableFlag) {
   test_utils::RestorePrerenderMode restore_prerender_mode;
-  ASSERT_TRUE(PrerenderManager::IsAnyPrerenderingPossible());
-  ASSERT_EQ(PrerenderManager::PRERENDER_MODE_ENABLED,
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(kNoStatePrefetchFeature);
+  prerender::ConfigurePrerender();
+  EXPECT_FALSE(PrerenderManager::IsAnyPrerenderingPossible());
+  EXPECT_EQ(PrerenderManager::PRERENDER_MODE_DISABLED,
             PrerenderManager::GetMode(ORIGIN_NONE));
-
-  {
-    base::test::ScopedFeatureList scoped_feature_list;
-    scoped_feature_list.InitAndDisableFeature(kNoStatePrefetchFeature);
-    prerender::ConfigurePrerender();
-    EXPECT_FALSE(PrerenderManager::IsAnyPrerenderingPossible());
-    EXPECT_EQ(PrerenderManager::PRERENDER_MODE_DISABLED,
-              PrerenderManager::GetMode(ORIGIN_NONE));
-    EXPECT_EQ(PrerenderManager::PRERENDER_MODE_DISABLED,
-              PrerenderManager::GetMode(ORIGIN_OMNIBOX));
-    EXPECT_EQ(PrerenderManager::PRERENDER_MODE_DISABLED,
-              PrerenderManager::GetMode(ORIGIN_INSTANT));
-  }
+  EXPECT_EQ(PrerenderManager::PRERENDER_MODE_DISABLED,
+            PrerenderManager::GetMode(ORIGIN_OMNIBOX));
+  EXPECT_EQ(PrerenderManager::PRERENDER_MODE_DISABLED,
+            PrerenderManager::GetMode(ORIGIN_INSTANT));
 }
 
 TEST_F(PrerenderTest, PrerenderRespectsFieldTrialParameters) {
@@ -602,7 +605,6 @@
 
 TEST_F(PrerenderTest, PrerenderRespectsThirdPartyCookiesPref) {
   GURL url("http://www.google.com/");
-  test_utils::RestorePrerenderMode restore_prerender_mode;
   ASSERT_TRUE(PrerenderManager::IsAnyPrerenderingPossible());
 
   profile()->GetPrefs()->SetBoolean(prefs::kBlockThirdPartyCookies, true);
@@ -613,7 +615,6 @@
 
 TEST_F(PrerenderTest, OfflinePrerenderIgnoresThirdPartyCookiesPref) {
   GURL url("http://www.google.com/");
-  test_utils::RestorePrerenderMode restore_prerender_mode;
   ASSERT_TRUE(PrerenderManager::IsAnyPrerenderingPossible());
 
   profile()->GetPrefs()->SetBoolean(prefs::kBlockThirdPartyCookies, true);
@@ -2107,7 +2108,7 @@
                                                        FINAL_STATUS_USED);
   std::unique_ptr<PrerenderHandle> prerender_handle(
       prerender_manager()->AddPrerenderForInstant(url, nullptr, kSize));
-  CHECK(prerender_handle);
+  ASSERT_TRUE(prerender_handle);
   EXPECT_TRUE(prerender_handle->IsPrerendering());
   EXPECT_TRUE(prerender_contents->prerendering_has_started());
   EXPECT_EQ(prerender_contents, prerender_handle->contents());
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 6e67636..fa0151b 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -785,7 +785,11 @@
     AppendPrintItem();
   }
 
-  if (editable && params_.misspelled_word.empty()) {
+  // Spell check and writing direction options are not currently supported by
+  // pepper plugins.
+  if (editable && params_.misspelled_word.empty() &&
+      !content_type_->SupportsGroup(
+          ContextMenuContentType::ITEM_GROUP_MEDIA_PLUGIN)) {
     menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
     AppendLanguageSettings();
     AppendPlatformEditableItems();
@@ -1173,9 +1177,12 @@
        (!embedder_web_contents_ || !embedder_web_contents_->IsSavable()))) {
     // Both full page and embedded plugins are hosted as guest now,
     // the difference is a full page plugin is not considered as savable.
-    // For full page plugin, we show page menu items.
-    if (params_.link_url.is_empty() && params_.selection_text.empty())
+    // For full page plugin, we show page menu items so long as focus is not
+    // within an editable text area.
+    if (params_.link_url.is_empty() && params_.selection_text.empty() &&
+        !params_.is_editable) {
       AppendPageItems();
+    }
   } else {
     menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_SAVEAVAS,
                                     IDS_CONTENT_CONTEXT_SAVEPAGEAS);
diff --git a/chrome/browser/resources/chromeos/login/screen_supervised_user_creation.html b/chrome/browser/resources/chromeos/login/screen_supervised_user_creation.html
index 04eec94..baef507 100644
--- a/chrome/browser/resources/chromeos/login/screen_supervised_user_creation.html
+++ b/chrome/browser/resources/chromeos/login/screen_supervised_user_creation.html
@@ -158,7 +158,7 @@
     </div>
   </div>
   <div id="supervised-user-creation-import-template" hidden class="import-pod">
-    <img class="import-pod-image"></img>
+    <img class="import-pod-image">
     <div class="import-pod-name"></div>
   </div>
   <navigation-bar id="supervised-user-creation-navigation" close-visible>
diff --git a/chrome/browser/resources/extensions/extension_options_overlay.html b/chrome/browser/resources/extensions/extension_options_overlay.html
index 2398ac3..e5ee0b9 100644
--- a/chrome/browser/resources/extensions/extension_options_overlay.html
+++ b/chrome/browser/resources/extensions/extension_options_overlay.html
@@ -6,8 +6,7 @@
 <div id="extension-options-overlay" class="page">
   <div class="close-button"></div>
   <div id="extension-options-overlay-header">
-    <img id="extension-options-overlay-icon"
-         aria-hidden="true"></img>
+    <img id="extension-options-overlay-icon" aria-hidden="true">
     <h1 id="extension-options-overlay-title"></h1>
   </div>
   <div id="extension-options-overlay-guest"></div>
diff --git a/chrome/browser/resources/extensions/extensions.html b/chrome/browser/resources/extensions/extensions.html
index 7ed15c8..bcca289 100644
--- a/chrome/browser/resources/extensions/extensions.html
+++ b/chrome/browser/resources/extensions/extensions.html
@@ -162,7 +162,7 @@
             $i18n{extensionSettingsReloadUnpacked}
           </a>
           <a is="action-link" role="button" class="errors-link">
-            <img class="extension-error-icon"></img>
+            <img class="extension-error-icon">
             <span>$i18n{extensionErrorHeading}</span>
           </a>
         </div>
diff --git a/chrome/browser/resources/md_extensions/OWNERS b/chrome/browser/resources/md_extensions/OWNERS
index 8483f0b4..4efe53b 100644
--- a/chrome/browser/resources/md_extensions/OWNERS
+++ b/chrome/browser/resources/md_extensions/OWNERS
@@ -1,4 +1,5 @@
 rdevlin.cronin@chromium.org
+scottchen@chromium.org
 
 # TEAM: extensions-dev@chromium.org
 # COMPONENT: Platform>Extensions
diff --git a/chrome/browser/resources/md_extensions/options_dialog.html b/chrome/browser/resources/md_extensions/options_dialog.html
index 09e4de7e..9ac04c3 100644
--- a/chrome/browser/resources/md_extensions/options_dialog.html
+++ b/chrome/browser/resources/md_extensions/options_dialog.html
@@ -30,7 +30,7 @@
     <dialog is="cr-dialog" id="dialog" close-text="$i18n{close}">
       <div slot="title">
         <div id="icon-and-name-wrapper">
-          <img id="icon" src="[[data_.iconUrl]]"></img>
+          <img id="icon" src="[[data_.iconUrl]]">
           <span>[[data_.name]]</span>
         </div>
       </div>
diff --git a/chrome/browser/resources/md_user_manager/import_supervised_user.html b/chrome/browser/resources/md_user_manager/import_supervised_user.html
index ff2da9e..b308c93 100644
--- a/chrome/browser/resources/md_user_manager/import_supervised_user.html
+++ b/chrome/browser/resources/md_user_manager/import_supervised_user.html
@@ -69,7 +69,7 @@
         <paper-listbox class="no-padding" selected="{{supervisedUserIndex_}}">
           <template is="dom-repeat" items="[[supervisedUsers_]]">
             <paper-item disabled="[[item.onCurrentDevice]]">
-              <img class="profile-img" src="[[item.iconURL]]"></img>
+              <img class="profile-img" src="[[item.iconURL]]">
               <div class="profile-name">[[item.name]]</div>
               <div class="on-device" hidden="[[!item.onCurrentDevice]]">
                 $i18n{supervisedUserAlreadyOnThisDevice}
diff --git a/chrome/browser/resources/policy.css b/chrome/browser/resources/policy.css
index eb4f543..3dce761 100644
--- a/chrome/browser/resources/policy.css
+++ b/chrome/browser/resources/policy.css
@@ -69,11 +69,11 @@
   text-align: left;
 }
 
-div.reload-policies-button {
+div.left-aligned-button {
   float: left;
 }
 
-html[dir='rtl'] div.reload-policies-button {
+html[dir='rtl'] div.left-aligned-button {
   float: right;
 }
 
diff --git a/chrome/browser/resources/policy.html b/chrome/browser/resources/policy.html
index dbf20f2..6a5d9eaa 100644
--- a/chrome/browser/resources/policy.html
+++ b/chrome/browser/resources/policy.html
@@ -31,9 +31,13 @@
       <h1 i18n-content="title"></h1>
     </header>
     <section class="reload-show-unset-section">
-      <div class="reload-policies-button">
+      <div class="left-aligned-button">
         <button id="reload-policies" i18n-content="reloadPolicies"></button>
       </div>
+      <div class="left-aligned-button">
+        <button id="export-policies" i18n-content="exportPoliciesJSON">
+        </button>
+      </div>
       <div class="chrome-for-work">
          <a href="http://g.co/chromeent/learn" target="_blank">
            <span i18n-content="chromeForWork"></span></a>
diff --git a/chrome/browser/resources/policy.js b/chrome/browser/resources/policy.js
index a46ff41a..242ab60 100644
--- a/chrome/browser/resources/policy.js
+++ b/chrome/browser/resources/policy.js
@@ -451,6 +451,10 @@
         chrome.send('reloadPolicies');
       };
 
+      $('export-policies').onclick = function(event) {
+        chrome.send('exportPoliciesJSON');
+      };
+
       $('show-unset').onchange = function() {
         for (policyTable in self.policyTables) {
           self.policyTables[policyTable].filter();
diff --git a/chrome/browser/resources/set_as_default_browser.html b/chrome/browser/resources/set_as_default_browser.html
index 945ec326..92865ee5 100644
--- a/chrome/browser/resources/set_as_default_browser.html
+++ b/chrome/browser/resources/set_as_default_browser.html
@@ -33,7 +33,7 @@
     </div>
   </div>
   <div id="chrome-logo-box">
-    <img src="chrome-logo-faded.png" i18n-values="alt:chromeLogoString"></img>
+    <img src="chrome-logo-faded.png" i18n-values="alt:chromeLogoString">
   </div>
 </div>
 </body>
diff --git a/chrome/browser/resources/settings/OWNERS b/chrome/browser/resources/settings/OWNERS
index 61b5877..387901f 100644
--- a/chrome/browser/resources/settings/OWNERS
+++ b/chrome/browser/resources/settings/OWNERS
@@ -2,6 +2,7 @@
 dschuyler@chromium.org
 hcarmona@chromium.org
 michaelpg@chromium.org
+scottchen@chromium.org
 stevenjb@chromium.org
 tommycli@chromium.org
 
diff --git a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html
index 6f7d2eff..99a1e11 100644
--- a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html
+++ b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html
@@ -37,7 +37,7 @@
               <div id="icon-circle-4" class="icon-circle fade-top-right"></div>
             </div>
             <div class="picture">
-              <img id="profile-picture"></img>
+              <img id="profile-picture">
               <div id="checkmark-circle">
                 <svg id="checkmark-check" width="13" height="10" viewBox="0 0 13 10">
                   <path id="checkmark-path" d="M1 5l3.5 3.5L12 1" stroke="#FFF"
diff --git a/chrome/browser/ssl/ssl_error_assistant.proto b/chrome/browser/ssl/ssl_error_assistant.proto
index ba05957c..99f6b1ca 100644
--- a/chrome/browser/ssl/ssl_error_assistant.proto
+++ b/chrome/browser/ssl/ssl_error_assistant.proto
@@ -18,7 +18,20 @@
   optional string sha256_hash = 1;
 }
 
+message MITMSoftware {
+  // Regex that SSLErrorHandler matches against the |issuer.common_name|
+  // of a certificate to determine whether or not it's a MITM software
+  // certificate.
+  optional string regex = 1;
+  // Name of the MITM software to display on the interstitial.
+  optional string name = 2;
+  // Link to a help center URL with information about how to uninstall or
+  // reconfigure the offending MITM software.
+  optional string help_url = 3;
+}
+
 message SSLErrorAssistantConfig {
   optional uint32 version_id = 1;
   repeated CaptivePortalCert captive_portal_cert = 2;
+  repeated MITMSoftware mitm_software = 3;
 }
diff --git a/chrome/browser/ssl/ssl_error_handler.cc b/chrome/browser/ssl/ssl_error_handler.cc
index 2b367d4..6952d1d 100644
--- a/chrome/browser/ssl/ssl_error_handler.cc
+++ b/chrome/browser/ssl/ssl_error_handler.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ssl/ssl_error_handler.h"
 
 #include <stdint.h>
+#include <regex>
 #include <unordered_set>
 #include <utility>
 
@@ -37,6 +38,7 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "net/base/net_errors.h"
+#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
 #include "ui/base/resource/resource_bundle.h"
 
 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
@@ -44,11 +46,15 @@
 #include "chrome/browser/captive_portal/captive_portal_service_factory.h"
 #include "chrome/browser/captive_portal/captive_portal_tab_helper.h"
 #include "chrome/browser/ssl/captive_portal_blocking_page.h"
-#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
 #endif
 
 namespace {
 
+#if !defined(OS_IOS)
+const base::Feature kMITMSoftwareInterstitial{
+    "MITMSoftwareInterstitial", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
 const base::Feature kCaptivePortalInterstitial{
     "CaptivePortalInterstitial", base::FEATURE_ENABLED_BY_DEFAULT};
@@ -177,6 +183,34 @@
   return base::FeatureList::IsEnabled(kCaptivePortalInterstitial);
 }
 
+std::unique_ptr<std::unordered_set<std::string>> LoadCaptivePortalCertHashes(
+    const chrome_browser_ssl::SSLErrorAssistantConfig& proto) {
+  auto hashes = base::MakeUnique<std::unordered_set<std::string>>();
+  for (const chrome_browser_ssl::CaptivePortalCert& cert :
+       proto.captive_portal_cert()) {
+    hashes.get()->insert(cert.sha256_hash());
+  }
+  return hashes;
+}
+#endif
+
+#if !defined(OS_IOS)
+bool IsMITMSoftwareInterstitialEnabled() {
+  return base::FeatureList::IsEnabled(kMITMSoftwareInterstitial);
+}
+
+std::unique_ptr<std::vector<std::regex>> LoadMITMSoftwareRegexes(
+    const chrome_browser_ssl::SSLErrorAssistantConfig& proto) {
+  auto regexes = base::MakeUnique<std::vector<std::regex>>();
+  for (const chrome_browser_ssl::MITMSoftware& filter : proto.mitm_software()) {
+    // There isn't a regex type in proto buffer world, so convert the string
+    // literals returned from our proto to regexes.
+    regexes.get()->push_back(std::regex(filter.regex()));
+  }
+  return regexes;
+}
+#endif
+
 // Reads the SSL error assistant configuration from the resource bundle.
 std::unique_ptr<chrome_browser_ssl::SSLErrorAssistantConfig>
 ReadErrorAssistantProtoFromResourceBundle() {
@@ -190,17 +224,6 @@
   return proto->ParseFromZeroCopyStream(&stream) ? std::move(proto) : nullptr;
 }
 
-std::unique_ptr<std::unordered_set<std::string>> LoadCaptivePortalCertHashes(
-    const chrome_browser_ssl::SSLErrorAssistantConfig& proto) {
-  auto hashes = base::MakeUnique<std::unordered_set<std::string>>();
-  for (const chrome_browser_ssl::CaptivePortalCert& cert :
-       proto.captive_portal_cert()) {
-    hashes.get()->insert(cert.sha256_hash());
-  }
-  return hashes;
-}
-#endif
-
 bool IsSSLCommonNameMismatchHandlingEnabled() {
   return base::FeatureList::IsEnabled(kSSLCommonNameMismatchHandling);
 }
@@ -222,6 +245,12 @@
   bool IsKnownCaptivePortalCert(const net::SSLInfo& ssl_info);
 #endif
 
+#if !defined(OS_IOS)
+  // Returns true if the cert issuer matches one of our known MITM software
+  // providers.
+  bool CertContainsMITMSoftwareString(const net::SSLInfo& ssl_info);
+#endif
+
   // Testing methods:
   void ResetForTesting();
   void SetInterstitialDelayForTesting(const base::TimeDelta& delay);
@@ -230,12 +259,9 @@
   void SetClockForTesting(base::Clock* clock);
   void SetNetworkTimeTrackerForTesting(
       network_time::NetworkTimeTracker* tracker);
-
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
   void SetErrorAssistantProto(
       std::unique_ptr<chrome_browser_ssl::SSLErrorAssistantConfig>
           error_assistant_proto);
-#endif
 
  private:
   base::TimeDelta interstitial_delay_;
@@ -250,11 +276,15 @@
 
   network_time::NetworkTimeTracker* network_time_tracker_ = nullptr;
 
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
+#if !defined(OS_IOS)
   // Error assistant configuration.
   std::unique_ptr<chrome_browser_ssl::SSLErrorAssistantConfig>
       error_assistant_proto_;
 
+  std::unique_ptr<std::vector<std::regex>> mitm_software_regexes_;
+#endif
+
+#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
   // SPKI hashes belonging to certs treated as captive portals. Null until the
   // first time IsKnownCaptivePortalCert() or SetErrorAssistantProto()
   // is called.
@@ -292,8 +322,11 @@
   timer_started_callback_ = nullptr;
   network_time_tracker_ = nullptr;
   testing_clock_ = nullptr;
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
+#if !defined(OS_IOS)
   error_assistant_proto_.reset();
+  mitm_software_regexes_.reset();
+#endif
+#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
   captive_portal_spki_hashes_.reset();
 #endif
 }
@@ -318,7 +351,7 @@
   network_time_tracker_ = tracker;
 }
 
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
+#if !defined(OS_IOS)
 void ConfigSingleton::SetErrorAssistantProto(
     std::unique_ptr<chrome_browser_ssl::SSLErrorAssistantConfig> proto) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -329,10 +362,17 @@
     return;
   }
   error_assistant_proto_ = std::move(proto);
+
+  mitm_software_regexes_ = LoadMITMSoftwareRegexes(*error_assistant_proto_);
+
+#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
   captive_portal_spki_hashes_ =
       LoadCaptivePortalCertHashes(*error_assistant_proto_);
+#endif  // ENABLE_CAPTIVE_PORTAL_DETECTION
 }
+#endif  // OS_IOS
 
+#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
 bool ConfigSingleton::IsKnownCaptivePortalCert(const net::SSLInfo& ssl_info) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (!captive_portal_spki_hashes_) {
@@ -355,6 +395,32 @@
 }
 #endif
 
+#if !defined(OS_IOS)
+bool ConfigSingleton::CertContainsMITMSoftwareString(
+    const net::SSLInfo& ssl_info) {
+  // If the certificate doesn't have an issuer common name return false.
+  if (ssl_info.cert->issuer().common_name.empty()) {
+    return false;
+  }
+
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!mitm_software_regexes_) {
+    error_assistant_proto_ = ReadErrorAssistantProtoFromResourceBundle();
+    DCHECK(error_assistant_proto_);
+    mitm_software_regexes_ = LoadMITMSoftwareRegexes(*error_assistant_proto_);
+  }
+
+  // Compares the common name of the issuer of the certificate to our
+  // MITM software regexes.
+  for (const std::regex& regex : *mitm_software_regexes_) {
+    if (std::regex_match(ssl_info.cert->issuer().common_name, regex)) {
+      return true;
+    }
+  }
+  return false;
+}
+#endif
+
 class SSLErrorHandlerDelegateImpl : public SSLErrorHandler::Delegate {
  public:
   SSLErrorHandlerDelegateImpl(
@@ -387,6 +453,7 @@
   void NavigateToSuggestedURL(const GURL& suggested_url) override;
   bool IsErrorOverridable() const override;
   void ShowCaptivePortalInterstitial(const GURL& landing_url) override;
+  void ShowMITMSoftwareInterstitial() override;
   void ShowSSLInterstitial() override;
   void ShowBadClockInterstitial(const base::Time& now,
                                 ssl_errors::ClockState clock_state) override;
@@ -462,6 +529,24 @@
 #endif
 }
 
+void SSLErrorHandlerDelegateImpl::ShowMITMSoftwareInterstitial() {
+#if !defined(OS_IOS)
+  // TODO(sperigo): Update this code to render the MITM software blocking
+  // page. For the first MITM software interstitial CL, I am not checking
+  // in any of the UI code. Therefore ShowMITMSoftwareInterstitial()
+  // currently renders the generic SSL interstitial.
+  (SSLBlockingPage::Create(
+       web_contents_, cert_error_, ssl_info_, request_url_, options_mask_,
+       base::Time::NowFromSystemTime(), std::move(ssl_cert_reporter_),
+       base::FeatureList::IsEnabled(kSuperfishInterstitial) &&
+           IsSuperfish(ssl_info_.cert),
+       callback_))
+      ->Show();
+#else
+  NOTREACHED();
+#endif
+}
+
 void SSLErrorHandlerDelegateImpl::ShowSSLInterstitial() {
   // Show SSL blocking page. The interstitial owns the blocking page.
   (SSLBlockingPage::Create(
@@ -554,7 +639,7 @@
 
 void SSLErrorHandler::SetErrorAssistantProto(
     std::unique_ptr<chrome_browser_ssl::SSLErrorAssistantConfig> config_proto) {
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
+#if !defined(OS_IOS)
   g_config.Pointer()->SetErrorAssistantProto(std::move(config_proto));
 #endif
 }
@@ -591,12 +676,8 @@
     return;
   }
 
-  const net::CertStatus non_name_mismatch_errors =
-      ssl_info_.cert_status ^ net::CERT_STATUS_COMMON_NAME_INVALID;
   const bool only_error_is_name_mismatch =
-      cert_error_ == net::ERR_CERT_COMMON_NAME_INVALID &&
-      (!net::IsCertStatusError(non_name_mismatch_errors) ||
-       net::IsCertStatusMinorError(ssl_info_.cert_status));
+      IsOnlyCertError(net::CERT_STATUS_COMMON_NAME_INVALID);
 
 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
   // Check known captive portal certificate list if the only error is
@@ -613,6 +694,20 @@
   }
 #endif
 
+#if !defined(OS_IOS)
+  // The MITM software interstitial is displayed if and only if:
+  // - the error thrown is not overridable
+  // - the only certificate error is CERT_STATUS_AUTHORITY_INVALID
+  // - the certificate contains a string that indicates it was issued by a
+  //   MITM software
+  if (IsMITMSoftwareInterstitialEnabled() && !delegate_->IsErrorOverridable() &&
+      IsOnlyCertError(net::CERT_STATUS_AUTHORITY_INVALID) &&
+      g_config.Pointer()->CertContainsMITMSoftwareString(ssl_info_)) {
+    ShowMITMSoftwareInterstitial();
+    return;
+  }
+#endif
+
   if (IsSSLCommonNameMismatchHandlingEnabled() &&
       cert_error_ == net::ERR_CERT_COMMON_NAME_INVALID &&
       delegate_->IsErrorOverridable()) {
@@ -688,6 +783,19 @@
 #endif
 }
 
+void SSLErrorHandler::ShowMITMSoftwareInterstitial() {
+#if !defined(OS_IOS)
+  // Show SSL blocking page. The interstitial owns the blocking page.
+  RecordUMA(SHOW_MITM_SOFTWARE_INTERSTITIAL);
+  delegate_->ShowMITMSoftwareInterstitial();
+  // Once an interstitial is displayed, no need to keep the handler around.
+  // This is the equivalent of "delete this".
+  web_contents_->RemoveUserData(UserDataKey());
+#else
+  NOTREACHED();
+#endif
+}
+
 void SSLErrorHandler::ShowSSLInterstitial() {
   // Show SSL blocking page. The interstitial owns the blocking page.
   RecordUMA(delegate_->IsErrorOverridable()
@@ -822,3 +930,20 @@
   }
   ShowSSLInterstitial();
 }
+
+// Returns true if |only_cert_error_expected| is the only error code present in
+// the certificate. The parameter |only_cert_error_expected| is a
+// net::CertStatus code representing the most serious error identified on the
+// certificate. For example, this could be net::CERT_STATUS_COMMON_NAME_INVALID.
+// This function is useful for rendering interstitials that are triggered by one
+// specific error code only.
+bool SSLErrorHandler::IsOnlyCertError(
+    net::CertStatus only_cert_error_expected) const {
+  const net::CertStatus other_errors =
+      ssl_info_.cert_status ^ only_cert_error_expected;
+
+  return cert_error_ ==
+             net::MapCertStatusToNetError(only_cert_error_expected) &&
+         (!net::IsCertStatusError(other_errors) ||
+          net::IsCertStatusMinorError(ssl_info_.cert_status));
+}
diff --git a/chrome/browser/ssl/ssl_error_handler.h b/chrome/browser/ssl/ssl_error_handler.h
index c1ae36c..b01cf0d 100644
--- a/chrome/browser/ssl/ssl_error_handler.h
+++ b/chrome/browser/ssl/ssl_error_handler.h
@@ -78,6 +78,7 @@
     SHOW_BAD_CLOCK = 8,
     CAPTIVE_PORTAL_CERT_FOUND = 9,
     WWW_MISMATCH_FOUND_IN_SAN = 10,
+    SHOW_MITM_SOFTWARE_INTERSTITIAL = 11,
     SSL_ERROR_HANDLER_EVENT_COUNT
   };
 
@@ -95,6 +96,7 @@
     virtual void NavigateToSuggestedURL(const GURL& suggested_url) = 0;
     virtual bool IsErrorOverridable() const = 0;
     virtual void ShowCaptivePortalInterstitial(const GURL& landing_url) = 0;
+    virtual void ShowMITMSoftwareInterstitial() = 0;
     virtual void ShowSSLInterstitial() = 0;
     virtual void ShowBadClockInterstitial(
         const base::Time& now,
@@ -152,6 +154,7 @@
 
  private:
   void ShowCaptivePortalInterstitial(const GURL& landing_url);
+  void ShowMITMSoftwareInterstitial();
   void ShowSSLInterstitial();
   void ShowBadClockInterstitial(const base::Time& now,
                                 ssl_errors::ClockState clock_state);
@@ -182,6 +185,8 @@
   void HandleCertDateInvalidError();
   void HandleCertDateInvalidErrorImpl(base::TimeTicks started_handling_error);
 
+  bool IsOnlyCertError(net::CertStatus only_cert_error_expected) const;
+
   std::unique_ptr<Delegate> delegate_;
   content::WebContents* const web_contents_;
   Profile* const profile_;
diff --git a/chrome/browser/ssl/ssl_error_handler_unittest.cc b/chrome/browser/ssl/ssl_error_handler_unittest.cc
index 43a34b4d..0b4013ae 100644
--- a/chrome/browser/ssl/ssl_error_handler_unittest.cc
+++ b/chrome/browser/ssl/ssl_error_handler_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/test/simple_test_clock.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "chrome/browser/captive_portal/captive_portal_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ssl/common_name_mismatch_handler.h"
@@ -47,6 +48,65 @@
 
 const net::SHA256HashValue kCertPublicKeyHashValue = {{0x01, 0x02}};
 
+const char kOkayCertName[] = "ok_cert.pem";
+
+// These certificates are self signed certificates with relevant issuer common
+// names generated using the following openssl command:
+//  openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes
+const char kOutdatedAntivirusCert[] =
+    "-----BEGIN CERTIFICATE-----\n"
+    "MIIEKzCCAxOgAwIBAgIJAKEHkWB4gBwRMA0GCSqGSIb3DQEBCwUAMIGrMQswCQYD\n"
+    "VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5j\n"
+    "aXNjbzEgMB4GA1UECgwXTWlzY29uZmlndXJlZCBBbnRpdmlydXMxLDAqBgNVBAMM\n"
+    "I01pc2NvbmZpZ3VyZWQgRmlyZXdhbGxfNEdIUE9TNTQxMkVGMR8wHQYJKoZIhvcN\n"
+    "AQkBFhB0ZXN0QGV4YW1wbGUuY29tMB4XDTE3MDgwOTA2MjQxMloXDTE4MDgwOTA2\n"
+    "MjQxMlowgasxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYD\n"
+    "VQQHDA1TYW4gRnJhbmNpc2NvMSAwHgYDVQQKDBdNaXNjb25maWd1cmVkIEFudGl2\n"
+    "aXJ1czEsMCoGA1UEAwwjTWlzY29uZmlndXJlZCBGaXJld2FsbF80R0hQT1M1NDEy\n"
+    "RUYxHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3\n"
+    "DQEBAQUAA4IBDwAwggEKAoIBAQDJUkuPE7tJzTS1n2B8sPM/yUxRysJ3OgFGT2ah\n"
+    "X7O/SFujnbWngjJNeCd/5BFoWcvlNIvnCexgpmkwY7A2IF02lqloqpXUxDusshgx\n"
+    "CHWtpGJPsfQgBr/G+DuskDxstpZwL/U9ib/hfFH3BxacpIr67vP/phox5LilPL2x\n"
+    "K5++c1ky1m0nBV6tgQdHK6tNDzsUwDtLWQoastf/QpAHNQ4FwHeeMAS3MyIc4UrR\n"
+    "UY79jQM6pI/PvDvpIXAfR+p4FFrYQDqKANNUfL//AKoYxyLUmvLnmHDtFAyvJZDL\n"
+    "ET/owkPr97FF9tYrx4/yFKnTVlK1XLZtvMWfKf4dOKp6xXuPAgMBAAGjUDBOMB0G\n"
+    "A1UdDgQWBBRjVm9bd9WCEKU2moKxqG2Lb7AqADAfBgNVHSMEGDAWgBRjVm9bd9WC\n"
+    "EKU2moKxqG2Lb7AqADAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBo\n"
+    "Xes5dVRP336D6Hhna5S/y5rEz/GHgksljszTUMFQoUpsG+NHH/4PV85JQHv/+qgd\n"
+    "CcBug8i7C63Uvgex6dhPfiyOp1/EEdqQ+WCTCtuZuTGidEAAIHwC1RpcDlVZ46pM\n"
+    "aZyvRJcR+9axQ8KKvuJcgQQpYi3eqC/j9oaGP9O+qHKvcp94SvwRjgBsEEkcsaA5\n"
+    "4KE5UtzJg5+e4suG+GTKkE51LCGnP62/qaMujrxdVv6on1oDrGmGW0ro05gM+GiX\n"
+    "CbMn1qQ7+wn+sESgR45HnqhlWpTYL12tRVnDdJPupqkihr8nwV5+uR0C5CG0pIJ+\n"
+    "Sv4GFvNvKocwCQBPb/Hd\n"
+    "-----END CERTIFICATE-----";
+
+const char kMisconfiguredFirewallCert[] =
+    "-----BEGIN CERTIFICATE-----\n"
+    "MIIEJTCCAw2gAwIBAgIJAO0EVfP6VLU9MA0GCSqGSIb3DQEBCwUAMIGoMQswCQYD\n"
+    "VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5j\n"
+    "aXNjbzEfMB0GA1UECgwWTWlzY29uZmlndXJlZCBGaXJld2FsbDEqMCgGA1UEAwwh\n"
+    "TWlzY29uZmlndXJlZCBGaXJld2FsbF8xR0gzNUZJTzMyMR8wHQYJKoZIhvcNAQkB\n"
+    "FhB0ZXN0QGV4YW1wbGUuY29tMB4XDTE3MDgwOTE3NDgxNloXDTE4MDgwOTE3NDgx\n"
+    "NlowgagxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH\n"
+    "DA1TYW4gRnJhbmNpc2NvMR8wHQYDVQQKDBZNaXNjb25maWd1cmVkIEZpcmV3YWxs\n"
+    "MSowKAYDVQQDDCFNaXNjb25maWd1cmVkIEZpcmV3YWxsXzFHSDM1RklPMzIxHzAd\n"
+    "BgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA\n"
+    "A4IBDwAwggEKAoIBAQDXazq1NGo2TOr7OIFi14KnBBBA9rJ3HXH78/gDUj7B+/ji\n"
+    "EB2+j1tNtdPHf7uaz0O6X3PKDeed5GV+C37khr7J+12NkIQqH2TdpXZ1rHvbjV8D\n"
+    "L/NUERwoGR0+xKS9cQdMYHGyUzPkOeTg+/UKQGpeUGfFbWJVPyxjIGZ1GFKkzstX\n"
+    "CLMv9lKFgU9Q0b6WNRyfvMt7ofTxkUuyTgzoN/M2WRzj8PPczYGhopsMIpQie8c5\n"
+    "ziX3VQTKTPyEVwAat7uNVaKi8nza02hEahGFvv5oUiyi1dVfsSCDvzL9IeNtvO1w\n"
+    "G/ooZrfqBsH45oa7kigzntwnsf0fb7Op0S1RDPnDAgMBAAGjUDBOMB0GA1UdDgQW\n"
+    "BBTkWcrjWP6EivGW0GdJbZikRh/AZTAfBgNVHSMEGDAWgBTkWcrjWP6EivGW0GdJ\n"
+    "bZikRh/AZTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQA1ZSnE+ntm\n"
+    "2KTfmT3Xj9cWezTPWD2kc+tVSfuFCNqFBlZE586wmmlPXzkiI3UTOs0BJvoIEZYb\n"
+    "yiuJW3xPe9ZVsL8y8d8z1miUIcYt1EVpoF3CxPWI0iilXoF/6GbDtmdzFk990fSO\n"
+    "oGjq1Zadc4E/wxkHcYChlkIUG94WemkFRBcYmabxDJOgEGK+KDI/2cy5OlqZReBM\n"
+    "o82wrUg88gNy2IdgT7LAh8nLoEwxDEE0nmawV7FXpJdEZzKlXrAidRtbrsC2hyDa\n"
+    "N/rrXaEJK0iLuDxeBH6d4cu+MSho5z2paevIJSIyX8SQfVFCwjTEqf06OE02a94T\n"
+    "D+5GEBRIUV20\n"
+    "-----END CERTIFICATE-----";
+
 // Runs |quit_closure| on the UI thread once a URL request has been
 // seen. Returns a request that hangs.
 std::unique_ptr<net::test_server::HttpResponse> WaitForRequest(
@@ -91,6 +151,7 @@
         ssl_interstitial_shown_(false),
         bad_clock_interstitial_shown_(false),
         captive_portal_interstitial_shown_(false),
+        mitm_software_interstitial_shown_(false),
         redirected_to_suggested_url_(false),
         is_overridable_error_(true) {}
 
@@ -116,6 +177,9 @@
   int captive_portal_interstitial_shown() const {
     return captive_portal_interstitial_shown_;
   }
+  int mitm_software_interstitial_shown() const {
+    return mitm_software_interstitial_shown_;
+  }
   bool bad_clock_interstitial_shown() const {
     return bad_clock_interstitial_shown_;
   }
@@ -134,6 +198,7 @@
     ssl_interstitial_shown_ = false;
     bad_clock_interstitial_shown_ = false;
     captive_portal_interstitial_shown_ = false;
+    mitm_software_interstitial_shown_ = false;
     redirected_to_suggested_url_ = false;
   }
 
@@ -161,6 +226,10 @@
     captive_portal_interstitial_shown_ = true;
   }
 
+  void ShowMITMSoftwareInterstitial() override {
+    mitm_software_interstitial_shown_ = true;
+  }
+
   void CheckSuggestedUrl(
       const GURL& suggested_url,
       const CommonNameMismatchHandler::CheckUrlCallback& callback) override {
@@ -182,6 +251,7 @@
   bool ssl_interstitial_shown_;
   bool bad_clock_interstitial_shown_;
   bool captive_portal_interstitial_shown_;
+  bool mitm_software_interstitial_shown_;
   bool redirected_to_suggested_url_;
   bool is_overridable_error_;
   CommonNameMismatchHandler::CheckUrlCallback suggested_url_callback_;
@@ -264,16 +334,16 @@
 // A class to test the captive portal certificate list feature. Creates an error
 // handler with a name mismatch error by default. The error handler can be
 // recreated by calling ResetErrorHandler() with an appropriate cert status.
-class SSLErrorHandlerCaptivePortalCertListTest
-    : public ChromeRenderViewHostTestHarness {
+class SSLErrorAssistantTest : public ChromeRenderViewHostTestHarness {
  public:
-  SSLErrorHandlerCaptivePortalCertListTest() : field_trial_list_(nullptr) {}
+  SSLErrorAssistantTest() : field_trial_list_(nullptr) {}
 
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
     SSLErrorHandler::ResetConfigForTesting();
     SSLErrorHandler::SetInterstitialDelayForTesting(base::TimeDelta());
-    ResetErrorHandler(net::CERT_STATUS_COMMON_NAME_INVALID);
+    ResetErrorHandlerFromFile(kOkayCertName,
+                              net::CERT_STATUS_COMMON_NAME_INVALID);
   }
 
   void TearDown() override {
@@ -289,7 +359,7 @@
   const net::SSLInfo& ssl_info() { return ssl_info_; }
 
  protected:
-  void SetFeatureEnabled(bool enabled) {
+  void SetCaptivePortalFeatureEnabled(bool enabled) {
     if (enabled) {
       scoped_feature_list_.InitFromCommandLine(
           "CaptivePortalCertificateList" /* enabled */,
@@ -300,36 +370,37 @@
     }
   }
 
-  // Deletes the current error handler and creates a new one with the given
-  // |cert_status|.
-  void ResetErrorHandler(net::CertStatus cert_status) {
-    ssl_info_.Reset();
-    ssl_info_.cert =
-        net::ImportCertFromFile(net::GetTestCertsDirectory(), "ok_cert.pem");
-    ssl_info_.cert_status = cert_status;
-    ssl_info_.public_key_hashes.push_back(
-        net::HashValue(kCertPublicKeyHashValue));
-
-    delegate_ =
-        new TestSSLErrorHandlerDelegate(profile(), web_contents(), ssl_info_);
-    error_handler_.reset(new TestSSLErrorHandler(
-        std::unique_ptr<SSLErrorHandler::Delegate>(delegate_), web_contents(),
-        profile(), net::MapCertStatusToNetError(ssl_info_.cert_status),
-        ssl_info_,
-        GURL(),  // request_url
-        base::Callback<void(content::CertificateRequestResultType)>()));
-
-    // Enable finch experiment for captive portal interstitials.
-    ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
-        "CaptivePortalInterstitial", "Enabled"));
-    // Enable finch experiment for SSL common name mismatch handling.
-    ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
-        "SSLCommonNameMismatchHandling", "Enabled"));
+  void SetMITMSoftwareFeatureEnabled(bool enabled) {
+    if (enabled) {
+      scoped_feature_list_.InitFromCommandLine(
+          "MITMSoftwareInterstitial" /* enabled */,
+          std::string() /* disabled */);
+    } else {
+      scoped_feature_list_.InitFromCommandLine(
+          std::string(), "MITMSoftwareInterstitial" /* disabled */);
+    }
   }
 
-  void TestNoCaptivePortalInterstitial() {
-    base::HistogramTester histograms;
+  void ResetErrorHandlerFromString(const std::string& cert_data,
+                                   net::CertStatus cert_status) {
+    net::CertificateList certs =
+        net::X509Certificate::CreateCertificateListFromBytes(
+            cert_data.data(), cert_data.size(),
+            net::X509Certificate::FORMAT_AUTO);
+    ASSERT_FALSE(certs.empty());
+    ResetErrorHandler(certs[0], cert_status);
+  }
 
+  void ResetErrorHandlerFromFile(const std::string& cert_name,
+                                 net::CertStatus cert_status) {
+    ResetErrorHandler(
+        net::ImportCertFromFile(net::GetTestCertsDirectory(), cert_name),
+        cert_status);
+  }
+
+  // Set up an error assistant proto with mock captive portal hash data and
+  // begin handling the certificate error.
+  void RunCaptivePortalTest() {
     EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
     EXPECT_EQ(1u, ssl_info().public_key_hashes.size());
 
@@ -344,6 +415,12 @@
     SSLErrorHandler::SetErrorAssistantProto(std::move(config_proto));
 
     error_handler()->StartHandlingError();
+  }
+
+  void TestNoCaptivePortalInterstitial() {
+    base::HistogramTester histograms;
+
+    RunCaptivePortalTest();
 
     // Timer should start for captive portal detection.
     EXPECT_TRUE(error_handler()->IsTimerRunningForTesting());
@@ -370,17 +447,109 @@
         SSLErrorHandler::SHOW_SSL_INTERSTITIAL_OVERRIDABLE, 1);
   }
 
+  // Set up a mock SSL Error Assistant config with regexes that match the
+  // outdated antivirus and misconfigured firewall certificate.
+  void InitMITMSoftwareList() {
+    auto config_proto =
+        base::MakeUnique<chrome_browser_ssl::SSLErrorAssistantConfig>();
+    chrome_browser_ssl::MITMSoftware* filter1 =
+        config_proto->add_mitm_software();
+    filter1->set_name("Outdated Antivirus");
+    filter1->set_regex("Outdated Antivirus");
+
+    chrome_browser_ssl::MITMSoftware* filter2 =
+        config_proto->add_mitm_software();
+    filter2->set_name("Misconfigured Firewall");
+    filter2->set_regex("Misconfigured Firewall_[A-Z0-9]+");
+    SSLErrorHandler::SetErrorAssistantProto(std::move(config_proto));
+  }
+
+  // Calls RunMITMSoftwareTest() to set up an error assistant proto with mock
+  // MITM software strings and start handling the SSL error. Check that the
+  // generic interstitial was not shown, and the MITM software interstitial
+  // was. Check the UMA histograms.
+  void TestMITMSoftwareInterstitial() {
+    base::HistogramTester histograms;
+
+    InitMITMSoftwareList();
+    error_handler()->StartHandlingError();
+    base::RunLoop().RunUntilIdle();
+
+    EXPECT_FALSE(delegate()->ssl_interstitial_shown());
+    EXPECT_TRUE(delegate()->mitm_software_interstitial_shown());
+    EXPECT_FALSE(delegate()->suggested_url_checked());
+
+    histograms.ExpectTotalCount(SSLErrorHandler::GetHistogramNameForTesting(),
+                                2);
+    histograms.ExpectBucketCount(SSLErrorHandler::GetHistogramNameForTesting(),
+                                 SSLErrorHandler::HANDLE_ALL, 1);
+    histograms.ExpectBucketCount(
+        SSLErrorHandler::GetHistogramNameForTesting(),
+        SSLErrorHandler::SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE, 0);
+    histograms.ExpectBucketCount(
+        SSLErrorHandler::GetHistogramNameForTesting(),
+        SSLErrorHandler::SHOW_SSL_INTERSTITIAL_OVERRIDABLE, 0);
+    histograms.ExpectBucketCount(
+        SSLErrorHandler::GetHistogramNameForTesting(),
+        SSLErrorHandler::SHOW_MITM_SOFTWARE_INTERSTITIAL, 1);
+  }
+
+  // Calls RunMITMSoftwareTest() to set up an error assistant proto with mock
+  // MITM software strings and start handling the SSL error. Check that the
+  // MITM software interstitial is not shown, and a nonoverridable generic SSL
+  // interstitial is shown in its place. Check UMA histograms.
+  void TestNoMITMSoftwareInterstitial() {
+    base::HistogramTester histograms;
+
+    InitMITMSoftwareList();
+    error_handler()->StartHandlingError();
+    base::RunLoop().RunUntilIdle();
+
+    EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
+    EXPECT_TRUE(delegate()->ssl_interstitial_shown());
+    EXPECT_FALSE(delegate()->mitm_software_interstitial_shown());
+    EXPECT_FALSE(delegate()->suggested_url_checked());
+
+    histograms.ExpectTotalCount(SSLErrorHandler::GetHistogramNameForTesting(),
+                                2);
+    histograms.ExpectBucketCount(SSLErrorHandler::GetHistogramNameForTesting(),
+                                 SSLErrorHandler::HANDLE_ALL, 1);
+    histograms.ExpectBucketCount(
+        SSLErrorHandler::GetHistogramNameForTesting(),
+        SSLErrorHandler::SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE, 1);
+    histograms.ExpectBucketCount(
+        SSLErrorHandler::GetHistogramNameForTesting(),
+        SSLErrorHandler::SHOW_MITM_SOFTWARE_INTERSTITIAL, 0);
+  }
+
  private:
+  void ResetErrorHandler(scoped_refptr<net::X509Certificate> cert,
+                         net::CertStatus cert_status) {
+    ssl_info_.Reset();
+    ssl_info_.cert = cert;
+    ssl_info_.cert_status = cert_status;
+    ssl_info_.public_key_hashes.push_back(
+        net::HashValue(kCertPublicKeyHashValue));
+
+    delegate_ =
+        new TestSSLErrorHandlerDelegate(profile(), web_contents(), ssl_info_);
+    error_handler_.reset(new TestSSLErrorHandler(
+        std::unique_ptr<SSLErrorHandler::Delegate>(delegate_), web_contents(),
+        profile(), net::MapCertStatusToNetError(ssl_info_.cert_status),
+        ssl_info_,
+        GURL(),  // request_url
+        base::Callback<void(content::CertificateRequestResultType)>()));
+  }
+
   net::SSLInfo ssl_info_;
   std::unique_ptr<TestSSLErrorHandler> error_handler_;
   TestSSLErrorHandlerDelegate* delegate_;
   base::FieldTrialList field_trial_list_;
   base::test::ScopedFeatureList scoped_feature_list_;
 
-  DISALLOW_COPY_AND_ASSIGN(SSLErrorHandlerCaptivePortalCertListTest);
+  DISALLOW_COPY_AND_ASSIGN(SSLErrorAssistantTest);
 };
 
-
 class SSLErrorHandlerDateInvalidTest : public ChromeRenderViewHostTestHarness {
  public:
   SSLErrorHandlerDateInvalidTest()
@@ -858,24 +1027,12 @@
 
 // Tests that a certificate marked as a known captive portal certificate causes
 // the captive portal interstitial to be shown.
-TEST_F(SSLErrorHandlerCaptivePortalCertListTest, Enabled) {
-  SetFeatureEnabled(true);
-
-  EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
-  EXPECT_EQ(1u, ssl_info().public_key_hashes.size());
-
-  auto config_proto =
-      base::MakeUnique<chrome_browser_ssl::SSLErrorAssistantConfig>();
-  config_proto->add_captive_portal_cert()->set_sha256_hash(
-      "sha256/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
-  config_proto->add_captive_portal_cert()->set_sha256_hash(
-      ssl_info().public_key_hashes[0].ToString());
-  config_proto->add_captive_portal_cert()->set_sha256_hash(
-      "sha256/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
-  SSLErrorHandler::SetErrorAssistantProto(std::move(config_proto));
+TEST_F(SSLErrorAssistantTest, CaptivePortal_FeatureEnabled) {
+  SetCaptivePortalFeatureEnabled(true);
 
   base::HistogramTester histograms;
-  error_handler()->StartHandlingError();
+
+  RunCaptivePortalTest();
 
   // Timer shouldn't start for a known captive portal certificate.
   EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
@@ -908,8 +1065,8 @@
 // Tests that a certificate marked as a known captive portal certificate does
 // not cause the captive portal interstitial to be shown, if the feature is
 // disabled.
-TEST_F(SSLErrorHandlerCaptivePortalCertListTest, Disabled) {
-  SetFeatureEnabled(false);
+TEST_F(SSLErrorAssistantTest, CaptivePortal_FeatureDisabled) {
+  SetCaptivePortalFeatureEnabled(false);
 
   // Default error for SSLErrorHandlerNameMismatchTest tests is name mismatch.
   TestNoCaptivePortalInterstitial();
@@ -918,10 +1075,11 @@
 // Tests that an error other than name mismatch does not cause a captive portal
 // interstitial to be shown, even if the certificate is marked as a known
 // captive portal certificate.
-TEST_F(SSLErrorHandlerCaptivePortalCertListTest, AuthorityInvalid) {
-  SetFeatureEnabled(true);
+TEST_F(SSLErrorAssistantTest,
+       CaptivePortal_AuthorityInvalidError_NoInterstitial) {
+  SetCaptivePortalFeatureEnabled(true);
 
-  ResetErrorHandler(net::CERT_STATUS_AUTHORITY_INVALID);
+  ResetErrorHandlerFromFile(kOkayCertName, net::CERT_STATUS_AUTHORITY_INVALID);
   TestNoCaptivePortalInterstitial();
 }
 
@@ -929,16 +1087,15 @@
 // not cause a captive portal interstitial to be shown, even if the certificate
 // is marked as a known captive portal certificate. The resulting error is
 // authority-invalid.
-TEST_F(SSLErrorHandlerCaptivePortalCertListTest,
-       NameMismatchAndAuthorityInvalid) {
-  SetFeatureEnabled(true);
+TEST_F(SSLErrorAssistantTest, CaptivePortal_TwoErrors_NoInterstitial) {
+  SetCaptivePortalFeatureEnabled(true);
 
   const net::CertStatus cert_status =
       net::CERT_STATUS_COMMON_NAME_INVALID | net::CERT_STATUS_AUTHORITY_INVALID;
   // Sanity check that AUTHORITY_INVALID is seen as the net error.
   ASSERT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
             net::MapCertStatusToNetError(cert_status));
-  ResetErrorHandler(cert_status);
+  ResetErrorHandlerFromFile(kOkayCertName, cert_status);
   TestNoCaptivePortalInterstitial();
 }
 
@@ -946,8 +1103,9 @@
 // captive portal interstitial to be shown, even if the certificate is marked as
 // a known captive portal certificate. Similar to
 // NameMismatchAndAuthorityInvalid, except the resulting error is name mismatch.
-TEST_F(SSLErrorHandlerCaptivePortalCertListTest, NameMismatchAndWeakKey) {
-  SetFeatureEnabled(true);
+TEST_F(SSLErrorAssistantTest,
+       CaptivePortal_TwoErrorsIncludingNameMismatch_NoInterstitial) {
+  SetCaptivePortalFeatureEnabled(true);
 
   const net::CertStatus cert_status =
       net::CERT_STATUS_COMMON_NAME_INVALID | net::CERT_STATUS_WEAK_KEY;
@@ -956,34 +1114,21 @@
   // CertStatus even when COMMON_NAME_INVALID is the net error.
   ASSERT_EQ(net::ERR_CERT_COMMON_NAME_INVALID,
             net::MapCertStatusToNetError(cert_status));
-  ResetErrorHandler(cert_status);
+  ResetErrorHandlerFromFile(kOkayCertName, cert_status);
   TestNoCaptivePortalInterstitial();
 }
 
 #else
 
-TEST_F(SSLErrorHandlerCaptivePortalCertListTest, DisabledByBuild) {
-  SetFeatureEnabled(true);
+TEST_F(SSLErrorAssistantTest, CaptivePortal_DisabledByBuild) {
+  SetCaptivePortalFeatureEnabled(true);
 
   // Default error for SSLErrorHandlerNameMismatchTest tests is name mismatch,
   // but the feature is disabled by build so a generic SSL interstitial will be
   // displayed.
   base::HistogramTester histograms;
 
-  EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
-  EXPECT_EQ(1u, ssl_info().public_key_hashes.size());
-
-  auto config_proto =
-      base::MakeUnique<chrome_browser_ssl::SSLErrorAssistantConfig>();
-  config_proto->add_captive_portal_cert()->set_sha256_hash(
-      "sha256/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
-  config_proto->add_captive_portal_cert()->set_sha256_hash(
-      ssl_info().public_key_hashes[0].ToString());
-  config_proto->add_captive_portal_cert()->set_sha256_hash(
-      "sha256/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
-  SSLErrorHandler::SetErrorAssistantProto(std::move(config_proto));
-
-  error_handler()->StartHandlingError();
+  RunCaptivePortalTest();
 
   EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
   EXPECT_FALSE(delegate()->captive_portal_checked());
@@ -1008,3 +1153,111 @@
 }
 
 #endif  // BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
+
+#if !defined(OS_IOS)
+
+TEST_F(SSLErrorAssistantTest, MITMSoftware_OutdatedAntivirusCertificate) {
+  SetMITMSoftwareFeatureEnabled(true);
+
+  ResetErrorHandlerFromString(kOutdatedAntivirusCert,
+                              net::CERT_STATUS_AUTHORITY_INVALID);
+  delegate()->set_non_overridable_error();
+
+  TestMITMSoftwareInterstitial();
+}
+
+TEST_F(SSLErrorAssistantTest, MITMSoftware_MisconfiguredFirewallCertificate) {
+  SetMITMSoftwareFeatureEnabled(true);
+
+  ResetErrorHandlerFromString(kMisconfiguredFirewallCert,
+                              net::CERT_STATUS_AUTHORITY_INVALID);
+  delegate()->set_non_overridable_error();
+
+  TestMITMSoftwareInterstitial();
+}
+
+TEST_F(SSLErrorAssistantTest, MITMSoftware_FeatureDisabled) {
+  SetMITMSoftwareFeatureEnabled(false);
+
+  ResetErrorHandlerFromString(kOutdatedAntivirusCert,
+                              net::CERT_STATUS_AUTHORITY_INVALID);
+  delegate()->set_non_overridable_error();
+
+  TestNoMITMSoftwareInterstitial();
+}
+
+TEST_F(SSLErrorAssistantTest,
+       MITMSoftware_NonMatchingCertificate_NoInterstitial) {
+  SetMITMSoftwareFeatureEnabled(true);
+
+  ResetErrorHandlerFromFile(kOkayCertName, net::CERT_STATUS_AUTHORITY_INVALID);
+  delegate()->set_non_overridable_error();
+
+  TestNoMITMSoftwareInterstitial();
+}
+
+TEST_F(SSLErrorAssistantTest, MITMSoftware_WrongError_NoInterstitial) {
+  SetMITMSoftwareFeatureEnabled(true);
+
+  ResetErrorHandlerFromString(kOutdatedAntivirusCert,
+                              net::CERT_STATUS_COMMON_NAME_INVALID);
+  delegate()->set_non_overridable_error();
+
+  TestNoMITMSoftwareInterstitial();
+}
+
+TEST_F(SSLErrorAssistantTest, MITMSoftware_TwoErrors_NoInterstitial) {
+  SetMITMSoftwareFeatureEnabled(true);
+
+  ResetErrorHandlerFromString(kOutdatedAntivirusCert,
+                              net::CERT_STATUS_AUTHORITY_INVALID |
+                                  net::CERT_STATUS_COMMON_NAME_INVALID);
+  delegate()->set_non_overridable_error();
+
+  TestNoMITMSoftwareInterstitial();
+}
+
+TEST_F(SSLErrorAssistantTest, MITMSoftware_Overridable_NoInterstitial) {
+  base::HistogramTester histograms;
+
+  SetMITMSoftwareFeatureEnabled(true);
+  ResetErrorHandlerFromString(kOutdatedAntivirusCert,
+                              net::CERT_STATUS_AUTHORITY_INVALID);
+
+  // Don't use the TestNoMITMSoftwareInterstitial helper here because it
+  // checks the histograms for a nonoverridable SSL error.
+  InitMITMSoftwareList();
+  error_handler()->StartHandlingError();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(delegate()->ssl_interstitial_shown());
+  EXPECT_FALSE(delegate()->mitm_software_interstitial_shown());
+  EXPECT_FALSE(delegate()->suggested_url_checked());
+
+  histograms.ExpectTotalCount(SSLErrorHandler::GetHistogramNameForTesting(), 2);
+  histograms.ExpectBucketCount(SSLErrorHandler::GetHistogramNameForTesting(),
+                               SSLErrorHandler::HANDLE_ALL, 1);
+  histograms.ExpectBucketCount(
+      SSLErrorHandler::GetHistogramNameForTesting(),
+      SSLErrorHandler::SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE, 0);
+  histograms.ExpectBucketCount(
+      SSLErrorHandler::GetHistogramNameForTesting(),
+      SSLErrorHandler::SHOW_SSL_INTERSTITIAL_OVERRIDABLE, 1);
+  histograms.ExpectBucketCount(SSLErrorHandler::GetHistogramNameForTesting(),
+                               SSLErrorHandler::SHOW_MITM_SOFTWARE_INTERSTITIAL,
+                               0);
+}
+
+#else
+
+TEST_F(SSLErrorAssistantTest, MITMSoftware_DisabledByBuild_NoInterstitial) {
+  SetMITMSoftwareFeatureEnabled(true);
+
+  ResetErrorHandlerFromString(kOutdatedAntivirusCert,
+                              net::CERT_STATUS_AUTHORITY_INVALID);
+  delegate()->set_non_overridable_error();
+
+  TestNoMITMSoftwareInterstitial();
+}
+
+#endif  // #if !defined(OS_IOS)
diff --git a/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc b/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc
index 809f704..de68d07 100644
--- a/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_user_events_sync_test.cc
@@ -4,8 +4,11 @@
 
 #include "base/macros.h"
 #include "base/time/time.h"
+#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/integration/sessions_helper.cc"
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
 #include "chrome/browser/sync/test/integration/status_change_checker.h"
+#include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/user_event_service_factory.h"
 #include "components/sync/protocol/user_event_specifics.pb.h"
@@ -122,7 +125,7 @@
       GetFakeServer()->GetSyncEntitiesByModelType(syncer::USER_EVENTS).size());
   syncer::UserEventService* event_service =
       browser_sync::UserEventServiceFactory::GetForProfile(GetProfile(0));
-  UserEventSpecifics specifics = CreateEvent(0);
+  const UserEventSpecifics specifics = CreateEvent(0);
   event_service->RecordUserEvent(specifics);
   UserEventEqualityChecker(GetSyncService(0), GetFakeServer(), {specifics})
       .Wait();
@@ -130,8 +133,8 @@
 
 IN_PROC_BROWSER_TEST_F(SingleClientUserEventsSyncTest, RetrySequential) {
   ASSERT_TRUE(SetupSync());
-  UserEventSpecifics specifics1 = CreateEvent(1);
-  UserEventSpecifics specifics2 = CreateEvent(2);
+  const UserEventSpecifics specifics1 = CreateEvent(1);
+  const UserEventSpecifics specifics2 = CreateEvent(2);
   syncer::UserEventService* event_service =
       browser_sync::UserEventServiceFactory::GetForProfile(GetProfile(0));
 
@@ -162,8 +165,8 @@
 IN_PROC_BROWSER_TEST_F(SingleClientUserEventsSyncTest, RetryParallel) {
   ASSERT_TRUE(SetupSync());
   bool first = true;
-  UserEventSpecifics specifics1 = CreateEvent(1);
-  UserEventSpecifics specifics2 = CreateEvent(2);
+  const UserEventSpecifics specifics1 = CreateEvent(1);
+  const UserEventSpecifics specifics2 = CreateEvent(2);
   UserEventSpecifics retry_specifics;
 
   syncer::UserEventService* event_service =
@@ -185,4 +188,63 @@
       .Wait();
 }
 
+IN_PROC_BROWSER_TEST_F(SingleClientUserEventsSyncTest, NoHistory) {
+  const UserEventSpecifics specifics1 = CreateEvent(1);
+  const UserEventSpecifics specifics2 = CreateEvent(2);
+  const UserEventSpecifics specifics3 = CreateEvent(3);
+  ASSERT_TRUE(SetupSync());
+  syncer::UserEventService* event_service =
+      browser_sync::UserEventServiceFactory::GetForProfile(GetProfile(0));
+
+  event_service->RecordUserEvent(specifics1);
+  ASSERT_TRUE(GetClient(0)->DisableSyncForDatatype(syncer::TYPED_URLS));
+  event_service->RecordUserEvent(specifics2);
+  ASSERT_TRUE(GetClient(0)->EnableSyncForDatatype(syncer::TYPED_URLS));
+  event_service->RecordUserEvent(specifics3);
+
+  // No |specifics2| because it was recorded while history was disabled.
+  UserEventEqualityChecker(GetSyncService(0), GetFakeServer(),
+                           {specifics1, specifics3})
+      .Wait();
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientUserEventsSyncTest, NoSessions) {
+  const UserEventSpecifics specifics = CreateEvent(1);
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(GetClient(0)->DisableSyncForDatatype(syncer::PROXY_TABS));
+  syncer::UserEventService* event_service =
+      browser_sync::UserEventServiceFactory::GetForProfile(GetProfile(0));
+
+  event_service->RecordUserEvent(specifics);
+
+  // PROXY_TABS shouldn't affect us in any way.
+  UserEventEqualityChecker(GetSyncService(0), GetFakeServer(), {specifics})
+      .Wait();
+}
+
+IN_PROC_BROWSER_TEST_F(SingleClientUserEventsSyncTest, Encryption) {
+  const UserEventSpecifics specifics1 = CreateEvent(1);
+  const UserEventSpecifics specifics2 = CreateEvent(2);
+  const GURL url("http://www.one.com/");
+
+  ASSERT_TRUE(SetupSync());
+  syncer::UserEventService* event_service =
+      browser_sync::UserEventServiceFactory::GetForProfile(GetProfile(0));
+  event_service->RecordUserEvent(specifics1);
+  ASSERT_TRUE(EnableEncryption(0));
+  UserEventEqualityChecker(GetSyncService(0), GetFakeServer(), {specifics1})
+      .Wait();
+  event_service->RecordUserEvent(specifics2);
+
+  // Just checking that we don't see specifics2 isn't very convincing yet,
+  // because it may simply not have reached the server yet. So lets send
+  // something else through the system that we can wait on before checking.
+  // Tab/SESSIONS data was picked fairly arbitrarily, note that we expect 2
+  // entries, one for the window/header and one for the tab.
+  sessions_helper::OpenTab(0, url);
+  ServerCountMatchStatusChecker(syncer::SESSIONS, 2);
+  UserEventEqualityChecker(GetSyncService(0), GetFakeServer(), {specifics1})
+      .Wait();
+}
+
 }  // namespace
diff --git a/chrome/browser/ui/app_list/search/arc/arc_playstore_search_provider.cc b/chrome/browser/ui/app_list/search/arc/arc_playstore_search_provider.cc
index f2894ac..fc95abb4 100644
--- a/chrome/browser/ui/app_list/search/arc/arc_playstore_search_provider.cc
+++ b/chrome/browser/ui/app_list/search/arc/arc_playstore_search_provider.cc
@@ -9,13 +9,31 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
+#include "chrome/browser/chromeos/extensions/gfx_utils.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.h"
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_service_manager.h"
 
 namespace {
-constexpr int kHistogramBuckets = 7;
+constexpr int kHistogramBuckets = 13;
+
+// Skips Play Store apps that have equivalent extensions installed.
+// Do not skip recent instant apps since they should be treated like
+// on-device apps.
+bool CanSkipSearchResult(content::BrowserContext* context,
+                         const arc::mojom::AppDiscoveryResult& result) {
+  if (result.is_instant_app && result.is_recent)
+    return false;
+
+  if (!result.package_name.has_value())
+    return false;
+
+  return !extensions::util::GetEquivalentInstalledExtensions(
+              context, result.package_name.value())
+              .empty();
+}
 }  // namespace
 
 namespace app_list {
@@ -27,7 +45,9 @@
     : max_results_(max_results),
       profile_(profile),
       list_controller_(list_controller),
-      weak_ptr_factory_(this) {}
+      weak_ptr_factory_(this) {
+  DCHECK_EQ(kHistogramBuckets, max_results + 1);
+}
 
 ArcPlayStoreSearchProvider::~ArcPlayStoreSearchProvider() = default;
 
@@ -67,6 +87,10 @@
   for (auto& result : results) {
     if (result->is_instant_app)
       ++instant_app_count;
+
+    if (CanSkipSearchResult(profile_, *result))
+      continue;
+
     new_results.emplace_back(base::MakeUnique<ArcPlayStoreSearchResult>(
         std::move(result), profile_, list_controller_));
   }
diff --git a/chrome/browser/ui/app_list/search/arc/arc_playstore_search_provider_unittest.cc b/chrome/browser/ui/app_list/search/arc/arc_playstore_search_provider_unittest.cc
index 3931883..e2ec5a8 100644
--- a/chrome/browser/ui/app_list/search/arc/arc_playstore_search_provider_unittest.cc
+++ b/chrome/browser/ui/app_list/search/arc/arc_playstore_search_provider_unittest.cc
@@ -10,11 +10,14 @@
 
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/ui/app_list/app_list_test_util.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
 #include "chrome/browser/ui/app_list/search/arc/arc_playstore_search_result.h"
 #include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
 #include "chrome/test/base/testing_profile.h"
+#include "extensions/common/extension_builder.h"
+#include "extensions/common/value_builder.h"
 #include "ui/app_list/search_result.h"
 
 namespace app_list {
@@ -43,6 +46,20 @@
         max_results, profile_.get(), controller_.get());
   }
 
+  scoped_refptr<extensions::Extension> CreateExtension(const std::string& id) {
+    return extensions::ExtensionBuilder()
+        .SetManifest(extensions::DictionaryBuilder()
+                         .Set("name", "test")
+                         .Set("version", "0.1")
+                         .Build())
+        .SetID(id)
+        .Build();
+  }
+
+  void AddExtension(const extensions::Extension* extension) {
+    service()->AddExtension(extension);
+  }
+
  private:
   std::unique_ptr<test::TestAppListControllerDelegate> controller_;
   ArcAppTest arc_test_;
@@ -51,7 +68,7 @@
 };
 
 TEST_F(ArcPlayStoreSearchProviderTest, Basic) {
-  constexpr size_t kMaxResults = 6;
+  constexpr size_t kMaxResults = 12;
   constexpr char kQuery[] = "Play App";
 
   std::unique_ptr<ArcPlayStoreSearchProvider> provider =
@@ -59,11 +76,15 @@
   EXPECT_TRUE(provider->results().empty());
   ArcPlayStoreSearchResult::DisableSafeDecodingForTesting();
 
+  AddExtension(CreateExtension(extension_misc::kGmailAppId).get());
+
   // Check that the result size of a query doesn't exceed the |kMaxResults|.
   provider->Start(false, base::UTF8ToUTF16(kQuery));
   const app_list::SearchProvider::Results& results = provider->results();
   ASSERT_GT(results.size(), 0u);
-  ASSERT_GE(kMaxResults, results.size());
+  // Play Store returns |kMaxResults| results, but the first one (GMail) already
+  // has Chrome extension installed, so it will be skipped.
+  ASSERT_EQ(kMaxResults - 1, results.size());
 
   // Check that information is correctly set in each result.
   for (size_t i = 0; i < results.size(); ++i) {
diff --git a/chrome/browser/ui/app_list/search/search_controller_factory.cc b/chrome/browser/ui/app_list/search/search_controller_factory.cc
index c1d0e5b..8bd8e2c 100644
--- a/chrome/browser/ui/app_list/search/search_controller_factory.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_factory.cc
@@ -44,7 +44,13 @@
 constexpr size_t kMaxSuggestionsResults = 6;
 constexpr size_t kMaxLauncherSearchResults = 2;
 #if defined(OS_CHROMEOS)
-constexpr size_t kMaxPlayStoreResults = 6;
+// We show up to 6 Play Store results. However, part of Play Store results may
+// be filtered out because they may correspond to already installed Web apps. So
+// we request twice as many Play Store apps as we can show. Note that this still
+// doesn't guarantee that all 6 positions will be filled, as we might in theory
+// filter out more than half of results.
+// TODO(753947): Consider progressive algorithm of getting Play Store results.
+constexpr size_t kMaxPlayStoreResults = 12;
 #endif
 
 // Constants related to the SuggestionsService in AppList field trial.
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc
index af9d1a5..04a9dba5 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -15,6 +15,7 @@
 #include "ash/accessibility_types.h"
 #include "ash/content/gpu_support_impl.h"
 #include "ash/shell.h"
+#include "ash/system/tray/system_tray_controller.h"
 #include "ash/wallpaper/wallpaper_delegate.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/window_state.h"
@@ -73,9 +74,11 @@
 #include "content/public/browser/media_session.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/common/service_manager_connection.h"
+#include "content/public/common/url_constants.h"
 #include "ui/aura/window.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "url/url_constants.h"
 
 #if defined(USE_OZONE)
 #include "services/ui/public/cpp/input_devices/input_device_controller_client.h"
@@ -468,6 +471,15 @@
     url_to_open = arc::ArcUrlToExternalFileUrl(url_to_open);
   }
 
+  // If the url is for system settings, show the settings in a system tray
+  // instead of a browser tab.
+  if (url_to_open.GetContent() == "settings" &&
+      (url_to_open.SchemeIs(url::kAboutScheme) ||
+       url_to_open.SchemeIs(content::kChromeUIScheme))) {
+    ash::Shell::Get()->system_tray_controller()->ShowSettings();
+    return;
+  }
+
   chrome::ScopedTabbedBrowserDisplayer displayer(
       ProfileManager::GetActiveUserProfile());
   chrome::AddSelectedTabWithURL(
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
index 7ccbf40..f400c2cb 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
+++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
@@ -22,8 +22,6 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/networking_config_delegate_chromeos.h"
 #include "chrome/browser/ui/ash/system_tray_client.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
@@ -37,11 +35,9 @@
 namespace chromeos {
 
 SystemTrayDelegateChromeOS::SystemTrayDelegateChromeOS()
-    : networking_config_delegate_(
+    : registrar_(base::MakeUnique<content::NotificationRegistrar>()),
+      networking_config_delegate_(
           base::MakeUnique<NetworkingConfigDelegateChromeos>()) {
-  // Register notifications on construction so that events such as
-  // PROFILE_CREATED do not get missed if they happen before Initialize().
-  registrar_.reset(new content::NotificationRegistrar);
   if (SystemTrayClient::GetUserLoginStatus() ==
       ash::LoginStatus::NOT_LOGGED_IN) {
     registrar_->Add(this,
@@ -62,10 +58,6 @@
                  base::Unretained(this)));
 }
 
-void SystemTrayDelegateChromeOS::Initialize() {
-  BrowserList::AddObserver(this);
-}
-
 SystemTrayDelegateChromeOS::~SystemTrayDelegateChromeOS() {
   user_pref_registrar_.reset();
 
@@ -74,9 +66,6 @@
 
   // Unregister a11y status subscription.
   accessibility_subscription_.reset();
-
-  BrowserList::RemoveObserver(this);
-  StopObservingAppWindowRegistry();
 }
 
 ash::NetworkingConfigDelegate*
@@ -93,14 +82,8 @@
 }
 
 void SystemTrayDelegateChromeOS::SetProfile(Profile* profile) {
-  // Stop observing the AppWindowRegistry of the current |user_profile_|.
-  StopObservingAppWindowRegistry();
-
   user_profile_ = profile;
 
-  // Start observing the AppWindowRegistry of the newly set |user_profile_|.
-  extensions::AppWindowRegistry::Get(user_profile_)->AddObserver(this);
-
   // TODO(jamescook): Move all these prefs into ash. See LogoutButtonTray for
   // an example of how to do this.
   PrefService* prefs = profile->GetPrefs();
@@ -128,40 +111,6 @@
   return true;
 }
 
-void SystemTrayDelegateChromeOS::StopObservingAppWindowRegistry() {
-  if (!user_profile_)
-    return;
-
-  extensions::AppWindowRegistry* registry =
-      extensions::AppWindowRegistry::Factory::GetForBrowserContext(
-          user_profile_, false);
-  if (registry)
-    registry->RemoveObserver(this);
-}
-
-void SystemTrayDelegateChromeOS::NotifyIfLastWindowClosed() {
-  if (!user_profile_)
-    return;
-
-  BrowserList* browser_list = BrowserList::GetInstance();
-  for (BrowserList::const_iterator it = browser_list->begin();
-       it != browser_list->end();
-       ++it) {
-    if ((*it)->profile()->IsSameProfile(user_profile_)) {
-      // The current user has at least one open browser window.
-      return;
-    }
-  }
-
-  if (!extensions::AppWindowRegistry::Get(
-          user_profile_)->app_windows().empty()) {
-    // The current user has at least one open app window.
-    return;
-  }
-
-  GetSystemTrayNotifier()->NotifyLastWindowClosed();
-}
-
 // content::NotificationObserver implementation.
 void SystemTrayDelegateChromeOS::Observe(
     int type,
@@ -197,17 +146,6 @@
   GetSystemTrayNotifier()->NotifyAccessibilityModeChanged(notify);
 }
 
-// Overridden from chrome::BrowserListObserver.
-void SystemTrayDelegateChromeOS::OnBrowserRemoved(Browser* browser) {
-  NotifyIfLastWindowClosed();
-}
-
-// Overridden from extensions::AppWindowRegistry::Observer.
-void SystemTrayDelegateChromeOS::OnAppWindowRemoved(
-    extensions::AppWindow* app_window) {
-  NotifyIfLastWindowClosed();
-}
-
 void SystemTrayDelegateChromeOS::OnAccessibilityStatusChanged(
     const AccessibilityStatusEventDetails& details) {
   if (details.notification_type == ACCESSIBILITY_MANAGER_SHUTDOWN)
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos.h b/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
index c9ec3691..54b57fc9 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
+++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
@@ -13,11 +13,9 @@
 #include "base/observer_list.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser_list_observer.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
-#include "extensions/browser/app_window/app_window_registry.h"
 
 namespace ash {
 class SystemTrayNotifier;
@@ -29,17 +27,13 @@
 // the transition to mustash. New code should be added to SystemTrayClient.
 // Use system_tray.mojom methods if you need to send information to ash.
 // Please contact jamescook@chromium.org if you have questions or need help.
-class SystemTrayDelegateChromeOS
-    : public ash::SystemTrayDelegate,
-      public content::NotificationObserver,
-      public chrome::BrowserListObserver,
-      public extensions::AppWindowRegistry::Observer {
+class SystemTrayDelegateChromeOS : public ash::SystemTrayDelegate,
+                                   public content::NotificationObserver {
  public:
   SystemTrayDelegateChromeOS();
   ~SystemTrayDelegateChromeOS() override;
 
   // Overridden from ash::SystemTrayDelegate:
-  void Initialize() override;
   ash::NetworkingConfigDelegate* GetNetworkingConfigDelegate() const override;
   void ActiveUserWasChanged() override;
 
@@ -50,16 +44,6 @@
 
   bool UnsetProfile(Profile* profile);
 
-  void UpdateSessionStartTime();
-
-  void UpdateSessionLengthLimit();
-
-  void StopObservingAppWindowRegistry();
-
-  // Notify observers if the current user has no more open browser or app
-  // windows.
-  void NotifyIfLastWindowClosed();
-
   // content::NotificationObserver implementation.
   void Observe(int type,
                const content::NotificationSource& source,
@@ -68,12 +52,6 @@
   void OnAccessibilityModeChanged(
       ash::AccessibilityNotificationVisibility notify);
 
-  // Overridden from chrome::BrowserListObserver:
-  void OnBrowserRemoved(Browser* browser) override;
-
-  // Overridden from extensions::AppWindowRegistry::Observer:
-  void OnAppWindowRemoved(extensions::AppWindow* app_window) override;
-
   void OnAccessibilityStatusChanged(
       const AccessibilityStatusEventDetails& details);
 
diff --git a/chrome/browser/ui/search/instant_search_prerenderer_unittest.cc b/chrome/browser/ui/search/instant_search_prerenderer_unittest.cc
index 55cc8da..abcf4e7 100644
--- a/chrome/browser/ui/search/instant_search_prerenderer_unittest.cc
+++ b/chrome/browser/ui/search/instant_search_prerenderer_unittest.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/prerender/prerender_manager_factory.h"
 #include "chrome/browser/prerender/prerender_origin.h"
 #include "chrome/browser/prerender/prerender_tab_helper.h"
+#include "chrome/browser/prerender/prerender_test_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/instant_service.h"
 #include "chrome/browser/search/instant_unittest_base.h"
@@ -187,12 +188,25 @@
   InstantSearchPrerendererTest() {}
 
  protected:
+  using RestorePrerenderMode = prerender::test_utils::RestorePrerenderMode;
+
   void SetUp() override {
     ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial("EmbeddedSearch",
                                                        "Group1 strk:20"));
+
+    // Prerender mode is stored in a few static variables. Remember the default
+    // mode to restore it later in TearDown() to avoid affecting other tests.
+    restore_prerender_mode_ = base::MakeUnique<RestorePrerenderMode>();
+    PrerenderManager::SetInstantMode(PrerenderManager::PRERENDER_MODE_ENABLED);
+
     InstantUnitTestBase::SetUp();
   }
 
+  void TearDown() override {
+    InstantUnitTestBase::TearDown();
+    restore_prerender_mode_.reset();
+  }
+
   void Init(bool prerender_search_results_base_page,
             bool call_did_finish_load) {
     AddTab(browser(), GURL(url::kAboutBlankURL));
@@ -250,6 +264,7 @@
 
  private:
   MockEmbeddedSearchClient mock_embedded_search_client_;
+  std::unique_ptr<RestorePrerenderMode> restore_prerender_mode_;
 };
 
 TEST_F(InstantSearchPrerendererTest, GetSearchTermsFromPrerenderedPage) {
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 3762681..b7a9acd 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -1153,7 +1153,13 @@
   if (active_tab && is_dragging)
     active_tab->Paint(paint_info);
 
-  ui::PaintRecorder recorder(paint_info.context(), size());
+  // Keep the recording scales consistent for the tab strip and its children.
+  // See crbug/753911
+  ui::PaintRecorder recorder(paint_info.context(),
+                             paint_info.paint_recording_size(),
+                             paint_info.paint_recording_scale_x(),
+                             paint_info.paint_recording_scale_y(), nullptr);
+
   gfx::Canvas* canvas = recorder.canvas();
   if (active_tab) {
     canvas->sk_canvas()->clipRect(
diff --git a/chrome/browser/ui/webui/policy_ui.cc b/chrome/browser/ui/webui/policy_ui.cc
index 35c90bc..326f03da 100644
--- a/chrome/browser/ui/webui/policy_ui.cc
+++ b/chrome/browser/ui/webui/policy_ui.cc
@@ -21,6 +21,7 @@
   source->AddLocalizedString("filterPlaceholder",
                              IDS_POLICY_FILTER_PLACEHOLDER);
   source->AddLocalizedString("reloadPolicies", IDS_POLICY_RELOAD_POLICIES);
+  source->AddLocalizedString("exportPoliciesJSON", IDS_EXPORT_POLICIES_JSON);
   source->AddLocalizedString("chromeForWork", IDS_POLICY_CHROME_FOR_WORK);
   source->AddLocalizedString("status", IDS_POLICY_STATUS);
   source->AddLocalizedString("statusDevice", IDS_POLICY_STATUS_DEVICE);
diff --git a/chrome/browser/ui/webui/policy_ui_browsertest.cc b/chrome/browser/ui/webui/policy_ui_browsertest.cc
index 38dbbb4..bc73159 100644
--- a/chrome/browser/ui/webui/policy_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/policy_ui_browsertest.cc
@@ -11,6 +11,7 @@
 #include "base/callback.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
@@ -38,6 +39,8 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
+#include "ui/shell_dialogs/select_file_dialog_factory.h"
 #include "url/gurl.h"
 
 using testing::Return;
@@ -93,6 +96,23 @@
   return expected_policy;
 }
 
+void SetExpectedPolicy(base::DictionaryValue* expected,
+                       const std::string& name,
+                       const std::string& level,
+                       const std::string& scope,
+                       const std::string& source,
+                       const base::Value& value) {
+  std::string prefix = "chromePolicies." + name + ".";
+  expected->SetString(prefix + "level", level);
+  expected->SetString(prefix + "scope", scope);
+  expected->SetString(prefix + "source", source);
+  expected->Set(prefix + "value", base::MakeUnique<base::Value>(value));
+}
+
+// The temporary directory and file paths for policy saving.
+base::ScopedTempDir export_policies_test_dir;
+base::FilePath export_policies_test_file_path;
+
 }  // namespace
 
 class PolicyUITest : public InProcessBrowserTest {
@@ -108,6 +128,8 @@
 
   void VerifyPolicies(const std::vector<std::vector<std::string> >& expected);
 
+  void VerifyExportingPolicies(const base::DictionaryValue& expected);
+
  protected:
   policy::MockConfigurationPolicyProvider provider_;
 
@@ -115,6 +137,46 @@
   DISALLOW_COPY_AND_ASSIGN(PolicyUITest);
 };
 
+// An artificial SelectFileDialog that immediately returns the location of test
+// file instead of showing the UI file picker.
+class TestSelectFileDialog : public ui::SelectFileDialog {
+ public:
+  TestSelectFileDialog(ui::SelectFileDialog::Listener* listener,
+                       ui::SelectFilePolicy* policy)
+      : ui::SelectFileDialog(listener, policy) {}
+
+  void SelectFileImpl(Type type,
+                      const base::string16& title,
+                      const base::FilePath& default_path,
+                      const FileTypeInfo* file_types,
+                      int file_type_index,
+                      const base::FilePath::StringType& default_extension,
+                      gfx::NativeWindow owning_window,
+                      void* params) override {
+    listener_->FileSelected(export_policies_test_file_path, 0, nullptr);
+  }
+
+  bool IsRunning(gfx::NativeWindow owning_window) const override {
+    return false;
+  }
+
+  void ListenerDestroyed() override {}
+
+  bool HasMultipleFileTypeChoicesImpl() override { return false; }
+
+ private:
+  ~TestSelectFileDialog() override {}
+};
+
+// A factory associated with the artificial file picker.
+class TestSelectFileDialogFactory : public ui::SelectFileDialogFactory {
+ private:
+  ui::SelectFileDialog* Create(ui::SelectFileDialog::Listener* listener,
+                               ui::SelectFilePolicy* policy) override {
+    return new TestSelectFileDialog(listener, policy);
+  }
+};
+
 PolicyUITest::PolicyUITest() {
 }
 
@@ -125,6 +187,12 @@
   EXPECT_CALL(provider_, IsInitializationComplete(_))
       .WillRepeatedly(Return(true));
   policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
+
+  // Create a directory for testing exporting policies.
+  ASSERT_TRUE(export_policies_test_dir.CreateUniqueTempDir());
+  const std::string filename = "policy.json";
+  export_policies_test_file_path =
+      export_policies_test_dir.GetPath().AppendASCII(filename);
 }
 
 void PolicyUITest::UpdateProviderPolicy(const policy::PolicyMap& policy) {
@@ -183,6 +251,120 @@
   }
 }
 
+void PolicyUITest::VerifyExportingPolicies(
+    const base::DictionaryValue& expected) {
+  // Set SelectFileDialog to use our factory.
+  ui::SelectFileDialog::SetFactory(new TestSelectFileDialogFactory());
+
+  // Navigate to the about:policy page.
+  ui_test_utils::NavigateToURL(browser(), GURL("chrome://policy"));
+
+  // Click on 'save policies' button.
+  const std::string javascript =
+      "document.getElementById(\"export-policies\").click()";
+
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  EXPECT_TRUE(content::ExecuteScript(contents, javascript));
+
+  base::TaskScheduler::GetInstance()->FlushForTesting();
+  // Open the created file.
+  base::ThreadRestrictions::ScopedAllowIO allow_io;
+  std::string file_contents;
+  EXPECT_TRUE(
+      base::ReadFileToString(export_policies_test_file_path, &file_contents));
+
+  std::unique_ptr<base::Value> value_ptr =
+      base::JSONReader::Read(file_contents);
+
+  // Check that the file contains a valid dictionary.
+  EXPECT_TRUE(value_ptr.get());
+  base::DictionaryValue* actual_policies = nullptr;
+  EXPECT_TRUE(value_ptr->GetAsDictionary(&actual_policies));
+
+  // Check that this dictionary is the same as expected.
+  EXPECT_EQ(expected, *actual_policies);
+}
+
+IN_PROC_BROWSER_TEST_F(PolicyUITest, WritePoliciesToJSONFile) {
+  // Set policy values and generate expected dictionary.
+  policy::PolicyMap values;
+  base::DictionaryValue expected_values;
+
+  base::ListValue popups_blocked_for_urls;
+  popups_blocked_for_urls.AppendString("aaa");
+  popups_blocked_for_urls.AppendString("bbb");
+  popups_blocked_for_urls.AppendString("ccc");
+  values.Set(policy::key::kPopupsBlockedForUrls, policy::POLICY_LEVEL_MANDATORY,
+             policy::POLICY_SCOPE_MACHINE, policy::POLICY_SOURCE_PLATFORM,
+             base::MakeUnique<base::Value>(popups_blocked_for_urls), nullptr);
+  SetExpectedPolicy(&expected_values, policy::key::kPopupsBlockedForUrls,
+                    "mandatory", "machine", "sourcePlatform",
+                    popups_blocked_for_urls);
+
+  values.Set(policy::key::kDefaultImagesSetting, policy::POLICY_LEVEL_MANDATORY,
+             policy::POLICY_SCOPE_MACHINE, policy::POLICY_SOURCE_CLOUD,
+             base::MakeUnique<base::Value>(2), nullptr);
+  SetExpectedPolicy(&expected_values, policy::key::kDefaultImagesSetting,
+                    "mandatory", "machine", "sourceCloud", base::Value(2));
+
+  // This also checks that we save complex policies correctly.
+  base::DictionaryValue unknown_policy;
+  base::DictionaryValue body;
+  body.SetInteger("first", 0);
+  body.SetBoolean("second", true);
+  unknown_policy.SetInteger("head", 12);
+  unknown_policy.SetDictionary("body",
+                               base::MakeUnique<base::DictionaryValue>(body));
+  const std::string kUnknownPolicy = "NoSuchThing";
+  values.Set(kUnknownPolicy, policy::POLICY_LEVEL_RECOMMENDED,
+             policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
+             base::MakeUnique<base::Value>(unknown_policy), nullptr);
+  SetExpectedPolicy(&expected_values, kUnknownPolicy, "recommended", "user",
+                    "sourceCloud", unknown_policy);
+
+  // Set the extension policies to an empty dictionary as we haven't added any
+  // such policies.
+  expected_values.SetDictionary("extensionPolicies",
+                                base::MakeUnique<base::DictionaryValue>());
+
+  UpdateProviderPolicy(values);
+
+  // Check writing those policies to a newly created file.
+  VerifyExportingPolicies(expected_values);
+
+  // Change policy values.
+  values.Erase(policy::key::kDefaultImagesSetting);
+  expected_values.RemovePath(
+      std::string("chromePolicies.") +
+          std::string(policy::key::kDefaultImagesSetting),
+      nullptr);
+
+  // This also checks that we bypass the policy that blocks file selection
+  // dialogs.
+  values.Set(policy::key::kAllowFileSelectionDialogs,
+             policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_MACHINE,
+             policy::POLICY_SOURCE_PLATFORM,
+             base::MakeUnique<base::Value>(false), nullptr);
+  SetExpectedPolicy(&expected_values, policy::key::kAllowFileSelectionDialogs,
+                    "mandatory", "machine", "sourcePlatform",
+                    base::Value(false));
+
+  popups_blocked_for_urls.AppendString("ddd");
+  values.Set(policy::key::kPopupsBlockedForUrls, policy::POLICY_LEVEL_MANDATORY,
+             policy::POLICY_SCOPE_MACHINE, policy::POLICY_SOURCE_PLATFORM,
+             base::MakeUnique<base::Value>(popups_blocked_for_urls), nullptr);
+  SetExpectedPolicy(&expected_values, policy::key::kPopupsBlockedForUrls,
+                    "mandatory", "machine", "sourcePlatform",
+                    popups_blocked_for_urls);
+
+  UpdateProviderPolicy(values);
+
+  // Check writing changed policies to the same file (should overwrite the
+  // contents).
+  VerifyExportingPolicies(expected_values);
+}
+
 IN_PROC_BROWSER_TEST_F(PolicyUITest, SendPolicyNames) {
   // Verifies that the names of known policies are sent to the UI and processed
   // there correctly by checking that the policy table contains all policies in
diff --git a/chrome/browser/ui/webui/policy_ui_handler.cc b/chrome/browser/ui/webui/policy_ui_handler.cc
index 42f02b4..7f3c502b 100644
--- a/chrome/browser/ui/webui/policy_ui_handler.cc
+++ b/chrome/browser/ui/webui/policy_ui_handler.cc
@@ -12,16 +12,20 @@
 #include "base/bind_helpers.h"
 #include "base/callback.h"
 #include "base/compiler_specific.h"
+#include "base/files/file_util.h"
 #include "base/json/json_writer.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
 #include "base/time/time.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/policy/schema_registry_service.h"
@@ -180,9 +184,13 @@
   return base::MakeUnique<base::Value>(json_string);
 }
 
-// Returns a copy of |value| with some values converted to a representation that
-// i18n_template.js will display in a nicer way.
-std::unique_ptr<base::Value> CopyAndConvert(const base::Value* value) {
+// Returns a copy of |value|. If necessary (which is specified by
+// |convert_values|), converts some values to a representation that
+// i18n_template.js will display.
+std::unique_ptr<base::Value> CopyAndMaybeConvert(const base::Value* value,
+                                                 bool convert_values) {
+  if (!convert_values)
+    return value->CreateDeepCopy();
   const base::DictionaryValue* dict = NULL;
   if (value->GetAsDictionary(&dict))
     return DictionaryToJSONString(*dict);
@@ -597,6 +605,10 @@
       "reloadPolicies",
       base::Bind(&PolicyUIHandler::HandleReloadPolicies,
                  base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "exportPoliciesJSON",
+      base::Bind(&PolicyUIHandler::HandleExportPoliciesJSON,
+                 base::Unretained(this)));
 }
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
@@ -687,12 +699,13 @@
   web_ui()->CallJavascriptFunctionUnsafe("policy.Page.setPolicyNames", names);
 }
 
-void PolicyUIHandler::SendPolicyValues() const {
+std::unique_ptr<base::DictionaryValue> PolicyUIHandler::GetAllPolicyValues(
+    bool convert_values) const {
   base::DictionaryValue all_policies;
 
   // Add Chrome policy values.
   auto chrome_policies = base::MakeUnique<base::DictionaryValue>();
-  GetChromePolicyValues(chrome_policies.get());
+  GetChromePolicyValues(chrome_policies.get(), convert_values);
   all_policies.Set("chromePolicies", std::move(chrome_policies));
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
@@ -712,21 +725,29 @@
         policy::POLICY_DOMAIN_EXTENSIONS, extension->id());
     policy::PolicyErrorMap empty_error_map;
     GetPolicyValues(GetPolicyService()->GetPolicies(policy_namespace),
-                    &empty_error_map, extension_policies.get());
+                    &empty_error_map, extension_policies.get(), convert_values);
     extension_values->Set(extension->id(), std::move(extension_policies));
   }
   all_policies.Set("extensionPolicies", std::move(extension_values));
 #endif
+  return base::MakeUnique<base::DictionaryValue>(all_policies);
+}
+
+void PolicyUIHandler::SendPolicyValues() const {
+  std::unique_ptr<base::DictionaryValue> all_policies =
+      GetAllPolicyValues(true);
   web_ui()->CallJavascriptFunctionUnsafe("policy.Page.setPolicyValues",
-                                         all_policies);
+                                         *all_policies);
 }
 
 void PolicyUIHandler::GetPolicyValues(const policy::PolicyMap& map,
                                       policy::PolicyErrorMap* errors,
-                                      base::DictionaryValue* values) const {
+                                      base::DictionaryValue* values,
+                                      bool convert_values) const {
   for (const auto& entry : map) {
     std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue);
-    value->Set("value", CopyAndConvert(entry.second.value.get()));
+    value->Set("value",
+               CopyAndMaybeConvert(entry.second.value.get(), convert_values));
     if (entry.second.scope == policy::POLICY_SCOPE_USER)
       value->SetString("scope", "user");
     else
@@ -743,8 +764,8 @@
   }
 }
 
-void PolicyUIHandler::GetChromePolicyValues(
-    base::DictionaryValue* values) const {
+void PolicyUIHandler::GetChromePolicyValues(base::DictionaryValue* values,
+                                            bool convert_values) const {
   policy::PolicyService* policy_service = GetPolicyService();
   policy::PolicyMap map;
 
@@ -762,7 +783,7 @@
   // Convert dictionary values to strings for display.
   handler_list->PrepareForDisplaying(&map);
 
-  GetPolicyValues(map, &errors, values);
+  GetPolicyValues(map, &errors, values, convert_values);
 }
 
 void PolicyUIHandler::SendStatus() const {
@@ -815,6 +836,70 @@
       &PolicyUIHandler::OnRefreshPoliciesDone, weak_factory_.GetWeakPtr()));
 }
 
+void DoWritePoliciesToJSONFile(const base::FilePath& path,
+                               const std::string& data) {
+  base::WriteFile(path, data.c_str(), data.size());
+}
+
+void PolicyUIHandler::WritePoliciesToJSONFile(
+    const base::FilePath& path) const {
+  std::unique_ptr<base::DictionaryValue> all_policies =
+      GetAllPolicyValues(false);
+  std::string json_policies =
+      DictionaryToJSONString(*all_policies)->GetString();
+
+  base::PostTaskWithTraits(
+      FROM_HERE,
+      {base::MayBlock(), base::TaskPriority::BACKGROUND,
+       base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
+      base::BindOnce(&DoWritePoliciesToJSONFile, path, json_policies));
+}
+
+void PolicyUIHandler::FileSelected(const base::FilePath& path,
+                                   int index,
+                                   void* params) {
+  DCHECK(export_policies_select_file_dialog_);
+
+  WritePoliciesToJSONFile(path);
+
+  export_policies_select_file_dialog_ = nullptr;
+}
+
+void PolicyUIHandler::FileSelectionCanceled(void* params) {
+  DCHECK(export_policies_select_file_dialog_);
+  export_policies_select_file_dialog_ = nullptr;
+}
+
+void PolicyUIHandler::HandleExportPoliciesJSON(const base::ListValue* args) {
+  // If the "select file" dialog window is already opened, we don't want to open
+  // it again.
+  if (export_policies_select_file_dialog_)
+    return;
+
+  content::WebContents* webcontents = web_ui()->GetWebContents();
+
+  // Building initial path based on download preferences.
+  base::FilePath initial_dir =
+      DownloadPrefs::FromBrowserContext(webcontents->GetBrowserContext())
+          ->DownloadPath();
+  base::FilePath initial_path =
+      initial_dir.Append(FILE_PATH_LITERAL("policies.json"));
+
+  // Here we overwrite the actual value of SelectFileDialog policy by passing a
+  // nullptr to ui::SelectFileDialog::Create instead of the actual policy value.
+  // This is done for the following reason: the admin might want to set this
+  // policy for the user to forbid the select file dialogs, but this shouldn't
+  // block the possibility to export the policies.
+  export_policies_select_file_dialog_ =
+      ui::SelectFileDialog::Create(this, nullptr);
+  ui::SelectFileDialog::FileTypeInfo file_type_info;
+  file_type_info.extensions = {{FILE_PATH_LITERAL("json")}};
+  gfx::NativeWindow owning_window = webcontents->GetTopLevelNativeWindow();
+  export_policies_select_file_dialog_->SelectFile(
+      ui::SelectFileDialog::SELECT_SAVEAS_FILE, base::string16(), initial_path,
+      &file_type_info, 0, base::FilePath::StringType(), owning_window, nullptr);
+}
+
 void PolicyUIHandler::OnRefreshPoliciesDone() const {
   web_ui()->CallJavascriptFunctionUnsafe("policy.Page.reloadPoliciesDone");
 }
diff --git a/chrome/browser/ui/webui/policy_ui_handler.h b/chrome/browser/ui/webui/policy_ui_handler.h
index 1201ce0..e181e422 100644
--- a/chrome/browser/ui/webui/policy_ui_handler.h
+++ b/chrome/browser/ui/webui/policy_ui_handler.h
@@ -21,6 +21,7 @@
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/browser/web_ui_message_handler.h"
 #include "extensions/features/features.h"
+#include "ui/shell_dialogs/select_file_dialog.h"
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/browser/extension_registry_observer.h"
@@ -39,7 +40,8 @@
                         public extensions::ExtensionRegistryObserver,
 #endif
                         public policy::PolicyService::Observer,
-                        public policy::SchemaRegistry::Observer {
+                        public policy::SchemaRegistry::Observer,
+                        public ui::SelectFileDialog::Listener {
  public:
   PolicyUIHandler();
   ~PolicyUIHandler() override;
@@ -79,6 +81,12 @@
   // Send a dictionary containing the names of all known policies to the UI.
   virtual void SendPolicyNames() const;
 
+  // ui::SelectFileDialog::Listener implementation.
+  void FileSelected(const base::FilePath& path,
+                    int index,
+                    void* params) override;
+  void FileSelectionCanceled(void* params) override;
+
  private:
   // Send information about the current policy values to the UI. For each policy
   // whose value has been set, a dictionary containing the value and additional
@@ -91,15 +99,26 @@
   void SendStatus() const;
 
   // Inserts a description of each policy in |policy_map| into |values|, using
-  // the optional errors in |errors| to determine the status of each policy.
+  // the optional errors in |errors| to determine the status of each policy. If
+  // |convert_values| is true, converts the values to show them in javascript.
   void GetPolicyValues(const policy::PolicyMap& policy_map,
                        policy::PolicyErrorMap* errors,
-                       base::DictionaryValue* values) const;
+                       base::DictionaryValue* values,
+                       bool convert_values) const;
 
-  void GetChromePolicyValues(base::DictionaryValue* values) const;
+  // Returns a dictionary with the values of all set policies, with some values
+  // converted to be shown in javascript, if it is specified.
+  std::unique_ptr<base::DictionaryValue> GetAllPolicyValues(
+      bool convert_values) const;
+
+  void GetChromePolicyValues(base::DictionaryValue* values,
+                             bool convert_values) const;
+
+  void WritePoliciesToJSONFile(const base::FilePath& path) const;
 
   void HandleInitialized(const base::ListValue* args);
   void HandleReloadPolicies(const base::ListValue* args);
+  void HandleExportPoliciesJSON(const base::ListValue* args);
 
   void OnRefreshPoliciesDone() const;
 
@@ -107,6 +126,8 @@
 
   std::string device_domain_;
 
+  scoped_refptr<ui::SelectFileDialog> export_policies_select_file_dialog_;
+
   // Providers that supply status dictionaries for user and device policy,
   // respectively. These are created on initialization time as appropriate for
   // the platform (Chrome OS / desktop) and type of policy that is in effect.
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 792243a4..5e54afaa 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -1648,7 +1648,13 @@
 
 void AddSearchStrings(content::WebUIDataSource* html_source) {
   LocalizedString localized_strings[] = {
+#if defined(OS_CHROMEOS)
+    {"searchPageTitle", chromeos::switches::IsVoiceInteractionEnabled()
+                            ? IDS_SETTINGS_SEARCH_AND_ASSISTANT
+                            : IDS_SETTINGS_SEARCH},
+#else
     {"searchPageTitle", IDS_SETTINGS_SEARCH},
+#endif
     {"searchEnginesManage", IDS_SETTINGS_SEARCH_MANAGE_SEARCH_ENGINES},
     {"searchOkGoogleLabel", IDS_SETTINGS_SEARCH_OK_GOOGLE_LABEL},
 #if defined(OS_CHROMEOS)
diff --git a/chrome/common/logging_chrome.cc b/chrome/common/logging_chrome.cc
index dc58f8d2..8899916 100644
--- a/chrome/common/logging_chrome.cc
+++ b/chrome/common/logging_chrome.cc
@@ -161,21 +161,51 @@
 base::FilePath SetUpSymlinkIfNeeded(const base::FilePath& symlink_path,
                                     bool new_log) {
   DCHECK(!symlink_path.empty());
+  // For backward compatibility, set up a .../chrome symlink to
+  // .../chrome.LATEST as needed.  This code needs to run only
+  // after the migration (i.e. the addition of chrome.LATEST).
+  if (symlink_path.Extension() == ".LATEST") {
+    base::FilePath extensionless_path = symlink_path.ReplaceExtension("");
+    base::FilePath target_path;
 
-  // If not starting a new log, then just log through the existing
-  // symlink, but if the symlink doesn't exist, create it.  If
-  // starting a new log, then delete the old symlink and make a new
-  // one to a fresh log file.
+    base::ReadSymbolicLink(extensionless_path, &target_path);
+    if (target_path != symlink_path) {
+      // No link, or wrong link.  Clean up.  This should happen only once in
+      // each log directory after the OS version update, but some of those
+      // directories may not be accessed for a long time, so this code needs to
+      // stay in forever :/
+      if (base::PathExists(extensionless_path) &&
+          !base::DeleteFile(extensionless_path, false)) {
+        DPLOG(WARNING) << "Cannot delete " << extensionless_path.value();
+      }
+      // After cleaning up, create the symlink.
+      if (!base::CreateSymbolicLink(symlink_path, extensionless_path)) {
+        DPLOG(ERROR) << "Cannot create " << extensionless_path.value();
+      }
+    }
+  }
+
+  // If not starting a new log, then just log through the existing symlink, but
+  // if the symlink doesn't exist, create it.
+  //
+  // If starting a new log, then rename the old symlink as
+  // symlink_path.PREVIOUS and make a new symlink to a fresh log file.
   base::FilePath target_path;
   bool symlink_exists = base::PathExists(symlink_path);
   if (new_log || !symlink_exists) {
     target_path = GenerateTimestampedName(symlink_path, base::Time::Now());
 
-    // We don't care if the unlink fails; we're going to continue anyway.
-    if (::unlink(symlink_path.value().c_str()) == -1) {
-      if (symlink_exists) // only warn if we might expect it to succeed.
-        DPLOG(WARNING) << "Unable to unlink " << symlink_path.value();
+    if (symlink_exists) {
+      base::FilePath previous_symlink_path =
+          symlink_path.ReplaceExtension(".PREVIOUS");
+      // Rename symlink to .PREVIOUS.  This nukes an existing symlink just like
+      // the rename(2) syscall does.
+      if (!base::ReplaceFile(symlink_path, previous_symlink_path, nullptr)) {
+        DPLOG(WARNING) << "Cannot rename " << symlink_path.value() << " to "
+                       << previous_symlink_path.value();
+      }
     }
+    // If all went well, the symlink no longer exists.  Recreate it.
     if (!base::CreateSymbolicLink(target_path, symlink_path)) {
       DPLOG(ERROR) << "Unable to create symlink " << symlink_path.value()
                    << " pointing at " << target_path.value();
@@ -231,7 +261,6 @@
                        OldFileDeletionState delete_old_log_file) {
   DCHECK(!chrome_logging_initialized_) <<
     "Attempted to initialize logging when it was already initialized.";
-
   LoggingDestination logging_dest = DetermineLoggingDestination(command_line);
   LogLockingState log_locking_state = LOCK_LOG_FILE;
   base::FilePath log_path;
@@ -379,10 +408,17 @@
 bool DialogsAreSuppressed() {
   return dialogs_are_suppressed_;
 }
+
+#if defined(OS_CHROMEOS)
 base::FilePath GenerateTimestampedName(const base::FilePath& base_path,
                                        base::Time timestamp) {
   base::Time::Exploded time_deets;
   timestamp.LocalExplode(&time_deets);
+  base::FilePath new_path = base_path;
+  // Assume that the base_path is "chrome.LATEST", and remove the extension.
+  // Ideally we would also check the value of base_path, but we cannot reliably
+  // log anything here, and aborting seems too harsh a choice.
+  new_path = new_path.ReplaceExtension("");
   std::string suffix = base::StringPrintf("_%02d%02d%02d-%02d%02d%02d",
                                           time_deets.year,
                                           time_deets.month,
@@ -390,7 +426,8 @@
                                           time_deets.hour,
                                           time_deets.minute,
                                           time_deets.second);
-  return base_path.InsertBeforeExtensionASCII(suffix);
+  return new_path.InsertBeforeExtensionASCII(suffix);
 }
+#endif  // defined(OS_CHROMEOS)
 
 }  // namespace logging
diff --git a/chrome/common/logging_chrome.h b/chrome/common/logging_chrome.h
index 42ba0343..0b00997 100644
--- a/chrome/common/logging_chrome.h
+++ b/chrome/common/logging_chrome.h
@@ -61,10 +61,12 @@
 // otherwise.
 bool DialogsAreSuppressed();
 
-// Inserts timestamp before file extension in the format
+#if defined(OS_CHROMEOS)
+// Inserts timestamp before file extension (if any) in the form
 // "_yymmdd-hhmmss".
 base::FilePath GenerateTimestampedName(const base::FilePath& base_path,
                                        base::Time timestamp);
+#endif
 }  // namespace logging
 
 #endif  // CHROME_COMMON_LOGGING_CHROME_H_
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 128f68b7..9df6bbc 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3637,6 +3637,7 @@
       "../browser/media_galleries/media_galleries_preferences_unittest.cc",
       "../browser/media_galleries/win/mtp_device_delegate_impl_win_unittest.cc",
       "../browser/media_galleries/win/mtp_device_object_enumerator_unittest.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/tab_manager_delegate_chromeos_unittest.cc",
       "../browser/resource_coordinator/tab_manager_stats_collector_unittest.cc",
diff --git a/chrome/test/ppapi/ppapi_filechooser_browsertest.cc b/chrome/test/ppapi/ppapi_filechooser_browsertest.cc
index 3d7d234..0214d7c 100644
--- a/chrome/test/ppapi/ppapi_filechooser_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_filechooser_browsertest.cc
@@ -349,7 +349,7 @@
 
   RunTestViaHTTP("FileChooser_SaveAsUnsafeDefaultName");
   base::FilePath actual_filename =
-      temp_dir.GetPath().AppendASCII("unsafe.txt-");
+      temp_dir.GetPath().AppendASCII("unsafe.txt_");
 
   ASSERT_TRUE(base::PathExists(actual_filename));
   std::string file_contents;
diff --git a/components/arc/common/app.mojom b/components/arc/common/app.mojom
index 9f31167..bfdf4ecf 100644
--- a/components/arc/common/app.mojom
+++ b/components/arc/common/app.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Next MinVersion: 22
+// Next MinVersion: 23
 
 module arc.mojom;
 
@@ -86,6 +86,7 @@
   string? formatted_price;
   float review_score;
   array<uint8> icon_png_data;
+  [MinVersion=22] string? package_name;
 };
 
 // Describes the status of an app discovery request.
diff --git a/components/arc/test/fake_app_instance.cc b/components/arc/test/fake_app_instance.cc
index da10ceb..7b1e74c 100644
--- a/components/arc/test/fake_app_instance.cc
+++ b/components/arc/test/fake_app_instance.cc
@@ -285,13 +285,26 @@
     const GetRecentAndSuggestedAppsFromPlayStoreCallback& callback) {
   // Fake Play Store app info
   std::vector<arc::mojom::AppDiscoveryResultPtr> fake_apps;
-  for (int i = 0; i < max_results; ++i) {
-    // Fake icon data
-    std::string png_data_as_string;
-    GetFakeIcon(mojom::ScaleFactor::SCALE_FACTOR_100P, &png_data_as_string);
-    std::vector<uint8_t> fake_icon_png_data(png_data_as_string.begin(),
-                                            png_data_as_string.end());
 
+  // Fake icon data.
+  std::string png_data_as_string;
+  GetFakeIcon(mojom::ScaleFactor::SCALE_FACTOR_100P, &png_data_as_string);
+  std::vector<uint8_t> fake_icon_png_data(png_data_as_string.begin(),
+                                          png_data_as_string.end());
+
+  fake_apps.push_back(mojom::AppDiscoveryResult::New(
+      std::string("LauncherIntentUri"),        // launch_intent_uri
+      std::string("InstallIntentUri"),         // install_intent_uri
+      std::string(query),                      // label
+      false,                                   // is_instant_app
+      false,                                   // is_recent
+      std::string("Publisher"),                // publisher_name
+      std::string("$7.22"),                    // formatted_price
+      5,                                       // review_score
+      fake_icon_png_data,                      // icon_png_data
+      std::string("com.google.android.gm")));  // package_name
+
+  for (int i = 0; i < max_results - 1; ++i) {
     fake_apps.push_back(mojom::AppDiscoveryResult::New(
         base::StringPrintf("LauncherIntentUri %d", i),  // launch_intent_uri
         base::StringPrintf("InstallIntentUri %d", i),   // install_intent_uri
@@ -301,7 +314,8 @@
         base::StringPrintf("Publisher %d", i),          // publisher_name
         base::StringPrintf("$%d.22", i),                // formatted_price
         i,                                              // review_score
-        fake_icon_png_data));                           // icon_png_data
+        fake_icon_png_data,                             // icon_png_data
+        base::StringPrintf("test.package.%d", i)));     // package_name
   }
   callback.Run(arc::mojom::AppDiscoveryRequestState::SUCCESS,
                std::move(fake_apps));
diff --git a/components/autofill/content/common/autofill_types_struct_traits.cc b/components/autofill/content/common/autofill_types_struct_traits.cc
index d76e5b0..ed4c5c4 100644
--- a/components/autofill/content/common/autofill_types_struct_traits.cc
+++ b/components/autofill/content/common/autofill_types_struct_traits.cc
@@ -604,24 +604,25 @@
 }
 
 // static
-void* StructTraits<mojom::PasswordFormFieldPredictionMapDataView,
-                   PasswordFormFieldPredictionMap>::
-    SetUpContext(const PasswordFormFieldPredictionMap& r) {
-  // Extracts keys vector and values vector from the map, saves them as a pair.
-  auto* pair = new KeysValuesPair();
-  for (const auto& i : r) {
-    pair->first.push_back(i.first);
-    pair->second.push_back(i.second);
-  }
-
-  return pair;
+std::vector<autofill::FormFieldData> StructTraits<
+    mojom::PasswordFormFieldPredictionMapDataView,
+    PasswordFormFieldPredictionMap>::keys(const PasswordFormFieldPredictionMap&
+                                              r) {
+  std::vector<autofill::FormFieldData> data;
+  for (const auto& i : r)
+    data.push_back(i.first);
+  return data;
 }
 
 // static
-void StructTraits<mojom::PasswordFormFieldPredictionMapDataView,
-                  PasswordFormFieldPredictionMap>::
-    TearDownContext(const PasswordFormFieldPredictionMap& r, void* context) {
-  delete static_cast<KeysValuesPair*>(context);
+std::vector<autofill::PasswordFormFieldPredictionType>
+StructTraits<mojom::PasswordFormFieldPredictionMapDataView,
+             PasswordFormFieldPredictionMap>::
+    values(const PasswordFormFieldPredictionMap& r) {
+  std::vector<autofill::PasswordFormFieldPredictionType> types;
+  for (const auto& i : r)
+    types.push_back(i.second);
+  return types;
 }
 
 // static
@@ -646,23 +647,23 @@
 }
 
 // static
-void* StructTraits<mojom::FormsPredictionsMapDataView,
-                   FormsPredictionsMap>::SetUpContext(const FormsPredictionsMap&
-                                                          r) {
-  // Extracts keys vector and values vector from the map, saves them as a pair.
-  auto* pair = new KeysValuesPair();
-  for (const auto& i : r) {
-    pair->first.push_back(i.first);
-    pair->second.push_back(i.second);
-  }
-
-  return pair;
+std::vector<autofill::FormData>
+StructTraits<mojom::FormsPredictionsMapDataView, FormsPredictionsMap>::keys(
+    const FormsPredictionsMap& r) {
+  std::vector<autofill::FormData> data;
+  for (const auto& i : r)
+    data.push_back(i.first);
+  return data;
 }
 
 // static
-void StructTraits<mojom::FormsPredictionsMapDataView, FormsPredictionsMap>::
-    TearDownContext(const FormsPredictionsMap& r, void* context) {
-  delete static_cast<KeysValuesPair*>(context);
+std::vector<autofill::PasswordFormFieldPredictionMap>
+StructTraits<mojom::FormsPredictionsMapDataView, FormsPredictionsMap>::values(
+    const FormsPredictionsMap& r) {
+  std::vector<autofill::PasswordFormFieldPredictionMap> maps;
+  for (const auto& i : r)
+    maps.push_back(i.second);
+  return maps;
 }
 
 // static
diff --git a/components/autofill/content/common/autofill_types_struct_traits.h b/components/autofill/content/common/autofill_types_struct_traits.h
index 0c51ed5..b98c9dc 100644
--- a/components/autofill/content/common/autofill_types_struct_traits.h
+++ b/components/autofill/content/common/autofill_types_struct_traits.h
@@ -519,26 +519,11 @@
 template <>
 struct StructTraits<autofill::mojom::PasswordFormFieldPredictionMapDataView,
                     autofill::PasswordFormFieldPredictionMap> {
-  using KeysValuesPair =
-      std::pair<std::vector<autofill::FormFieldData>,
-                std::vector<autofill::PasswordFormFieldPredictionType>>;
+  static std::vector<autofill::FormFieldData> keys(
+      const autofill::PasswordFormFieldPredictionMap& r);
 
-  static void* SetUpContext(const autofill::PasswordFormFieldPredictionMap& r);
-
-  static void TearDownContext(const autofill::PasswordFormFieldPredictionMap& r,
-                              void* context);
-
-  static const std::vector<autofill::FormFieldData>& keys(
-      const autofill::PasswordFormFieldPredictionMap& r,
-      void* context) {
-    return static_cast<KeysValuesPair*>(context)->first;
-  }
-
-  static const std::vector<autofill::PasswordFormFieldPredictionType>& values(
-      const autofill::PasswordFormFieldPredictionMap& r,
-      void* context) {
-    return static_cast<KeysValuesPair*>(context)->second;
-  }
+  static std::vector<autofill::PasswordFormFieldPredictionType> values(
+      const autofill::PasswordFormFieldPredictionMap& r);
 
   static bool Read(autofill::mojom::PasswordFormFieldPredictionMapDataView data,
                    autofill::PasswordFormFieldPredictionMap* out);
@@ -547,26 +532,11 @@
 template <>
 struct StructTraits<autofill::mojom::FormsPredictionsMapDataView,
                     autofill::FormsPredictionsMap> {
-  using KeysValuesPair =
-      std::pair<std::vector<autofill::FormData>,
-                std::vector<autofill::PasswordFormFieldPredictionMap>>;
+  static std::vector<autofill::FormData> keys(
+      const autofill::FormsPredictionsMap& r);
 
-  static void* SetUpContext(const autofill::FormsPredictionsMap& r);
-
-  static void TearDownContext(const autofill::FormsPredictionsMap& r,
-                              void* context);
-
-  static const std::vector<autofill::FormData>& keys(
-      const autofill::FormsPredictionsMap& r,
-      void* context) {
-    return static_cast<KeysValuesPair*>(context)->first;
-  }
-
-  static const std::vector<autofill::PasswordFormFieldPredictionMap>& values(
-      const autofill::FormsPredictionsMap& r,
-      void* context) {
-    return static_cast<KeysValuesPair*>(context)->second;
-  }
+  static std::vector<autofill::PasswordFormFieldPredictionMap> values(
+      const autofill::FormsPredictionsMap& r);
 
   static bool Read(autofill::mojom::FormsPredictionsMapDataView data,
                    autofill::FormsPredictionsMap* out);
diff --git a/components/payments/core/payments_profile_comparator.cc b/components/payments/core/payments_profile_comparator.cc
index faeab2d7..23f7c865 100644
--- a/components/payments/core/payments_profile_comparator.cc
+++ b/components/payments/core/payments_profile_comparator.cc
@@ -142,6 +142,12 @@
                                    GetRequiredProfileFieldsForContact());
 }
 
+base::string16 PaymentsProfileComparator::GetTitleForMissingContactFields(
+    const autofill::AutofillProfile& profile) const {
+  return GetTitleForMissingFields(GetMissingProfileFields(&profile) &
+                                  GetRequiredProfileFieldsForContact());
+}
+
 std::vector<autofill::AutofillProfile*>
 PaymentsProfileComparator::FilterProfilesForShipping(
     const std::vector<autofill::AutofillProfile*>& profiles) const {
@@ -266,6 +272,28 @@
   }
 }
 
+base::string16 PaymentsProfileComparator::GetTitleForMissingFields(
+    PaymentsProfileComparator::ProfileFields fields) const {
+  switch (fields) {
+    case 0:
+      NOTREACHED() << "Title should not be requested if no fields are missing";
+      return base::string16();
+    case kName:
+      return l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_NAME);
+    case kPhone:
+      return l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_PHONE_NUMBER);
+    case kEmail:
+      return l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_EMAIL);
+    case kAddress:
+      return l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_VALID_ADDRESS);
+    default:
+      // Either multiple bits are set (likely) or one bit that doesn't
+      // correspond to a named constant is set (shouldn't happen). Return a
+      // generic "More information" message.
+      return l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_MORE_INFORMATION);
+  }
+}
+
 bool PaymentsProfileComparator::AreRequiredAddressFieldsPresent(
     const autofill::AutofillProfile& profile) const {
   std::unique_ptr<::i18n::addressinput::AddressData> data =
diff --git a/components/payments/core/payments_profile_comparator.h b/components/payments/core/payments_profile_comparator.h
index 61e7609..ab7ea02 100644
--- a/components/payments/core/payments_profile_comparator.h
+++ b/components/payments/core/payments_profile_comparator.h
@@ -89,6 +89,12 @@
   base::string16 GetStringForMissingContactFields(
       const autofill::AutofillProfile& profile) const;
 
+  // Returns a localized string to be displayed as the title of a piece of UI,
+  // indicating what action must be taken for the given profile to be used as
+  // contact info.
+  base::string16 GetTitleForMissingContactFields(
+      const autofill::AutofillProfile& profile) const;
+
   // Returns a localized string to be displayed in UI indicating what action,
   // if any, must be taken for the given profile to be used as a shipping
   // address.
@@ -105,6 +111,7 @@
   ProfileFields GetRequiredProfileFieldsForContact() const;
   ProfileFields GetRequiredProfileFieldsForShipping() const;
   base::string16 GetStringForMissingFields(ProfileFields fields) const;
+  base::string16 GetTitleForMissingFields(ProfileFields fields) const;
   bool AreRequiredAddressFieldsPresent(
       const autofill::AutofillProfile& profile) const;
 
diff --git a/components/payments/core/payments_profile_comparator_unittest.cc b/components/payments/core/payments_profile_comparator_unittest.cc
index 18f3e58a..a1b0dc7 100644
--- a/components/payments/core/payments_profile_comparator_unittest.cc
+++ b/components/payments/core/payments_profile_comparator_unittest.cc
@@ -417,6 +417,35 @@
             comp.GetStringForMissingContactFields(p5));
 }
 
+TEST(PaymentRequestProfileUtilTest, GetTitleForMissingContactFields) {
+  MockPaymentOptionsProvider provider(kRequestPayerName | kRequestPayerPhone |
+                                      kRequestPayerEmail | kRequestShipping);
+  PaymentsProfileComparator comp("en-US", provider);
+
+  // Error message for email address if email address is missing and required.
+  AutofillProfile p1 = CreateProfileWithContactInfo("Homer", "", "6515553226");
+  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_EMAIL),
+            comp.GetTitleForMissingContactFields(p1));
+
+  // Error message for phone number if phone is missing and required.
+  AutofillProfile p2 =
+      CreateProfileWithContactInfo("Homer", "homer@simpson.net", "");
+  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_PHONE_NUMBER),
+            comp.GetTitleForMissingContactFields(p2));
+
+  // Error message for name if name is missing and required.
+  AutofillProfile p3 =
+      CreateProfileWithContactInfo("", "homer@simpson.net", "6515553226");
+  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_NAME),
+            comp.GetTitleForMissingContactFields(p3));
+
+  // Generic error message if multiple fields missing.
+  AutofillProfile p4 =
+      CreateProfileWithContactInfo("", "homer@simpson.net", "");
+  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_MORE_INFORMATION),
+            comp.GetTitleForMissingContactFields(p4));
+}
+
 TEST(PaymentRequestProfileUtilTest, GetStringForMissingShippingFields) {
   MockPaymentOptionsProvider provider(kRequestPayerName | kRequestPayerPhone |
                                       kRequestPayerEmail | kRequestShipping);
diff --git a/components/policy_strings.grdp b/components/policy_strings.grdp
index 4b96b81..2d0599b 100644
--- a/components/policy_strings.grdp
+++ b/components/policy_strings.grdp
@@ -234,6 +234,9 @@
   <message name="IDS_POLICY_RELOAD_POLICIES" desc="Label for the button that reloads policies.">
     Reload policies
   </message>
+  <message name="IDS_EXPORT_POLICIES_JSON" desc="Label for the button that exports policies in JSON format.">
+    Export to JSON
+  </message>
   <message name="IDS_POLICY_CHROME_FOR_WORK" desc="Title of the link to the chrome for work website.">
     Using Chrome at work? Businesses can manage Chrome settings for their employees. Learn more
   </message>
diff --git a/components/sync/driver/about_sync_util.cc b/components/sync/driver/about_sync_util.cc
index 3da933e..414a952 100644
--- a/components/sync/driver/about_sync_util.cc
+++ b/components/sync/driver/about_sync_util.cc
@@ -336,14 +336,15 @@
                                         "Sync Backend Initialization");
   BoolSyncStat is_syncing(section_local, "Syncing");
   BoolSyncStat is_local_sync_enabled(section_local,
-                                     "Local sync backend enabled");
-  StringSyncStat local_backend_path(section_local, "Local backend path");
+                                     "Local Sync Backend Enabled");
+  StringSyncStat local_backend_path(section_local, "Local Backend Path");
 
   base::ListValue* section_network = AddSection(stats_list.get(), "Network");
   // TODO(crbug.com/702230): Remove the usages of raw pointers in this file.
   section_network->Reserve(3);
-  BoolSyncStat is_throttled(section_network, "Throttled");
-  StringSyncStat retry_time(section_network, "Retry time (maybe stale)");
+  BoolSyncStat is_any_throttled_or_backoff(section_network,
+                                           "Throttled or Backoff");
+  StringSyncStat retry_time(section_network, "Retry Time");
   BoolSyncStat are_notifications_enabled(section_network,
                                          "Notifications Enabled");
 
@@ -473,7 +474,7 @@
   }
 
   if (snapshot.is_initialized())
-    is_throttled.SetValue(snapshot.is_silenced());
+    is_any_throttled_or_backoff.SetValue(snapshot.is_silenced());
   if (is_status_valid) {
     are_notifications_enabled.SetValue(full_status.notifications_enabled);
   }
diff --git a/components/sync/engine_impl/cycle/sync_cycle.cc b/components/sync/engine_impl/cycle/sync_cycle.cc
index a8cf982..ed53404 100644
--- a/components/sync/engine_impl/cycle/sync_cycle.cc
+++ b/components/sync/engine_impl/cycle/sync_cycle.cc
@@ -42,7 +42,7 @@
 
   SyncCycleSnapshot snapshot(
       status_controller_->model_neutral_state(), download_progress_markers,
-      delegate_->IsCurrentlyThrottled(),
+      delegate_->IsAnyThrottleOrBackoff(),
       status_controller_->num_encryption_conflicts(),
       status_controller_->num_hierarchy_conflicts(),
       status_controller_->num_server_conflicts(),
diff --git a/components/sync/engine_impl/cycle/sync_cycle.h b/components/sync/engine_impl/cycle/sync_cycle.h
index 0d8804e..11201dd 100644
--- a/components/sync/engine_impl/cycle/sync_cycle.h
+++ b/components/sync/engine_impl/cycle/sync_cycle.h
@@ -56,7 +56,7 @@
     // solely based on absolute time values. So, this cannot be used to infer
     // that any given cycle _instance_ is silenced.  An example of reasonable
     // use is for UI reporting.
-    virtual bool IsCurrentlyThrottled() = 0;
+    virtual bool IsAnyThrottleOrBackoff() = 0;
 
     // The client has been instructed to change its short poll interval.
     virtual void OnReceivedShortPollIntervalUpdate(
diff --git a/components/sync/engine_impl/sync_scheduler_impl.cc b/components/sync/engine_impl/sync_scheduler_impl.cc
index 58d3e57..8595a3d2 100644
--- a/components/sync/engine_impl/sync_scheduler_impl.cc
+++ b/components/sync/engine_impl/sync_scheduler_impl.cc
@@ -287,12 +287,12 @@
 bool SyncSchedulerImpl::CanRunJobNow(JobPriority priority) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (IsCurrentlyThrottled()) {
+  if (IsGlobalThrottle()) {
     SDVLOG(1) << "Unable to run a job because we're throttled.";
     return false;
   }
 
-  if (IsBackingOff() && priority != CANARY_PRIORITY) {
+  if (IsGlobalBackoff() && priority != CANARY_PRIORITY) {
     SDVLOG(1) << "Unable to run a job because we're backing off.";
     return false;
   }
@@ -512,28 +512,26 @@
 }
 
 void SyncSchedulerImpl::HandleSuccess() {
-  // If we're here, then we successfully reached the server.  End all backoff.
+  // If we're here, then we successfully reached the server. End all global
+  // throttle or backoff.
   wait_interval_.reset();
-  NotifyRetryTime(base::Time());
 }
 
 void SyncSchedulerImpl::HandleFailure(
     const ModelNeutralState& model_neutral_state) {
-  if (IsCurrentlyThrottled()) {
+  if (IsGlobalThrottle()) {
     SDVLOG(2) << "Was throttled during previous sync cycle.";
-  } else if (!IsBackingOff()) {
-    // Setup our backoff if this is our first such failure.
-    TimeDelta length = delay_provider_->GetDelay(
-        delay_provider_->GetInitialDelay(model_neutral_state));
-    wait_interval_ = base::MakeUnique<WaitInterval>(
-        WaitInterval::EXPONENTIAL_BACKOFF, length);
-    SDVLOG(2) << "Sync cycle failed.  Will back off for "
-              << wait_interval_->length.InMilliseconds() << "ms.";
   } else {
-    // Increase our backoff interval and schedule another retry.
-    TimeDelta length = delay_provider_->GetDelay(wait_interval_->length);
+    // TODO(skym): Slightly bizarre, the initial SYNC_AUTH_ERROR seems to
+    // trigger exponential backoff here, although it's immediately retried with
+    // correct credentials, it'd be nice if things were a bit more clean.
+    base::TimeDelta previous_delay =
+        IsGlobalBackoff()
+            ? wait_interval_->length
+            : delay_provider_->GetInitialDelay(model_neutral_state);
+    TimeDelta next_delay = delay_provider_->GetDelay(previous_delay);
     wait_interval_ = base::MakeUnique<WaitInterval>(
-        WaitInterval::EXPONENTIAL_BACKOFF, length);
+        WaitInterval::EXPONENTIAL_BACKOFF, next_delay);
     SDVLOG(2) << "Sync cycle failed.  Will back off for "
               << wait_interval_->length.InMilliseconds() << "ms.";
   }
@@ -625,7 +623,8 @@
 }
 
 void SyncSchedulerImpl::RestartWaiting() {
-  if (wait_interval_.get()) {
+  NotifyBlockedTypesChanged();
+  if (wait_interval_) {
     // Global throttling or backoff.
     if (!IsEarlierThanCurrentPendingJob(wait_interval_->length)) {
       // We check here because if we do not check here, and we already scheduled
@@ -656,9 +655,12 @@
     if (!IsEarlierThanCurrentPendingJob(time_until_next_unblock)) {
       return;
     }
+    NotifyRetryTime(base::Time::Now() + time_until_next_unblock);
     pending_wakeup_timer_.Start(FROM_HERE, time_until_next_unblock,
                                 base::Bind(&SyncSchedulerImpl::OnTypesUnblocked,
                                            weak_ptr_factory_.GetWeakPtr()));
+  } else {
+    NotifyRetryTime(base::Time());
   }
 }
 
@@ -722,7 +724,7 @@
   } else {
     // We must be in an error state. Transitioning out of each of these
     // error states should trigger a canary job.
-    DCHECK(IsCurrentlyThrottled() || IsBackingOff() ||
+    DCHECK(IsGlobalThrottle() || IsGlobalBackoff() ||
            cycle_context_->connection_manager()->HasInvalidAuthToken());
   }
 
@@ -746,8 +748,6 @@
 
   // We're no longer throttled, so clear the wait interval.
   wait_interval_.reset();
-  NotifyRetryTime(base::Time());
-  NotifyBlockedTypesChanged(nudge_tracker_.GetBlockedTypes());
 
   // We treat this as a 'canary' in the sense that it was originally scheduled
   // to run some time ago, failed, and we now want to retry, versus a job that
@@ -761,7 +761,6 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   nudge_tracker_.UpdateTypeThrottlingAndBackoffState();
-  NotifyBlockedTypesChanged(nudge_tracker_.GetBlockedTypes());
 
   // Maybe this is a good time to run a nudge job.  Let's try it.
   // If not a good time, reschedule a new run.
@@ -794,63 +793,63 @@
     observer.OnRetryTimeChanged(retry_time);
 }
 
-void SyncSchedulerImpl::NotifyBlockedTypesChanged(ModelTypeSet types) {
+void SyncSchedulerImpl::NotifyBlockedTypesChanged() {
+  ModelTypeSet types = nudge_tracker_.GetBlockedTypes();
   ModelTypeSet throttled_types;
   ModelTypeSet backed_off_types;
   for (ModelTypeSet::Iterator type_it = types.First(); type_it.Good();
        type_it.Inc()) {
-    if (nudge_tracker_.GetTypeBlockingMode(type_it.Get()) ==
-        WaitInterval::THROTTLED) {
+    WaitInterval::BlockingMode mode =
+        nudge_tracker_.GetTypeBlockingMode(type_it.Get());
+    if (mode == WaitInterval::THROTTLED) {
       throttled_types.Put(type_it.Get());
-    } else if (nudge_tracker_.GetTypeBlockingMode(type_it.Get()) ==
-               WaitInterval::EXPONENTIAL_BACKOFF) {
+    } else if (mode == WaitInterval::EXPONENTIAL_BACKOFF ||
+               mode == WaitInterval::EXPONENTIAL_BACKOFF_RETRYING) {
       backed_off_types.Put(type_it.Get());
     }
   }
 
   for (auto& observer : *cycle_context_->listeners()) {
-    observer.OnThrottledTypesChanged(throttled_types);
-    observer.OnBackedOffTypesChanged(backed_off_types);
+    observer.OnThrottledTypesChanged(IsGlobalThrottle() ? ModelTypeSet::All()
+                                                        : throttled_types);
+    observer.OnBackedOffTypesChanged(IsGlobalBackoff() ? ModelTypeSet::All()
+                                                       : backed_off_types);
   }
 }
 
-bool SyncSchedulerImpl::IsBackingOff() const {
+bool SyncSchedulerImpl::IsGlobalThrottle() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return wait_interval_ && wait_interval_->mode == WaitInterval::THROTTLED;
+}
 
-  return wait_interval_.get() &&
+bool SyncSchedulerImpl::IsGlobalBackoff() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return wait_interval_ &&
          wait_interval_->mode == WaitInterval::EXPONENTIAL_BACKOFF;
 }
 
 void SyncSchedulerImpl::OnThrottled(const TimeDelta& throttle_duration) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
   wait_interval_ = base::MakeUnique<WaitInterval>(WaitInterval::THROTTLED,
                                                   throttle_duration);
-  NotifyRetryTime(base::Time::Now() + wait_interval_->length);
-
   for (auto& observer : *cycle_context_->listeners()) {
     observer.OnThrottledTypesChanged(ModelTypeSet::All());
   }
+  RestartWaiting();
 }
 
 void SyncSchedulerImpl::OnTypesThrottled(ModelTypeSet types,
                                          const TimeDelta& throttle_duration) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  TimeTicks now = TimeTicks::Now();
-
   SDVLOG(1) << "Throttling " << ModelTypeSetToString(types) << " for "
             << throttle_duration.InMinutes() << " minutes.";
-
-  nudge_tracker_.SetTypesThrottledUntil(types, throttle_duration, now);
-  NotifyBlockedTypesChanged(nudge_tracker_.GetBlockedTypes());
+  nudge_tracker_.SetTypesThrottledUntil(types, throttle_duration,
+                                        TimeTicks::Now());
+  RestartWaiting();
 }
 
 void SyncSchedulerImpl::OnTypesBackedOff(ModelTypeSet types) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  TimeTicks now = TimeTicks::Now();
-
   for (ModelTypeSet::Iterator type = types.First(); type.Good(); type.Inc()) {
     TimeDelta last_backoff_time =
         TimeDelta::FromSeconds(kInitialBackoffRetrySeconds);
@@ -860,18 +859,15 @@
     }
 
     TimeDelta length = delay_provider_->GetDelay(last_backoff_time);
-    nudge_tracker_.SetTypeBackedOff(type.Get(), length, now);
+    nudge_tracker_.SetTypeBackedOff(type.Get(), length, TimeTicks::Now());
     SDVLOG(1) << "Backing off " << ModelTypeToString(type.Get()) << " for "
               << length.InSeconds() << " second.";
   }
-  NotifyBlockedTypesChanged(nudge_tracker_.GetBlockedTypes());
+  RestartWaiting();
 }
 
-bool SyncSchedulerImpl::IsCurrentlyThrottled() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  return wait_interval_.get() &&
-         wait_interval_->mode == WaitInterval::THROTTLED;
+bool SyncSchedulerImpl::IsAnyThrottleOrBackoff() {
+  return wait_interval_ || nudge_tracker_.IsAnyTypeBlocked();
 }
 
 void SyncSchedulerImpl::OnReceivedShortPollIntervalUpdate(
@@ -965,11 +961,8 @@
 }
 
 #undef SDVLOG_LOC
-
 #undef SDVLOG
-
 #undef SLOG
-
 #undef ENUM_CASE
 
 }  // namespace syncer
diff --git a/components/sync/engine_impl/sync_scheduler_impl.h b/components/sync/engine_impl/sync_scheduler_impl.h
index d83b038..40d018e 100644
--- a/components/sync/engine_impl/sync_scheduler_impl.h
+++ b/components/sync/engine_impl/sync_scheduler_impl.h
@@ -70,7 +70,7 @@
   void OnTypesThrottled(ModelTypeSet types,
                         const base::TimeDelta& throttle_duration) override;
   void OnTypesBackedOff(ModelTypeSet types) override;
-  bool IsCurrentlyThrottled() override;
+  bool IsAnyThrottleOrBackoff() override;
   void OnReceivedShortPollIntervalUpdate(
       const base::TimeDelta& new_interval) override;
   void OnReceivedLongPollIntervalUpdate(
@@ -83,8 +83,8 @@
   void OnReceivedGuRetryDelay(const base::TimeDelta& delay) override;
   void OnReceivedMigrationRequest(ModelTypeSet types) override;
 
-  // Returns true if the client is currently in exponential backoff.
-  bool IsBackingOff() const;
+  bool IsGlobalThrottle() const;
+  bool IsGlobalBackoff() const;
 
   // Changes the default delay between nudge cycles. Model-type specific
   // overrides will still apply. This is made public so that nudge cycles can be
@@ -174,7 +174,7 @@
   void NotifyRetryTime(base::Time retry_time);
 
   // Helper to signal listeners about changed throttled or backed off types.
-  void NotifyBlockedTypesChanged(ModelTypeSet types);
+  void NotifyBlockedTypesChanged();
 
   // Looks for pending work and, if it finds any, run this work at "canary"
   // priority.
diff --git a/components/sync/engine_impl/sync_scheduler_impl_unittest.cc b/components/sync/engine_impl/sync_scheduler_impl_unittest.cc
index e6a5c65..ca018cd4 100644
--- a/components/sync/engine_impl/sync_scheduler_impl_unittest.cc
+++ b/components/sync/engine_impl/sync_scheduler_impl_unittest.cc
@@ -205,7 +205,7 @@
     scheduler()->ScheduleLocalNudge(nudge_types, FROM_HERE);
     RunLoop();
 
-    return scheduler()->IsBackingOff();
+    return scheduler()->IsGlobalBackoff();
   }
 
   void UseMockDelayProvider() {
@@ -854,9 +854,9 @@
 
   PumpLoop();  // To get PerformDelayedNudge called.
   PumpLoop();  // To get TrySyncCycleJob called
-  EXPECT_TRUE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_TRUE(scheduler()->IsGlobalThrottle());
   RunLoop();
-  EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_FALSE(scheduler()->IsGlobalThrottle());
 
   StopSyncScheduler();
 }
@@ -889,10 +889,10 @@
   PumpLoop();
   EXPECT_EQ(0, ready_counter.times_called());
   EXPECT_EQ(1, retry_counter.times_called());
-  EXPECT_TRUE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_TRUE(scheduler()->IsGlobalThrottle());
 
   RunLoop();
-  EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_FALSE(scheduler()->IsGlobalThrottle());
 
   StopSyncScheduler();
 }
@@ -916,8 +916,8 @@
   PumpLoop();  // To get PerformDelayedNudge called.
   PumpLoop();  // To get TrySyncCycleJob called
   EXPECT_TRUE(GetThrottledTypes().HasAll(types));
-  EXPECT_FALSE(scheduler()->IsBackingOff());
-  EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_FALSE(scheduler()->IsGlobalBackoff());
+  EXPECT_FALSE(scheduler()->IsGlobalThrottle());
 
   // This won't cause a sync cycle because the types are throttled.
   scheduler()->ScheduleLocalNudge(types, FROM_HERE);
@@ -946,8 +946,8 @@
   PumpLoop();  // To get PerformDelayedNudge called.
   PumpLoop();  // To get TrySyncCycleJob called
   EXPECT_TRUE(GetBackedOffTypes().HasAll(types));
-  EXPECT_FALSE(scheduler()->IsBackingOff());
-  EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_FALSE(scheduler()->IsGlobalBackoff());
+  EXPECT_FALSE(scheduler()->IsGlobalThrottle());
 
   // This won't cause a sync cycle because the types are backed off.
   scheduler()->ScheduleLocalNudge(types, FROM_HERE);
@@ -976,8 +976,8 @@
   PumpLoop();  // To get PerformDelayedNudge called.
   PumpLoop();  // To get TrySyncCycleJob called
   EXPECT_TRUE(GetBackedOffTypes().HasAll(types));
-  EXPECT_FALSE(scheduler()->IsBackingOff());
-  EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_FALSE(scheduler()->IsGlobalBackoff());
+  EXPECT_FALSE(scheduler()->IsGlobalThrottle());
 
   SyncShareTimes times;
   EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
@@ -986,8 +986,8 @@
   PumpLoop();  // To get PerformDelayedNudge called.
   PumpLoop();  // To get TrySyncCycleJob called
   EXPECT_FALSE(IsAnyTypeBlocked());
-  EXPECT_FALSE(scheduler()->IsBackingOff());
-  EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_FALSE(scheduler()->IsGlobalBackoff());
+  EXPECT_FALSE(scheduler()->IsGlobalThrottle());
 
   StopSyncScheduler();
 }
@@ -1013,8 +1013,8 @@
   PumpLoop();  // To get TrySyncCycleJob called
   EXPECT_TRUE(GetBackedOffTypes().HasAll(types));
   EXPECT_TRUE(BlockTimerIsRunning());
-  EXPECT_FALSE(scheduler()->IsBackingOff());
-  EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_FALSE(scheduler()->IsGlobalBackoff());
+  EXPECT_FALSE(scheduler()->IsGlobalThrottle());
 
   TimeDelta throttle1(TimeDelta::FromMilliseconds(150));
 
@@ -1031,8 +1031,8 @@
 
   EXPECT_TRUE(GetBackedOffTypes().HasAll(types));
   EXPECT_TRUE(BlockTimerIsRunning());
-  EXPECT_FALSE(scheduler()->IsBackingOff());
-  EXPECT_TRUE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_FALSE(scheduler()->IsGlobalBackoff());
+  EXPECT_TRUE(scheduler()->IsGlobalThrottle());
 
   // Unthrottled client, but the backingoff datatype is still in backoff and
   // scheduled.
@@ -1040,7 +1040,7 @@
       .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
                       QuitLoopNowAction(true)));
   RunLoop();
-  EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_FALSE(scheduler()->IsGlobalThrottle());
   EXPECT_TRUE(GetBackedOffTypes().HasAll(types));
   EXPECT_TRUE(BlockTimerIsRunning());
 
@@ -1085,8 +1085,8 @@
   EXPECT_TRUE(GetThrottledTypes().HasAll(throttled_types));
   EXPECT_TRUE(GetBackedOffTypes().HasAll(backed_off_types));
   EXPECT_TRUE(BlockTimerIsRunning());
-  EXPECT_FALSE(scheduler()->IsBackingOff());
-  EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_FALSE(scheduler()->IsGlobalBackoff());
+  EXPECT_FALSE(scheduler()->IsGlobalThrottle());
 
   // This won't cause a sync cycle because the types are throttled or backed
   // off.
@@ -1121,8 +1121,8 @@
   PumpLoop();  // To get PerformDelayedNudge called.
   PumpLoop();  // To get TrySyncCycleJob called
   EXPECT_TRUE(GetThrottledTypes().HasAll(throttled_types));
-  EXPECT_FALSE(scheduler()->IsBackingOff());
-  EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_FALSE(scheduler()->IsGlobalBackoff());
+  EXPECT_FALSE(scheduler()->IsGlobalThrottle());
 
   // Ignore invalidations for throttled types.
   scheduler()->ScheduleInvalidationNudge(THEMES, BuildInvalidation(10, "test"),
@@ -1169,8 +1169,8 @@
   PumpLoop();  // To get PerformDelayedNudge called.
   PumpLoop();  // To get TrySyncCycleJob called
   EXPECT_TRUE(GetBackedOffTypes().HasAll(backed_off_types));
-  EXPECT_FALSE(scheduler()->IsBackingOff());
-  EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_FALSE(scheduler()->IsGlobalBackoff());
+  EXPECT_FALSE(scheduler()->IsGlobalThrottle());
 
   // Ignore invalidations for backed off types.
   scheduler()->ScheduleInvalidationNudge(THEMES, BuildInvalidation(10, "test"),
@@ -1313,7 +1313,7 @@
   scheduler()->ScheduleConfiguration(params);
   RunLoop();
 
-  EXPECT_TRUE(scheduler()->IsBackingOff());
+  EXPECT_TRUE(scheduler()->IsGlobalBackoff());
 }
 
 // Test that no polls or extraneous nudges occur when in backoff.
@@ -1486,11 +1486,11 @@
 
   // Run the unsuccessful poll. The failed poll should not trigger backoff.
   RunLoop();
-  EXPECT_TRUE(scheduler()->IsBackingOff());
+  EXPECT_TRUE(scheduler()->IsGlobalBackoff());
 
   // Run the successful poll.
   RunLoop();
-  EXPECT_FALSE(scheduler()->IsBackingOff());
+  EXPECT_FALSE(scheduler()->IsGlobalBackoff());
 }
 
 // Test that starting the syncer thread without a valid connection doesn't
@@ -1531,7 +1531,7 @@
   scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
   PumpLoop();  // To get PerformDelayedNudge called.
   PumpLoop();  // Run the nudge, that will fail and schedule a quick retry.
-  ASSERT_TRUE(scheduler()->IsBackingOff());
+  ASSERT_TRUE(scheduler()->IsGlobalBackoff());
 
   // Before we run the scheduled canary, trigger a server connection change.
   scheduler()->OnConnectionStatusChange();
@@ -1563,7 +1563,7 @@
 
   PumpLoop();  // To get PerformDelayedNudge called.
   PumpLoop();  // Run the nudge, that will fail and schedule a quick retry.
-  ASSERT_TRUE(scheduler()->IsBackingOff());
+  ASSERT_TRUE(scheduler()->IsGlobalBackoff());
 
   // Before we run the scheduled canary, trigger a server connection change.
   scheduler()->OnConnectionStatusChange();
@@ -1665,7 +1665,7 @@
   // Run to wait for retrying.
   RunLoop();
 
-  EXPECT_TRUE(scheduler()->IsBackingOff());
+  EXPECT_TRUE(scheduler()->IsGlobalBackoff());
   EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
       .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
                       RecordSyncShare(&times, true)));
@@ -1738,13 +1738,13 @@
   scheduler()->ScheduleClearServerData(params);
   PumpLoop();
   ASSERT_EQ(0, success_counter.times_called());
-  ASSERT_TRUE(scheduler()->IsBackingOff());
+  ASSERT_TRUE(scheduler()->IsGlobalBackoff());
 
   // Now succeed.
   connection()->SetServerReachable();
   PumpLoopFor(2 * delta);
   ASSERT_EQ(1, success_counter.times_called());
-  ASSERT_FALSE(scheduler()->IsBackingOff());
+  ASSERT_FALSE(scheduler()->IsGlobalBackoff());
 }
 
 TEST_F(SyncSchedulerImplTest, PartialFailureWillExponentialBackoff) {
@@ -1764,8 +1764,8 @@
   PumpLoop();  // To get PerformDelayedNudge called.
   PumpLoop();  // To get TrySyncCycleJob called
   EXPECT_TRUE(GetBackedOffTypes().HasAll(types));
-  EXPECT_FALSE(scheduler()->IsBackingOff());
-  EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_FALSE(scheduler()->IsGlobalBackoff());
+  EXPECT_FALSE(scheduler()->IsGlobalThrottle());
   TimeDelta first_blocking_time = GetTypeBlockingTime(THEMES);
 
   SetTypeBlockingMode(THEMES, WaitInterval::EXPONENTIAL_BACKOFF_RETRYING);
@@ -1811,8 +1811,8 @@
   PumpLoop();  // To get TrySyncCycleJob called
   EXPECT_TRUE(GetBackedOffTypes().HasAll(types));
   EXPECT_TRUE(BlockTimerIsRunning());
-  EXPECT_FALSE(scheduler()->IsBackingOff());
-  EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_FALSE(scheduler()->IsGlobalBackoff());
+  EXPECT_FALSE(scheduler()->IsGlobalThrottle());
 
   SyncShareTimes times;
   EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
@@ -1829,8 +1829,8 @@
   // Timer is still running for backoff datatype after Sync success.
   EXPECT_TRUE(GetBackedOffTypes().HasAll(types));
   EXPECT_TRUE(BlockTimerIsRunning());
-  EXPECT_FALSE(scheduler()->IsBackingOff());
-  EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_FALSE(scheduler()->IsGlobalBackoff());
+  EXPECT_FALSE(scheduler()->IsGlobalThrottle());
 
   StopSyncScheduler();
 }
@@ -1861,8 +1861,8 @@
   PumpLoop();  // To get TrySyncCycleJob called
   EXPECT_TRUE(GetBackedOffTypes().HasAll(themes_types));
   EXPECT_TRUE(BlockTimerIsRunning());
-  EXPECT_FALSE(scheduler()->IsBackingOff());
-  EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_FALSE(scheduler()->IsGlobalBackoff());
+  EXPECT_FALSE(scheduler()->IsGlobalThrottle());
 
   // Set anther backoff datatype.
   const ModelTypeSet typed_urls_types(TYPED_URLS);
@@ -1882,8 +1882,8 @@
   EXPECT_TRUE(GetBackedOffTypes().HasAll(themes_types));
   EXPECT_TRUE(GetBackedOffTypes().HasAll(typed_urls_types));
   EXPECT_TRUE(BlockTimerIsRunning());
-  EXPECT_FALSE(scheduler()->IsBackingOff());
-  EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_FALSE(scheduler()->IsGlobalBackoff());
+  EXPECT_FALSE(scheduler()->IsGlobalThrottle());
 
   // Unblock one datatype.
   SyncShareTimes times;
@@ -1899,8 +1899,8 @@
   EXPECT_TRUE(GetBackedOffTypes().HasAll(themes_types));
   EXPECT_FALSE(GetBackedOffTypes().HasAll(typed_urls_types));
   EXPECT_TRUE(BlockTimerIsRunning());
-  EXPECT_FALSE(scheduler()->IsBackingOff());
-  EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
+  EXPECT_FALSE(scheduler()->IsGlobalBackoff());
+  EXPECT_FALSE(scheduler()->IsGlobalThrottle());
 
   StopSyncScheduler();
 }
@@ -1917,14 +1917,14 @@
   scheduler()->ScheduleLocalNudge({THEMES}, FROM_HERE);
   PumpLoop();  // To get PerformDelayedNudge called.
   EXPECT_FALSE(BlockTimerIsRunning());
-  EXPECT_FALSE(scheduler()->IsBackingOff());
+  EXPECT_FALSE(scheduler()->IsGlobalBackoff());
 
   // This is the tricky piece. We have a gap while the sync job is bouncing to
   // get onto the |pending_wakeup_timer_|, should be scheduled with no delay.
   scheduler()->ScheduleLocalNudge({TYPED_URLS}, FROM_HERE);
   EXPECT_TRUE(BlockTimerIsRunning());
   EXPECT_EQ(TimeDelta(), GetPendingWakeupTimerDelay());
-  EXPECT_FALSE(scheduler()->IsBackingOff());
+  EXPECT_FALSE(scheduler()->IsGlobalBackoff());
 
   // Setup mock as we're about to attempt to sync.
   SyncShareTimes times;
@@ -1937,7 +1937,7 @@
   PumpLoop();
   EXPECT_TRUE(BlockTimerIsRunning());
   EXPECT_EQ(TimeDelta(), GetPendingWakeupTimerDelay());
-  EXPECT_TRUE(scheduler()->IsBackingOff());
+  EXPECT_TRUE(scheduler()->IsGlobalBackoff());
 
   // Triggers TYPED_URLS PerformDelayedNudge(), which should no-op, because
   // we're no long healthy, and normal priorities shouldn't go through, but it
@@ -1946,7 +1946,7 @@
   PumpLoop();
   EXPECT_TRUE(BlockTimerIsRunning());
   EXPECT_LT(TimeDelta::FromSeconds(50), GetPendingWakeupTimerDelay());
-  EXPECT_TRUE(scheduler()->IsBackingOff());
+  EXPECT_TRUE(scheduler()->IsGlobalBackoff());
 }
 
 }  // namespace syncer
diff --git a/components/sync/engine_impl/syncer_unittest.cc b/components/sync/engine_impl/syncer_unittest.cc
index 5f75d5a..9eb8a88f 100644
--- a/components/sync/engine_impl/syncer_unittest.cc
+++ b/components/sync/engine_impl/syncer_unittest.cc
@@ -193,7 +193,7 @@
   void OnTypesBackedOff(ModelTypeSet types) override {
     scheduler_->OnTypesBackedOff(types);
   }
-  bool IsCurrentlyThrottled() override { return false; }
+  bool IsAnyThrottleOrBackoff() override { return false; }
   void OnReceivedLongPollIntervalUpdate(
       const base::TimeDelta& new_interval) override {
     last_long_poll_interval_received_ = new_interval;
diff --git a/components/sync/test/engine/fake_sync_scheduler.cc b/components/sync/test/engine/fake_sync_scheduler.cc
index 1b879cb..8a91921 100644
--- a/components/sync/test/engine/fake_sync_scheduler.cc
+++ b/components/sync/test/engine/fake_sync_scheduler.cc
@@ -52,7 +52,7 @@
 
 void FakeSyncScheduler::OnTypesBackedOff(ModelTypeSet types) {}
 
-bool FakeSyncScheduler::IsCurrentlyThrottled() {
+bool FakeSyncScheduler::IsAnyThrottleOrBackoff() {
   return false;
 }
 
diff --git a/components/sync/test/engine/fake_sync_scheduler.h b/components/sync/test/engine/fake_sync_scheduler.h
index 6982fc7..70347f4 100644
--- a/components/sync/test/engine/fake_sync_scheduler.h
+++ b/components/sync/test/engine/fake_sync_scheduler.h
@@ -47,7 +47,7 @@
   void OnTypesThrottled(ModelTypeSet types,
                         const base::TimeDelta& throttle_duration) override;
   void OnTypesBackedOff(ModelTypeSet types) override;
-  bool IsCurrentlyThrottled() override;
+  bool IsAnyThrottleOrBackoff() override;
   void OnReceivedShortPollIntervalUpdate(
       const base::TimeDelta& new_interval) override;
   void OnReceivedLongPollIntervalUpdate(
diff --git a/components/sync/user_events/user_event_service_impl.cc b/components/sync/user_events/user_event_service_impl.cc
index 2c692724..e4e2c0e1 100644
--- a/components/sync/user_events/user_event_service_impl.cc
+++ b/components/sync/user_events/user_event_service_impl.cc
@@ -61,9 +61,9 @@
 
 bool UserEventServiceImpl::ShouldRecordEvent(
     const UserEventSpecifics& specifics) {
-  // We only record events if the user is syncing history and has not enabled
-  // a custom passphrase. The type HISTORY_DELETE_DIRECTIVES is enabled in and
-  // only in this exact scenario.
+  // We only record events if the user is syncing history (as indicated by
+  // GetPreferredDataTypes()) and has not enabled a custom passphrase (as
+  // indicated by IsUsingSecondaryPassphrase()).
   return sync_service_->IsEngineInitialized() &&
          !sync_service_->IsUsingSecondaryPassphrase() &&
          sync_service_->GetPreferredDataTypes().Has(HISTORY_DELETE_DIRECTIVES);
diff --git a/components/sync/user_events/user_event_service_impl_unittest.cc b/components/sync/user_events/user_event_service_impl_unittest.cc
index 083565e..eee46dd 100644
--- a/components/sync/user_events/user_event_service_impl_unittest.cc
+++ b/components/sync/user_events/user_event_service_impl_unittest.cc
@@ -24,11 +24,17 @@
 
 class TestSyncService : public FakeSyncService {
  public:
-  TestSyncService(bool is_engine_initialized, ModelTypeSet preferred_data_types)
+  TestSyncService(bool is_engine_initialized,
+                  bool is_using_secondary_passphrase,
+                  ModelTypeSet preferred_data_types)
       : is_engine_initialized_(is_engine_initialized),
+        is_using_secondary_passphrase_(is_using_secondary_passphrase),
         preferred_data_types_(preferred_data_types) {}
 
   bool IsEngineInitialized() const override { return is_engine_initialized_; }
+  bool IsUsingSecondaryPassphrase() const override {
+    return is_using_secondary_passphrase_;
+  }
 
   ModelTypeSet GetPreferredDataTypes() const override {
     return preferred_data_types_;
@@ -36,6 +42,7 @@
 
  private:
   bool is_engine_initialized_;
+  bool is_using_secondary_passphrase_;
   ModelTypeSet preferred_data_types_;
 };
 
@@ -47,7 +54,7 @@
 class UserEventServiceImplTest : public testing::Test {
  protected:
   UserEventServiceImplTest()
-      : sync_service_(true, ModelTypeSet(HISTORY_DELETE_DIRECTIVES)) {}
+      : sync_service_(true, false, {HISTORY_DELETE_DIRECTIVES}) {}
 
   std::unique_ptr<UserEventSyncBridge> MakeBridge() {
     return base::MakeUnique<UserEventSyncBridge>(
@@ -83,15 +90,23 @@
 }
 
 TEST_F(UserEventServiceImplTest, ShouldNotRecordNoHistory) {
-  TestSyncService no_history_sync_service(true, ModelTypeSet());
+  TestSyncService no_history_sync_service(true, false, ModelTypeSet());
   UserEventServiceImpl service(&no_history_sync_service, MakeBridge());
   service.RecordUserEvent(base::MakeUnique<UserEventSpecifics>());
   EXPECT_EQ(0u, processor().put_multimap().size());
 }
 
+TEST_F(UserEventServiceImplTest, ShouldNotRecordPassphrase) {
+  TestSyncService passphrase_sync_service(true, true,
+                                          {HISTORY_DELETE_DIRECTIVES});
+  UserEventServiceImpl service(&passphrase_sync_service, MakeBridge());
+  service.RecordUserEvent(base::MakeUnique<UserEventSpecifics>());
+  EXPECT_EQ(0u, processor().put_multimap().size());
+}
+
 TEST_F(UserEventServiceImplTest, ShouldNotRecordEngineOff) {
   TestSyncService engine_not_initialized_sync_service(
-      false, ModelTypeSet(HISTORY_DELETE_DIRECTIVES));
+      false, false, {HISTORY_DELETE_DIRECTIVES});
   UserEventServiceImpl service(&engine_not_initialized_sync_service,
                                MakeBridge());
   service.RecordUserEvent(base::MakeUnique<UserEventSpecifics>());
diff --git a/components/viz/PRESUBMIT.py b/components/viz/PRESUBMIT.py
index a3b3d1b..c8bef10 100644
--- a/components/viz/PRESUBMIT.py
+++ b/components/viz/PRESUBMIT.py
@@ -2,317 +2,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Top-level presubmit script for components/viz.
-
-See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
-for more details about the presubmit API built into depot_tools.
-"""
-
-import re
-import string
-
-VIZ_SOURCE_FILES=(r'^components[\\/]viz[\\/].*\.(cc|h)$',)
-
-def CheckChangeLintsClean(input_api, output_api):
-  source_filter = lambda x: input_api.FilterSourceFile(
-    x, white_list=VIZ_SOURCE_FILES, black_list=None)
-
-  return input_api.canned_checks.CheckChangeLintsClean(
-      input_api, output_api, source_filter, lint_filters=[], verbose_level=1)
-
-def CheckAsserts(input_api, output_api, white_list=VIZ_SOURCE_FILES, black_list=None):
-  black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
-  source_file_filter = lambda x: input_api.FilterSourceFile(x, white_list, black_list)
-
-  assert_files = []
-
-  for f in input_api.AffectedSourceFiles(source_file_filter):
-    contents = input_api.ReadFile(f, 'rb')
-    # WebKit ASSERT() is not allowed.
-    if re.search(r"\bASSERT\(", contents):
-      assert_files.append(f.LocalPath())
-
-  if assert_files:
-    return [output_api.PresubmitError(
-      'These files use ASSERT instead of using DCHECK:',
-      items=assert_files)]
-  return []
-
-def CheckStdAbs(input_api, output_api,
-                white_list=VIZ_SOURCE_FILES, black_list=None):
-  black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
-  source_file_filter = lambda x: input_api.FilterSourceFile(x,
-                                                            white_list,
-                                                            black_list)
-
-  using_std_abs_files = []
-  found_fabs_files = []
-  missing_std_prefix_files = []
-
-  for f in input_api.AffectedSourceFiles(source_file_filter):
-    contents = input_api.ReadFile(f, 'rb')
-    if re.search(r"using std::f?abs;", contents):
-      using_std_abs_files.append(f.LocalPath())
-    if re.search(r"\bfabsf?\(", contents):
-      found_fabs_files.append(f.LocalPath());
-
-    no_std_prefix = r"(?<!std::)"
-    # Matches occurrences of abs/absf/fabs/fabsf without a "std::" prefix.
-    abs_without_prefix = r"%s(\babsf?\()" % no_std_prefix
-    fabs_without_prefix = r"%s(\bfabsf?\()" % no_std_prefix
-    # Skips matching any lines that have "// NOLINT".
-    no_nolint = r"(?![^\n]*//\s+NOLINT)"
-
-    expression = re.compile("(%s|%s)%s" %
-        (abs_without_prefix, fabs_without_prefix, no_nolint))
-    if expression.search(contents):
-      missing_std_prefix_files.append(f.LocalPath())
-
-  result = []
-  if using_std_abs_files:
-    result.append(output_api.PresubmitError(
-        'These files have "using std::abs" which is not permitted.',
-        items=using_std_abs_files))
-  if found_fabs_files:
-    result.append(output_api.PresubmitError(
-        'std::abs() should be used instead of std::fabs() for consistency.',
-        items=found_fabs_files))
-  if missing_std_prefix_files:
-    result.append(output_api.PresubmitError(
-        'These files use abs(), absf(), fabs(), or fabsf() without qualifying '
-        'the std namespace. Please use std::abs() in all places.',
-        items=missing_std_prefix_files))
-  return result
-
-def CheckPassByValue(input_api,
-                     output_api,
-                     white_list=VIZ_SOURCE_FILES,
-                     black_list=None):
-  black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
-  source_file_filter = lambda x: input_api.FilterSourceFile(x,
-                                                            white_list,
-                                                            black_list)
-
-  local_errors = []
-
-  # Well-defined simple classes the same size as a primitive type.
-  pass_by_value_types = ['base::Time',
-                         'base::TimeTicks',
-                         ]
-
-  for f in input_api.AffectedSourceFiles(source_file_filter):
-    contents = input_api.ReadFile(f, 'rb')
-    match = re.search(
-      r'\bconst +' + '(?P<type>(%s))&' %
-        string.join(pass_by_value_types, '|'),
-      contents)
-    if match:
-      local_errors.append(output_api.PresubmitError(
-        '%s passes %s by const ref instead of by value.' %
-        (f.LocalPath(), match.group('type'))))
-  return local_errors
-
-def CheckTodos(input_api, output_api):
-  errors = []
-
-  source_file_filter = lambda x: x
-  for f in input_api.AffectedSourceFiles(source_file_filter):
-    contents = input_api.ReadFile(f, 'rb')
-    if ('FIX'+'ME') in contents:
-      errors.append(f.LocalPath())
-
-  if errors:
-    return [output_api.PresubmitError(
-      'All TODO comments should be of the form TODO(name/bug). ' +
-      'Use TODO instead of FIX' + 'ME',
-      items=errors)]
-  return []
-
-def CheckDoubleAngles(input_api, output_api, white_list=VIZ_SOURCE_FILES,
-                      black_list=None):
-  errors = []
-
-  source_file_filter = lambda x: input_api.FilterSourceFile(x,
-                                                            white_list,
-                                                            black_list)
-  for f in input_api.AffectedSourceFiles(source_file_filter):
-    contents = input_api.ReadFile(f, 'rb')
-    if ('> >') in contents:
-      errors.append(f.LocalPath())
-
-  if errors:
-    return [output_api.PresubmitError('Use >> instead of > >:', items=errors)]
-  return []
-
-def CheckUniquePtr(input_api, output_api,
-                   white_list=VIZ_SOURCE_FILES, black_list=None):
-  black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
-  source_file_filter = lambda x: input_api.FilterSourceFile(x,
-                                                            white_list,
-                                                            black_list)
-  errors = []
-  for f in input_api.AffectedSourceFiles(source_file_filter):
-    for line_number, line in f.ChangedContents():
-      # Disallow:
-      # return std::unique_ptr<T>(foo);
-      # bar = std::unique_ptr<T>(foo);
-      # But allow:
-      # return std::unique_ptr<T[]>(foo);
-      # bar = std::unique_ptr<T[]>(foo);
-      if re.search(r'(=|\breturn)\s*std::unique_ptr<.*?(?<!])>\([^)]+\)', line):
-        errors.append(output_api.PresubmitError(
-          ('%s:%d uses explicit std::unique_ptr constructor. ' +
-           'Use base::MakeUnique<T>() instead.') %
-          (f.LocalPath(), line_number)))
-      # Disallow:
-      # std::unique_ptr<T>()
-      if re.search(r'\bstd::unique_ptr<.*?>\(\)', line):
-        errors.append(output_api.PresubmitError(
-          '%s:%d uses std::unique_ptr<T>(). Use nullptr instead.' %
-          (f.LocalPath(), line_number)))
-  return errors
-
-def FindUnquotedQuote(contents, pos):
-  match = re.search(r"(?<!\\)(?P<quote>\")", contents[pos:])
-  return -1 if not match else match.start("quote") + pos
-
-def FindUselessIfdefs(input_api, output_api):
-  errors = []
-  source_file_filter = lambda x: x
-  for f in input_api.AffectedSourceFiles(source_file_filter):
-    contents = input_api.ReadFile(f, 'rb')
-    if re.search(r'#if\s*0\s', contents):
-      errors.append(f.LocalPath())
-  if errors:
-    return [output_api.PresubmitError(
-      'Don\'t use #if '+'0; just delete the code.',
-      items=errors)]
-  return []
-
-def FindNamespaceInBlock(pos, namespace, contents, whitelist=[]):
-  open_brace = -1
-  close_brace = -1
-  quote = -1
-  name = -1
-  brace_count = 1
-  quote_count = 0
-  while pos < len(contents) and brace_count > 0:
-    if open_brace < pos: open_brace = contents.find("{", pos)
-    if close_brace < pos: close_brace = contents.find("}", pos)
-    if quote < pos: quote = FindUnquotedQuote(contents, pos)
-    if name < pos: name = contents.find(("%s::" % namespace), pos)
-
-    if name < 0:
-      return False # The namespace is not used at all.
-    if open_brace < 0:
-      open_brace = len(contents)
-    if close_brace < 0:
-      close_brace = len(contents)
-    if quote < 0:
-      quote = len(contents)
-
-    next = min(open_brace, min(close_brace, min(quote, name)))
-
-    if next == open_brace:
-      brace_count += 1
-    elif next == close_brace:
-      brace_count -= 1
-    elif next == quote:
-      quote_count = 0 if quote_count else 1
-    elif next == name and not quote_count:
-      in_whitelist = False
-      for w in whitelist:
-        if re.match(w, contents[next:]):
-          in_whitelist = True
-          break
-      if not in_whitelist:
-        return True
-    pos = next + 1
-  return False
-
-# Checks for the use of viz:: within the viz namespace, which is usually
-# redundant.
-def CheckNamespace(input_api, output_api):
-  errors = []
-
-  source_file_filter = lambda x: x
-  for f in input_api.AffectedSourceFiles(source_file_filter):
-    contents = input_api.ReadFile(f, 'rb')
-    match = re.search(r'namespace\s*viz\s*{', contents)
-    if match:
-      whitelist = []
-      if FindNamespaceInBlock(match.end(), 'viz', contents, whitelist=whitelist):
-        errors.append(f.LocalPath())
-
-  if errors:
-    return [output_api.PresubmitError(
-      'Do not use viz:: inside of the viz namespace.',
-      items=errors)]
-  return []
-
-def CheckForUseOfWrongClock(input_api,
-                            output_api,
-                            white_list=VIZ_SOURCE_FILES,
-                            black_list=None):
-  """Make sure new lines of code don't use a clock susceptible to skew."""
-  black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
-  source_file_filter = lambda x: input_api.FilterSourceFile(x,
-                                                            white_list,
-                                                            black_list)
-  # Regular expression that should detect any explicit references to the
-  # base::Time type (or base::Clock/DefaultClock), whether in using decls,
-  # typedefs, or to call static methods.
-  base_time_type_pattern = r'(^|\W)base::(Time|Clock|DefaultClock)(\W|$)'
-
-  # Regular expression that should detect references to the base::Time class
-  # members, such as a call to base::Time::Now.
-  base_time_member_pattern = r'(^|\W)(Time|Clock|DefaultClock)::'
-
-  # Regular expression to detect "using base::Time" declarations.  We want to
-  # prevent these from triggerring a warning.  For example, it's perfectly
-  # reasonable for code to be written like this:
-  #
-  #   using base::Time;
-  #   ...
-  #   int64 foo_us = foo_s * Time::kMicrosecondsPerSecond;
-  using_base_time_decl_pattern = r'^\s*using\s+(::)?base::Time\s*;'
-
-  # Regular expression to detect references to the kXXX constants in the
-  # base::Time class.  We want to prevent these from triggerring a warning.
-  base_time_konstant_pattern = r'(^|\W)Time::k\w+'
-
-  problem_re = input_api.re.compile(
-      r'(' + base_time_type_pattern + r')|(' + base_time_member_pattern + r')')
-  exception_re = input_api.re.compile(
-      r'(' + using_base_time_decl_pattern + r')|(' +
-      base_time_konstant_pattern + r')')
-  problems = []
-  for f in input_api.AffectedSourceFiles(source_file_filter):
-    for line_number, line in f.ChangedContents():
-      if problem_re.search(line):
-        if not exception_re.search(line):
-          problems.append(
-              '  %s:%d\n    %s' % (f.LocalPath(), line_number, line.strip()))
-
-  if problems:
-    return [output_api.PresubmitPromptOrNotify(
-        'You added one or more references to the base::Time class and/or one\n'
-        'of its member functions (or base::Clock/DefaultClock). In cc code,\n'
-        'it is most certainly incorrect! Instead use base::TimeTicks.\n\n'
-        '\n'.join(problems))]
-  else:
-    return []
+"""Top-level presubmit script for components/viz."""
 
 def CheckChangeOnUpload(input_api, output_api):
-  results = []
-  results += CheckAsserts(input_api, output_api)
-  results += CheckStdAbs(input_api, output_api)
-  results += CheckPassByValue(input_api, output_api)
-  results += CheckChangeLintsClean(input_api, output_api)
-  results += CheckTodos(input_api, output_api)
-  results += CheckDoubleAngles(input_api, output_api)
-  results += CheckUniquePtr(input_api, output_api)
-  results += CheckNamespace(input_api, output_api)
-  results += CheckForUseOfWrongClock(input_api, output_api)
-  results += FindUselessIfdefs(input_api, output_api)
-  return results
+  import sys
+  original_sys_path = sys.path
+  sys.path = sys.path + [input_api.os_path.join(
+    input_api.change.RepositoryRoot(),
+    'components', 'viz')]
+
+  import presubmit_checks as ps
+  white_list=(r'^components[\\/]viz[\\/].*\.(cc|h)$',)
+  return ps.RunAllChecks(input_api, output_api, white_list)
diff --git a/components/viz/host/host_frame_sink_manager_unittests.cc b/components/viz/host/host_frame_sink_manager_unittests.cc
index bd89d4f3..1571f91 100644
--- a/components/viz/host/host_frame_sink_manager_unittests.cc
+++ b/components/viz/host/host_frame_sink_manager_unittests.cc
@@ -24,8 +24,9 @@
 namespace test {
 namespace {
 
-constexpr FrameSinkId kParentFrameSinkId(1, 1);
-constexpr FrameSinkId kClientFrameSinkId(2, 1);
+constexpr FrameSinkId kFrameSinkParent1(1, 1);
+constexpr FrameSinkId kFrameSinkParent2(2, 1);
+constexpr FrameSinkId kFrameSinkChild1(3, 1);
 
 // Makes a SurfaceId with a default nonce.
 SurfaceId MakeSurfaceId(const FrameSinkId& frame_sink_id, uint32_t local_id) {
@@ -91,9 +92,9 @@
   HostFrameSinkManagerTest() = default;
   ~HostFrameSinkManagerTest() override = default;
 
-  HostFrameSinkManager& host_manager() { return *host_manager_; }
+  HostFrameSinkManager& host() { return *host_manager_; }
 
-  MockFrameSinkManagerImpl& manager_impl() { return *manager_impl_; }
+  MockFrameSinkManagerImpl& impl() { return *manager_impl_; }
 
   std::unique_ptr<CompositorFrameSinkSupport> CreateCompositorFrameSinkSupport(
       const FrameSinkId& frame_sink_id,
@@ -138,105 +139,99 @@
 TEST_F(HostFrameSinkManagerTest, CreateMojomCompositorFrameSink) {
   // Calling CreateCompositorFrameSink() should first register the frame sink
   // and then request to create it.
-  EXPECT_CALL(manager_impl(), RegisterFrameSinkId(kClientFrameSinkId));
+  EXPECT_CALL(impl(), RegisterFrameSinkId(kFrameSinkChild1));
 
   FakeHostFrameSinkClient client;
-  host_manager().RegisterFrameSinkId(kClientFrameSinkId, &client);
+  host().RegisterFrameSinkId(kFrameSinkChild1, &client);
 
-  EXPECT_CALL(manager_impl(),
-              MockCreateCompositorFrameSink(kClientFrameSinkId));
-  host_manager().CreateCompositorFrameSink(
-      kClientFrameSinkId, nullptr /* request */, nullptr /* client */);
+  EXPECT_CALL(impl(), MockCreateCompositorFrameSink(kFrameSinkChild1));
+  host().CreateCompositorFrameSink(kFrameSinkChild1, nullptr /* request */,
+                                   nullptr /* client */);
 
-  EXPECT_TRUE(FrameSinkDataExists(kClientFrameSinkId));
+  EXPECT_TRUE(FrameSinkDataExists(kFrameSinkChild1));
 
   // Register should call through to FrameSinkManagerImpl and should work even
-  // though |kParentFrameSinkId| was not created yet.
-  EXPECT_CALL(manager_impl(), RegisterFrameSinkHierarchy(kParentFrameSinkId,
-                                                         kClientFrameSinkId));
-  host_manager().RegisterFrameSinkHierarchy(kParentFrameSinkId,
-                                            kClientFrameSinkId);
+  // though |kFrameSinkParent1| was not created yet.
+  EXPECT_CALL(impl(),
+              RegisterFrameSinkHierarchy(kFrameSinkParent1, kFrameSinkChild1));
+  host().RegisterFrameSinkHierarchy(kFrameSinkParent1, kFrameSinkChild1);
 
   // Destroying the CompositorFrameSink should invalidate it in viz.
-  EXPECT_CALL(manager_impl(), InvalidateFrameSinkId(kClientFrameSinkId));
+  EXPECT_CALL(impl(), InvalidateFrameSinkId(kFrameSinkChild1));
 
-  // We should still have the hierarchy data for |kClientFrameSinkId|.
-  EXPECT_TRUE(FrameSinkDataExists(kClientFrameSinkId));
+  // We should still have the hierarchy data for |kFrameSinkChild1|.
+  EXPECT_TRUE(FrameSinkDataExists(kFrameSinkChild1));
 
   // Unregister should work after the CompositorFrameSink is destroyed.
-  EXPECT_CALL(manager_impl(), UnregisterFrameSinkHierarchy(kParentFrameSinkId,
-                                                           kClientFrameSinkId));
-  host_manager().UnregisterFrameSinkHierarchy(kParentFrameSinkId,
-                                              kClientFrameSinkId);
-  host_manager().InvalidateFrameSinkId(kClientFrameSinkId);
+  EXPECT_CALL(impl(), UnregisterFrameSinkHierarchy(kFrameSinkParent1,
+                                                   kFrameSinkChild1));
+  host().UnregisterFrameSinkHierarchy(kFrameSinkParent1, kFrameSinkChild1);
+  host().InvalidateFrameSinkId(kFrameSinkChild1);
 
-  // Data for |kClientFrameSinkId| should be deleted now.
-  EXPECT_FALSE(FrameSinkDataExists(kClientFrameSinkId));
+  // Data for |kFrameSinkChild1| should be deleted now.
+  EXPECT_FALSE(FrameSinkDataExists(kFrameSinkChild1));
 }
 
 // Verify that that creating two CompositorFrameSinkSupports works.
 TEST_F(HostFrameSinkManagerTest, CreateCompositorFrameSinkSupport) {
   auto support_client =
-      CreateCompositorFrameSinkSupport(kClientFrameSinkId, true /* is_root */);
-  EXPECT_TRUE(FrameSinkDataExists(kClientFrameSinkId));
+      CreateCompositorFrameSinkSupport(kFrameSinkChild1, true /* is_root */);
+  EXPECT_TRUE(FrameSinkDataExists(kFrameSinkChild1));
 
   auto support_parent =
-      CreateCompositorFrameSinkSupport(kParentFrameSinkId, true /* is_root */);
-  EXPECT_TRUE(FrameSinkDataExists(kParentFrameSinkId));
+      CreateCompositorFrameSinkSupport(kFrameSinkParent1, true /* is_root */);
+  EXPECT_TRUE(FrameSinkDataExists(kFrameSinkParent1));
 
   // Register should call through to FrameSinkManagerImpl.
-  EXPECT_CALL(manager_impl(), RegisterFrameSinkHierarchy(kParentFrameSinkId,
-                                                         kClientFrameSinkId));
-  host_manager().RegisterFrameSinkHierarchy(kParentFrameSinkId,
-                                            kClientFrameSinkId);
+  EXPECT_CALL(impl(),
+              RegisterFrameSinkHierarchy(kFrameSinkParent1, kFrameSinkChild1));
+  host().RegisterFrameSinkHierarchy(kFrameSinkParent1, kFrameSinkChild1);
 
-  EXPECT_CALL(manager_impl(), UnregisterFrameSinkHierarchy(kParentFrameSinkId,
-                                                           kClientFrameSinkId));
-  host_manager().UnregisterFrameSinkHierarchy(kParentFrameSinkId,
-                                              kClientFrameSinkId);
+  EXPECT_CALL(impl(), UnregisterFrameSinkHierarchy(kFrameSinkParent1,
+                                                   kFrameSinkChild1));
+  host().UnregisterFrameSinkHierarchy(kFrameSinkParent1, kFrameSinkChild1);
 
-  // We should still have the CompositorFrameSink data for |kClientFrameSinkId|.
-  EXPECT_TRUE(FrameSinkDataExists(kClientFrameSinkId));
+  // We should still have the CompositorFrameSink data for |kFrameSinkChild1|.
+  EXPECT_TRUE(FrameSinkDataExists(kFrameSinkChild1));
 
   support_client.reset();
 
-  // Data for |kClientFrameSinkId| should be deleted now.
-  EXPECT_FALSE(FrameSinkDataExists(kClientFrameSinkId));
+  // Data for |kFrameSinkChild1| should be deleted now.
+  EXPECT_FALSE(FrameSinkDataExists(kFrameSinkChild1));
 
   support_parent.reset();
 
-  // Data for |kParentFrameSinkId| should be deleted now.
-  EXPECT_FALSE(FrameSinkDataExists(kParentFrameSinkId));
+  // Data for |kFrameSinkParent1| should be deleted now.
+  EXPECT_FALSE(FrameSinkDataExists(kFrameSinkParent1));
 }
 
 TEST_F(HostFrameSinkManagerTest, AssignTemporaryReference) {
   FakeHostFrameSinkClient client;
-  host_manager().RegisterFrameSinkId(kParentFrameSinkId, &client);
-  host_manager().RegisterFrameSinkId(kClientFrameSinkId, &client);
+  host().RegisterFrameSinkId(kFrameSinkParent1, &client);
+  host().RegisterFrameSinkId(kFrameSinkChild1, &client);
 
-  const SurfaceId surface_id = MakeSurfaceId(kClientFrameSinkId, 1);
+  const SurfaceId surface_id = MakeSurfaceId(kFrameSinkChild1, 1);
   auto support = CreateCompositorFrameSinkSupport(surface_id.frame_sink_id(),
                                                   false /* is_root */);
 
-  host_manager().RegisterFrameSinkHierarchy(kParentFrameSinkId,
-                                            surface_id.frame_sink_id());
+  host().RegisterFrameSinkHierarchy(kFrameSinkParent1,
+                                    surface_id.frame_sink_id());
 
   // When HostFrameSinkManager gets OnFirstSurfaceActivation() it should assign
-  // the temporary reference to the registered parent |kParentFrameSinkId|.
-  EXPECT_CALL(manager_impl(),
-              AssignTemporaryReference(surface_id, kParentFrameSinkId));
+  // the temporary reference to the registered parent |kFrameSinkParent1|.
+  EXPECT_CALL(impl(), AssignTemporaryReference(surface_id, kFrameSinkParent1));
   GetFrameSinkManagerClient()->OnFirstSurfaceActivation(
       MakeSurfaceInfo(surface_id));
 }
 
 TEST_F(HostFrameSinkManagerTest, DropTemporaryReference) {
-  const SurfaceId surface_id = MakeSurfaceId(kClientFrameSinkId, 1);
+  const SurfaceId surface_id = MakeSurfaceId(kFrameSinkChild1, 1);
   auto support = CreateCompositorFrameSinkSupport(surface_id.frame_sink_id(),
                                                   false /* is_root */);
 
   // When HostFrameSinkManager gets OnFirstSurfaceActivation() it should find no
   // registered parent and drop the temporary reference.
-  EXPECT_CALL(manager_impl(), DropTemporaryReference(surface_id));
+  EXPECT_CALL(impl(), DropTemporaryReference(surface_id));
   GetFrameSinkManagerClient()->OnFirstSurfaceActivation(
       MakeSurfaceInfo(surface_id));
 }
@@ -245,104 +240,143 @@
 // sink that corresponds to the new surface has been invalidated.
 TEST_F(HostFrameSinkManagerTest, DropTemporaryReferenceForStaleClient) {
   FakeHostFrameSinkClient client;
-  host_manager().RegisterFrameSinkId(kClientFrameSinkId, &client);
+  host().RegisterFrameSinkId(kFrameSinkChild1, &client);
   auto support_client =
-      CreateCompositorFrameSinkSupport(kClientFrameSinkId, false /* is_root */);
-  EXPECT_TRUE(FrameSinkDataExists(kClientFrameSinkId));
+      CreateCompositorFrameSinkSupport(kFrameSinkChild1, false /* is_root */);
+  EXPECT_TRUE(FrameSinkDataExists(kFrameSinkChild1));
 
-  host_manager().RegisterFrameSinkId(kParentFrameSinkId, &client);
+  host().RegisterFrameSinkId(kFrameSinkParent1, &client);
   auto support_parent =
-      CreateCompositorFrameSinkSupport(kParentFrameSinkId, true /* is_root */);
-  EXPECT_TRUE(FrameSinkDataExists(kParentFrameSinkId));
+      CreateCompositorFrameSinkSupport(kFrameSinkParent1, true /* is_root */);
+  EXPECT_TRUE(FrameSinkDataExists(kFrameSinkParent1));
 
   // Register should call through to FrameSinkManagerImpl.
-  EXPECT_CALL(manager_impl(), RegisterFrameSinkHierarchy(kParentFrameSinkId,
-                                                         kClientFrameSinkId));
-  host_manager().RegisterFrameSinkHierarchy(kParentFrameSinkId,
-                                            kClientFrameSinkId);
+  EXPECT_CALL(impl(),
+              RegisterFrameSinkHierarchy(kFrameSinkParent1, kFrameSinkChild1));
+  host().RegisterFrameSinkHierarchy(kFrameSinkParent1, kFrameSinkChild1);
 
-  const SurfaceId client_surface_id = MakeSurfaceId(kClientFrameSinkId, 1);
-  EXPECT_CALL(manager_impl(), DropTemporaryReference(client_surface_id))
-      .Times(0);
-  EXPECT_CALL(manager_impl(),
-              AssignTemporaryReference(client_surface_id, kParentFrameSinkId))
+  const SurfaceId client_surface_id = MakeSurfaceId(kFrameSinkChild1, 1);
+  EXPECT_CALL(impl(), DropTemporaryReference(client_surface_id)).Times(0);
+  EXPECT_CALL(impl(),
+              AssignTemporaryReference(client_surface_id, kFrameSinkParent1))
       .Times(1);
   GetFrameSinkManagerClient()->OnFirstSurfaceActivation(
       MakeSurfaceInfo(client_surface_id));
-  testing::Mock::VerifyAndClearExpectations(&manager_impl());
+  testing::Mock::VerifyAndClearExpectations(&impl());
 
   // Invaidating the client should cause the next SurfaceId to be dropped.
   support_client.reset();
-  host_manager().InvalidateFrameSinkId(kClientFrameSinkId);
+  host().InvalidateFrameSinkId(kFrameSinkChild1);
 
-  const SurfaceId client_surface_id2 = MakeSurfaceId(kClientFrameSinkId, 2);
-  EXPECT_CALL(manager_impl(), DropTemporaryReference(client_surface_id2))
-      .Times(1);
-  EXPECT_CALL(manager_impl(), AssignTemporaryReference(client_surface_id2, _))
-      .Times(0);
+  const SurfaceId client_surface_id2 = MakeSurfaceId(kFrameSinkChild1, 2);
+  EXPECT_CALL(impl(), DropTemporaryReference(client_surface_id2)).Times(1);
+  EXPECT_CALL(impl(), AssignTemporaryReference(client_surface_id2, _)).Times(0);
   GetFrameSinkManagerClient()->OnFirstSurfaceActivation(
       MakeSurfaceInfo(client_surface_id2));
 
   support_parent.reset();
-  host_manager().InvalidateFrameSinkId(kParentFrameSinkId);
+  host().InvalidateFrameSinkId(kFrameSinkParent1);
+}
+
+// Verify that multiple parents in the frame sink hierarchy works.
+TEST_F(HostFrameSinkManagerTest, HierarchyMultipleParents) {
+  FakeHostFrameSinkClient client;
+
+  // Register two parent and child CompositorFrameSink.
+  const FrameSinkId& id_parent1 = kFrameSinkParent1;
+  host().RegisterFrameSinkId(id_parent1, &client);
+  auto support_parent1 =
+      CreateCompositorFrameSinkSupport(id_parent1, true /* is_root */);
+
+  const FrameSinkId& id_parent2 = kFrameSinkChild1;
+  host().RegisterFrameSinkId(id_parent2, &client);
+  auto support_parent2 =
+      CreateCompositorFrameSinkSupport(id_parent2, true /* is_root */);
+
+  const FrameSinkId& id_child = kFrameSinkParent2;
+  host().RegisterFrameSinkId(id_child, &client);
+  auto support_child =
+      CreateCompositorFrameSinkSupport(id_child, false /* is_root */);
+
+  // Register |id_parent1| in hierarchy first, this is the original window
+  // embedding.
+  EXPECT_CALL(impl(), RegisterFrameSinkHierarchy(id_parent1, id_child));
+  host().RegisterFrameSinkHierarchy(id_parent1, id_child);
+
+  // Register |id_parent2| in hierarchy second, this is a second embedding for
+  // something like alt-tab on a different monitor.
+  EXPECT_CALL(impl(), RegisterFrameSinkHierarchy(id_parent2, id_child));
+  host().RegisterFrameSinkHierarchy(id_parent2, id_child);
+  testing::Mock::VerifyAndClearExpectations(&impl());
+
+  // The oldest registered parent in the hierarchy is assigned the temporary
+  // reference.
+  const SurfaceId surface_id = MakeSurfaceId(id_child, 1);
+  EXPECT_CALL(impl(), AssignTemporaryReference(surface_id, id_parent1));
+  GetFrameSinkManagerClient()->OnFirstSurfaceActivation(
+      MakeSurfaceInfo(surface_id));
+  testing::Mock::VerifyAndClearExpectations(&impl());
+
+  // Unregistering hierarchy with multiple parents should also work.
+  EXPECT_CALL(impl(), UnregisterFrameSinkHierarchy(id_parent2, id_child));
+  host().UnregisterFrameSinkHierarchy(id_parent2, id_child);
+
+  EXPECT_CALL(impl(), UnregisterFrameSinkHierarchy(id_parent1, id_child));
+  host().UnregisterFrameSinkHierarchy(id_parent1, id_child);
 }
 
 // Verify that we drop the temporary reference to a new surface if the only
 // frame sink registered as an embedder has been invalidated.
 TEST_F(HostFrameSinkManagerTest, DropTemporaryReferenceForInvalidatedParent) {
   FakeHostFrameSinkClient client;
-  host_manager().RegisterFrameSinkId(kClientFrameSinkId, &client);
+  host().RegisterFrameSinkId(kFrameSinkChild1, &client);
   auto support_client =
-      CreateCompositorFrameSinkSupport(kClientFrameSinkId, false /* is_root */);
-  EXPECT_TRUE(FrameSinkDataExists(kClientFrameSinkId));
+      CreateCompositorFrameSinkSupport(kFrameSinkChild1, false /* is_root */);
+  EXPECT_TRUE(FrameSinkDataExists(kFrameSinkChild1));
 
-  host_manager().RegisterFrameSinkId(kParentFrameSinkId, &client);
+  host().RegisterFrameSinkId(kFrameSinkParent1, &client);
   auto support_parent =
-      CreateCompositorFrameSinkSupport(kParentFrameSinkId, true /* is_root */);
-  EXPECT_TRUE(FrameSinkDataExists(kParentFrameSinkId));
+      CreateCompositorFrameSinkSupport(kFrameSinkParent1, true /* is_root */);
+  EXPECT_TRUE(FrameSinkDataExists(kFrameSinkParent1));
 
   // Register should call through to FrameSinkManagerImpl.
-  EXPECT_CALL(manager_impl(), RegisterFrameSinkHierarchy(kParentFrameSinkId,
-                                                         kClientFrameSinkId));
-  host_manager().RegisterFrameSinkHierarchy(kParentFrameSinkId,
-                                            kClientFrameSinkId);
+  EXPECT_CALL(impl(),
+              RegisterFrameSinkHierarchy(kFrameSinkParent1, kFrameSinkChild1));
+  host().RegisterFrameSinkHierarchy(kFrameSinkParent1, kFrameSinkChild1);
 
-  const SurfaceId client_surface_id = MakeSurfaceId(kClientFrameSinkId, 1);
-  EXPECT_CALL(manager_impl(), DropTemporaryReference(client_surface_id))
-      .Times(0);
-  EXPECT_CALL(manager_impl(),
-              AssignTemporaryReference(client_surface_id, kParentFrameSinkId))
+  const SurfaceId client_surface_id = MakeSurfaceId(kFrameSinkChild1, 1);
+  EXPECT_CALL(impl(), DropTemporaryReference(client_surface_id)).Times(0);
+  EXPECT_CALL(impl(),
+              AssignTemporaryReference(client_surface_id, kFrameSinkParent1))
       .Times(1);
   GetFrameSinkManagerClient()->OnFirstSurfaceActivation(
       MakeSurfaceInfo(client_surface_id));
-  testing::Mock::VerifyAndClearExpectations(&manager_impl());
+  testing::Mock::VerifyAndClearExpectations(&impl());
 
   // Invaidating the parent should cause the next SurfaceId to be dropped
   // because there is no registered frame sink as the parent.
   support_parent.reset();
-  host_manager().InvalidateFrameSinkId(kParentFrameSinkId);
+  host().InvalidateFrameSinkId(kFrameSinkParent1);
 
-  const SurfaceId client_surface_id2 = MakeSurfaceId(kClientFrameSinkId, 2);
-  EXPECT_CALL(manager_impl(), DropTemporaryReference(client_surface_id2))
-      .Times(1);
-  EXPECT_CALL(manager_impl(), AssignTemporaryReference(client_surface_id2, _))
-      .Times(0);
+  const SurfaceId client_surface_id2 = MakeSurfaceId(kFrameSinkChild1, 2);
+  EXPECT_CALL(impl(), DropTemporaryReference(client_surface_id2)).Times(1);
+  EXPECT_CALL(impl(), AssignTemporaryReference(client_surface_id2, _)).Times(0);
   GetFrameSinkManagerClient()->OnFirstSurfaceActivation(
       MakeSurfaceInfo(client_surface_id2));
 
   support_client.reset();
-  host_manager().InvalidateFrameSinkId(kClientFrameSinkId);
+  host().InvalidateFrameSinkId(kFrameSinkChild1);
 }
 
 TEST_F(HostFrameSinkManagerTest, DisplayRootTemporaryReference) {
-  const SurfaceId surface_id = MakeSurfaceId(kParentFrameSinkId, 1);
+  const SurfaceId surface_id = MakeSurfaceId(kFrameSinkParent1, 1);
   auto support = CreateCompositorFrameSinkSupport(surface_id.frame_sink_id(),
                                                   true /* is_root */);
 
   // When HostFrameSinkManager gets OnFirstSurfaceActivation() it should do
-  // nothing since |kParentFrameSinkId| is a display root.
-  EXPECT_CALL(manager_impl(), DropTemporaryReference(surface_id)).Times(0);
-  EXPECT_CALL(manager_impl(), AssignTemporaryReference(surface_id, _)).Times(0);
+  // nothing since |kFrameSinkParent1| is a display root.
+  EXPECT_CALL(impl(), DropTemporaryReference(surface_id)).Times(0);
+  EXPECT_CALL(impl(), AssignTemporaryReference(surface_id, _)).Times(0);
   GetFrameSinkManagerClient()->OnFirstSurfaceActivation(
       MakeSurfaceInfo(surface_id));
 }
diff --git a/components/viz/presubmit_checks.py b/components/viz/presubmit_checks.py
new file mode 100644
index 0000000..9db40f1
--- /dev/null
+++ b/components/viz/presubmit_checks.py
@@ -0,0 +1,312 @@
+# 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.
+
+"""Presubmit checks used in viz"""
+
+import re
+import string
+
+def CheckChangeLintsClean(input_api, output_api, white_list, black_list=None):
+  source_filter = lambda x: input_api.FilterSourceFile(
+    x, white_list=white_list, black_list=black_list)
+
+  return input_api.canned_checks.CheckChangeLintsClean(
+      input_api, output_api, source_filter, lint_filters=[], verbose_level=1)
+
+def CheckAsserts(input_api, output_api, white_list, black_list=None):
+  black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
+  source_file_filter = lambda x: input_api.FilterSourceFile(x, white_list, black_list)
+
+  assert_files = []
+
+  for f in input_api.AffectedSourceFiles(source_file_filter):
+    contents = input_api.ReadFile(f, 'rb')
+    # WebKit ASSERT() is not allowed.
+    if re.search(r"\bASSERT\(", contents):
+      assert_files.append(f.LocalPath())
+
+  if assert_files:
+    return [output_api.PresubmitError(
+      'These files use ASSERT instead of using DCHECK:',
+      items=assert_files)]
+  return []
+
+def CheckStdAbs(input_api, output_api,
+                white_list, black_list=None):
+  black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
+  source_file_filter = lambda x: input_api.FilterSourceFile(x,
+                                                            white_list,
+                                                            black_list)
+
+  using_std_abs_files = []
+  found_fabs_files = []
+  missing_std_prefix_files = []
+
+  for f in input_api.AffectedSourceFiles(source_file_filter):
+    contents = input_api.ReadFile(f, 'rb')
+    if re.search(r"using std::f?abs;", contents):
+      using_std_abs_files.append(f.LocalPath())
+    if re.search(r"\bfabsf?\(", contents):
+      found_fabs_files.append(f.LocalPath());
+
+    no_std_prefix = r"(?<!std::)"
+    # Matches occurrences of abs/absf/fabs/fabsf without a "std::" prefix.
+    abs_without_prefix = r"%s(\babsf?\()" % no_std_prefix
+    fabs_without_prefix = r"%s(\bfabsf?\()" % no_std_prefix
+    # Skips matching any lines that have "// NOLINT".
+    no_nolint = r"(?![^\n]*//\s+NOLINT)"
+
+    expression = re.compile("(%s|%s)%s" %
+        (abs_without_prefix, fabs_without_prefix, no_nolint))
+    if expression.search(contents):
+      missing_std_prefix_files.append(f.LocalPath())
+
+  result = []
+  if using_std_abs_files:
+    result.append(output_api.PresubmitError(
+        'These files have "using std::abs" which is not permitted.',
+        items=using_std_abs_files))
+  if found_fabs_files:
+    result.append(output_api.PresubmitError(
+        'std::abs() should be used instead of std::fabs() for consistency.',
+        items=found_fabs_files))
+  if missing_std_prefix_files:
+    result.append(output_api.PresubmitError(
+        'These files use abs(), absf(), fabs(), or fabsf() without qualifying '
+        'the std namespace. Please use std::abs() in all places.',
+        items=missing_std_prefix_files))
+  return result
+
+def CheckPassByValue(input_api,
+                     output_api,
+                     white_list,
+                     black_list=None):
+  black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
+  source_file_filter = lambda x: input_api.FilterSourceFile(x,
+                                                            white_list,
+                                                            black_list)
+
+  local_errors = []
+
+  # Well-defined simple classes the same size as a primitive type.
+  pass_by_value_types = ['base::Time',
+                         'base::TimeTicks',
+                         ]
+
+  for f in input_api.AffectedSourceFiles(source_file_filter):
+    contents = input_api.ReadFile(f, 'rb')
+    match = re.search(
+      r'\bconst +' + '(?P<type>(%s))&' %
+        string.join(pass_by_value_types, '|'),
+      contents)
+    if match:
+      local_errors.append(output_api.PresubmitError(
+        '%s passes %s by const ref instead of by value.' %
+        (f.LocalPath(), match.group('type'))))
+  return local_errors
+
+def CheckTodos(input_api, output_api):
+  errors = []
+
+  source_file_filter = lambda x: x
+  for f in input_api.AffectedSourceFiles(source_file_filter):
+    contents = input_api.ReadFile(f, 'rb')
+    if ('FIX'+'ME') in contents:
+      errors.append(f.LocalPath())
+
+  if errors:
+    return [output_api.PresubmitError(
+      'All TODO comments should be of the form TODO(name/bug). ' +
+      'Use TODO instead of FIX' + 'ME',
+      items=errors)]
+  return []
+
+def CheckDoubleAngles(input_api, output_api, white_list,
+                      black_list=None):
+  errors = []
+
+  source_file_filter = lambda x: input_api.FilterSourceFile(x,
+                                                            white_list,
+                                                            black_list)
+  for f in input_api.AffectedSourceFiles(source_file_filter):
+    contents = input_api.ReadFile(f, 'rb')
+    if ('> >') in contents:
+      errors.append(f.LocalPath())
+
+  if errors:
+    return [output_api.PresubmitError('Use >> instead of > >:', items=errors)]
+  return []
+
+def CheckUniquePtr(input_api, output_api,
+                   white_list, black_list=None):
+  black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
+  source_file_filter = lambda x: input_api.FilterSourceFile(x,
+                                                            white_list,
+                                                            black_list)
+  errors = []
+  for f in input_api.AffectedSourceFiles(source_file_filter):
+    for line_number, line in f.ChangedContents():
+      # Disallow:
+      # return std::unique_ptr<T>(foo);
+      # bar = std::unique_ptr<T>(foo);
+      # But allow:
+      # return std::unique_ptr<T[]>(foo);
+      # bar = std::unique_ptr<T[]>(foo);
+      if re.search(r'(=|\breturn)\s*std::unique_ptr<.*?(?<!])>\([^)]+\)', line):
+        errors.append(output_api.PresubmitError(
+          ('%s:%d uses explicit std::unique_ptr constructor. ' +
+           'Use base::MakeUnique<T>() instead.') %
+          (f.LocalPath(), line_number)))
+      # Disallow:
+      # std::unique_ptr<T>()
+      if re.search(r'\bstd::unique_ptr<.*?>\(\)', line):
+        errors.append(output_api.PresubmitError(
+          '%s:%d uses std::unique_ptr<T>(). Use nullptr instead.' %
+          (f.LocalPath(), line_number)))
+  return errors
+
+def FindUnquotedQuote(contents, pos):
+  match = re.search(r"(?<!\\)(?P<quote>\")", contents[pos:])
+  return -1 if not match else match.start("quote") + pos
+
+def FindUselessIfdefs(input_api, output_api):
+  errors = []
+  source_file_filter = lambda x: x
+  for f in input_api.AffectedSourceFiles(source_file_filter):
+    contents = input_api.ReadFile(f, 'rb')
+    if re.search(r'#if\s*0\s', contents):
+      errors.append(f.LocalPath())
+  if errors:
+    return [output_api.PresubmitError(
+      'Don\'t use #if '+'0; just delete the code.',
+      items=errors)]
+  return []
+
+def FindNamespaceInBlock(pos, namespace, contents, whitelist=[]):
+  open_brace = -1
+  close_brace = -1
+  quote = -1
+  name = -1
+  brace_count = 1
+  quote_count = 0
+  while pos < len(contents) and brace_count > 0:
+    if open_brace < pos: open_brace = contents.find("{", pos)
+    if close_brace < pos: close_brace = contents.find("}", pos)
+    if quote < pos: quote = FindUnquotedQuote(contents, pos)
+    if name < pos: name = contents.find(("%s::" % namespace), pos)
+
+    if name < 0:
+      return False # The namespace is not used at all.
+    if open_brace < 0:
+      open_brace = len(contents)
+    if close_brace < 0:
+      close_brace = len(contents)
+    if quote < 0:
+      quote = len(contents)
+
+    next = min(open_brace, min(close_brace, min(quote, name)))
+
+    if next == open_brace:
+      brace_count += 1
+    elif next == close_brace:
+      brace_count -= 1
+    elif next == quote:
+      quote_count = 0 if quote_count else 1
+    elif next == name and not quote_count:
+      in_whitelist = False
+      for w in whitelist:
+        if re.match(w, contents[next:]):
+          in_whitelist = True
+          break
+      if not in_whitelist:
+        return True
+    pos = next + 1
+  return False
+
+# Checks for the use of viz:: within the viz namespace, which is usually
+# redundant.
+def CheckNamespace(input_api, output_api):
+  errors = []
+
+  source_file_filter = lambda x: x
+  for f in input_api.AffectedSourceFiles(source_file_filter):
+    contents = input_api.ReadFile(f, 'rb')
+    match = re.search(r'namespace\s*viz\s*{', contents)
+    if match:
+      whitelist = []
+      if FindNamespaceInBlock(match.end(), 'viz', contents, whitelist=whitelist):
+        errors.append(f.LocalPath())
+
+  if errors:
+    return [output_api.PresubmitError(
+      'Do not use viz:: inside of the viz namespace.',
+      items=errors)]
+  return []
+
+def CheckForUseOfWrongClock(input_api,
+                            output_api,
+                            white_list,
+                            black_list=None):
+  """Make sure new lines of code don't use a clock susceptible to skew."""
+  black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
+  source_file_filter = lambda x: input_api.FilterSourceFile(x,
+                                                            white_list,
+                                                            black_list)
+  # Regular expression that should detect any explicit references to the
+  # base::Time type (or base::Clock/DefaultClock), whether in using decls,
+  # typedefs, or to call static methods.
+  base_time_type_pattern = r'(^|\W)base::(Time|Clock|DefaultClock)(\W|$)'
+
+  # Regular expression that should detect references to the base::Time class
+  # members, such as a call to base::Time::Now.
+  base_time_member_pattern = r'(^|\W)(Time|Clock|DefaultClock)::'
+
+  # Regular expression to detect "using base::Time" declarations.  We want to
+  # prevent these from triggerring a warning.  For example, it's perfectly
+  # reasonable for code to be written like this:
+  #
+  #   using base::Time;
+  #   ...
+  #   int64 foo_us = foo_s * Time::kMicrosecondsPerSecond;
+  using_base_time_decl_pattern = r'^\s*using\s+(::)?base::Time\s*;'
+
+  # Regular expression to detect references to the kXXX constants in the
+  # base::Time class.  We want to prevent these from triggerring a warning.
+  base_time_konstant_pattern = r'(^|\W)Time::k\w+'
+
+  problem_re = input_api.re.compile(
+      r'(' + base_time_type_pattern + r')|(' + base_time_member_pattern + r')')
+  exception_re = input_api.re.compile(
+      r'(' + using_base_time_decl_pattern + r')|(' +
+      base_time_konstant_pattern + r')')
+  problems = []
+  for f in input_api.AffectedSourceFiles(source_file_filter):
+    for line_number, line in f.ChangedContents():
+      if problem_re.search(line):
+        if not exception_re.search(line):
+          problems.append(
+              '  %s:%d\n    %s' % (f.LocalPath(), line_number, line.strip()))
+
+  if problems:
+    return [output_api.PresubmitPromptOrNotify(
+        'You added one or more references to the base::Time class and/or one\n'
+        'of its member functions (or base::Clock/DefaultClock). In cc code,\n'
+        'it is most certainly incorrect! Instead use base::TimeTicks.\n\n'
+        '\n'.join(problems))]
+  else:
+    return []
+
+def RunAllChecks(input_api, output_api, white_list):
+  results = []
+  results += CheckAsserts(input_api, output_api, white_list)
+  results += CheckStdAbs(input_api, output_api, white_list)
+  results += CheckPassByValue(input_api, output_api, white_list)
+  results += CheckChangeLintsClean(input_api, output_api, white_list)
+  results += CheckTodos(input_api, output_api)
+  results += CheckDoubleAngles(input_api, output_api, white_list)
+  results += CheckUniquePtr(input_api, output_api, white_list)
+  results += CheckNamespace(input_api, output_api)
+  results += CheckForUseOfWrongClock(input_api, output_api, white_list)
+  results += FindUselessIfdefs(input_api, output_api)
+  return results
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index adfe70a..3826931 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -137,7 +137,6 @@
       factory_(factory),
       tree_(new ui::AXSerializableTree()),
       user_is_navigating_away_(false),
-      osk_state_(OSK_ALLOWED),
       last_focused_node_(nullptr),
       last_focused_manager_(nullptr),
       connected_to_parent_tree_node_(false),
@@ -156,7 +155,6 @@
       factory_(factory),
       tree_(new ui::AXSerializableTree()),
       user_is_navigating_away_(false),
-      osk_state_(OSK_ALLOWED),
       last_focused_node_(nullptr),
       last_focused_manager_(nullptr),
       ax_tree_id_(ui::AXTreeIDRegistry::kNoAXTreeID),
@@ -445,11 +443,6 @@
 
     if (event_type == ui::AX_EVENT_FOCUS ||
         event_type == ui::AX_EVENT_BLUR) {
-      if (osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_HIDDEN &&
-          osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED) {
-        osk_state_ = OSK_ALLOWED;
-      }
-
       // We already handled all focus events above.
       continue;
     }
diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h
index 1624878..9486d9f 100644
--- a/content/browser/accessibility/browser_accessibility_manager.h
+++ b/content/browser/accessibility/browser_accessibility_manager.h
@@ -411,29 +411,6 @@
   virtual void SendLocationChangeEvents(
       const std::vector<AccessibilityHostMsg_LocationChangeParams>& params);
 
- private:
-  // The following states keep track of whether or not the
-  // on-screen keyboard is allowed to be shown.
-  enum OnScreenKeyboardState {
-    // Never show the on-screen keyboard because this tab is hidden.
-    OSK_DISALLOWED_BECAUSE_TAB_HIDDEN,
-
-    // This tab was just shown, so don't pop-up the on-screen keyboard if a
-    // text field gets focus that wasn't the result of an explicit touch.
-    OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED,
-
-    // A touch event has occurred within the window, but focus has not
-    // explicitly changed. Allow the on-screen keyboard to be shown if the
-    // touch event was within the bounds of the currently focused object.
-    // Otherwise we'll just wait to see if focus changes.
-    OSK_ALLOWED_WITHIN_FOCUSED_OBJECT,
-
-    // Focus has changed within a tab that's already visible. Allow the
-    // on-screen keyboard to show anytime that a touch event leads to an
-    // editable text control getting focus.
-    OSK_ALLOWED
-  };
-
  protected:
   // The object that can perform actions on our behalf.
   BrowserAccessibilityDelegate* delegate_;
@@ -455,9 +432,6 @@
   // True if the user has initiated a navigation to another page.
   bool user_is_navigating_away_;
 
-  // The on-screen keyboard state.
-  OnScreenKeyboardState osk_state_;
-
   BrowserAccessibilityFindInPageInfo find_in_page_info_;
 
   // These are only used by the root BrowserAccessibilityManager of a
diff --git a/content/browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc b/content/browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc
index 9b60c731..a650aaa 100644
--- a/content/browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc
@@ -141,8 +141,7 @@
     int rv = CreateCacheBackend(
         net::MEMORY_CACHE, net::CACHE_BACKEND_DEFAULT, base::FilePath(),
         (CacheStorageBlobToDiskCache::kBufferSize * 100) /* max bytes */,
-        false /* force */, base::ThreadTaskRunnerHandle::Get(),
-        nullptr /* net log */, &cache_backend_,
+        false /* force */, nullptr /* net log */, &cache_backend_,
         base::Bind(&DoNothingCompletion));
     // The memory cache runs synchronously.
     EXPECT_EQ(net::OK, rv);
diff --git a/content/browser/cache_storage/cache_storage_cache.cc b/content/browser/cache_storage/cache_storage_cache.cc
index 702f5af..d872977 100644
--- a/content/browser/cache_storage/cache_storage_cache.cc
+++ b/content/browser/cache_storage/cache_storage_cache.cc
@@ -1402,11 +1402,18 @@
 }
 
 void CacheStorageCache::CloseImpl(base::OnceClosure callback) {
-  DCHECK_NE(BACKEND_CLOSED, backend_state_);
+  DCHECK_EQ(BACKEND_OPEN, backend_state_);
 
-  backend_state_ = BACKEND_CLOSED;
   backend_.reset();
-  std::move(callback).Run();
+  post_backend_closed_callback_ = std::move(callback);
+}
+
+void CacheStorageCache::DeleteBackendCompletedIO() {
+  if (!post_backend_closed_callback_.is_null()) {
+    DCHECK_NE(BACKEND_CLOSED, backend_state_);
+    backend_state_ = BACKEND_CLOSED;
+    std::move(post_backend_closed_callback_).Run();
+  }
 }
 
 void CacheStorageCache::SizeImpl(SizeCallback callback) {
@@ -1439,13 +1446,13 @@
                          weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                          base::Passed(std::move(backend_ptr))));
 
-  // TODO(jkarlin): Use the cache task runner that ServiceWorkerCacheCore
-  // has for disk caches.
   int rv = disk_cache::CreateCacheBackend(
       cache_type, net::CACHE_BACKEND_SIMPLE, path_, kMaxCacheBytes,
       false, /* force */
-      BrowserThread::GetTaskRunnerForThread(BrowserThread::CACHE).get(), NULL,
-      backend, create_cache_callback);
+      NULL, backend,
+      base::BindOnce(&CacheStorageCache::DeleteBackendCompletedIO,
+                     weak_ptr_factory_.GetWeakPtr()),
+      create_cache_callback);
   if (rv != net::ERR_IO_PENDING)
     create_cache_callback.Run(rv);
 }
diff --git a/content/browser/cache_storage/cache_storage_cache.h b/content/browser/cache_storage/cache_storage_cache.h
index 3a737bd..46ea4f0 100644
--- a/content/browser/cache_storage/cache_storage_cache.h
+++ b/content/browser/cache_storage/cache_storage_cache.h
@@ -362,6 +362,7 @@
   void InitGotCacheSize(base::OnceClosure callback,
                         CacheStorageError cache_create_error,
                         int cache_size);
+  void DeleteBackendCompletedIO();
 
   std::unique_ptr<storage::BlobDataHandle> PopulateResponseBody(
       disk_cache::ScopedEntryPtr entry,
@@ -396,6 +397,10 @@
   // Whether or not to store data in disk or memory.
   bool memory_only_;
 
+  // Active while waiting for the backend to finish its closing up, and contains
+  // the callback passed to CloseImpl.
+  base::OnceClosure post_backend_closed_callback_;
+
   base::WeakPtrFactory<CacheStorageCache> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(CacheStorageCache);
diff --git a/content/browser/cache_storage/cache_storage_cache_unittest.cc b/content/browser/cache_storage/cache_storage_cache_unittest.cc
index a92dd20..5802535 100644
--- a/content/browser/cache_storage/cache_storage_cache_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_cache_unittest.cc
@@ -29,7 +29,9 @@
 #include "content/public/common/referrer.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_utils.h"
 #include "net/base/test_completion_callback.h"
+#include "net/disk_cache/disk_cache.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "net/url_request/url_request_job_factory_impl.h"
@@ -384,7 +386,8 @@
 
   void TearDown() override {
     quota_manager_proxy_->SimulateQuotaManagerDestroyed();
-    base::RunLoop().RunUntilIdle();
+    disk_cache::FlushCacheThreadForTesting();
+    content::RunAllBlockingPoolTasksUntilIdle();
   }
 
   void CreateRequests(ChromeBlobStorageContext* blob_storage_context) {
diff --git a/content/browser/cache_storage/cache_storage_manager_unittest.cc b/content/browser/cache_storage/cache_storage_manager_unittest.cc
index 04d8675..f1fbe6c 100644
--- a/content/browser/cache_storage/cache_storage_manager_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_manager_unittest.cc
@@ -34,6 +34,8 @@
 #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 "content/public/test/test_utils.h"
+#include "net/disk_cache/disk_cache.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "net/url_request/url_request_job_factory_impl.h"
@@ -108,7 +110,11 @@
     CreateStorageManager();
   }
 
-  void TearDown() override { DestroyStorageManager(); }
+  void TearDown() override {
+    DestroyStorageManager();
+    disk_cache::FlushCacheThreadForTesting();
+    content::RunAllBlockingPoolTasksUntilIdle();
+  }
 
   virtual bool MemoryOnly() { return false; }
 
diff --git a/content/common/input/input_event_struct_traits.cc b/content/common/input/input_event_struct_traits.cc
index a02c660..1c57527 100644
--- a/content/common/input/input_event_struct_traits.cc
+++ b/content/common/input/input_event_struct_traits.cc
@@ -366,178 +366,175 @@
   return event.ReadLatency(&((*out)->latency_info));
 }
 
-void* StructTraits<content::mojom::EventDataView,
-                   InputEventUniquePtr>::SetUpContext(const InputEventUniquePtr&
-                                                          event) {
-  InputEventSerializationContext* context =
-      new InputEventSerializationContext();
+// static
+content::mojom::KeyDataPtr
+StructTraits<content::mojom::EventDataView, InputEventUniquePtr>::key_data(
+    const InputEventUniquePtr& event) {
+  if (!event->web_event ||
+      !blink::WebInputEvent::IsKeyboardEventType(event->web_event->GetType()))
+    return nullptr;
+  const blink::WebKeyboardEvent* key_event =
+      static_cast<const blink::WebKeyboardEvent*>(event->web_event.get());
+  return content::mojom::KeyData::New(
+      key_event->dom_key, key_event->dom_code, key_event->windows_key_code,
+      key_event->native_key_code, key_event->is_system_key,
+      key_event->is_browser_shortcut, key_event->text,
+      key_event->unmodified_text);
+}
 
+// static
+content::mojom::PointerDataPtr
+StructTraits<content::mojom::EventDataView, InputEventUniquePtr>::pointer_data(
+    const InputEventUniquePtr& event) {
   if (!event->web_event)
-    return context;
-
-  if (blink::WebInputEvent::IsKeyboardEventType(event->web_event->GetType())) {
-    const blink::WebKeyboardEvent* key_event =
-        static_cast<const blink::WebKeyboardEvent*>(event->web_event.get());
-    context->key_data = content::mojom::KeyData::New(
-        key_event->dom_key, key_event->dom_code, key_event->windows_key_code,
-        key_event->native_key_code, key_event->is_system_key,
-        key_event->is_browser_shortcut, key_event->text,
-        key_event->unmodified_text);
-    return context;
-  }
-  if (blink::WebInputEvent::IsGestureEventType(event->web_event->GetType())) {
-    const blink::WebGestureEvent* gesture_event =
-        static_cast<const blink::WebGestureEvent*>(event->web_event.get());
-
-    context->gesture_data = content::mojom::GestureData::New();
-    content::mojom::GestureDataPtr& gesture_data = context->gesture_data;
-    gesture_data->screen_position = gesture_event->PositionInScreen();
-    gesture_data->widget_position = gesture_event->PositionInWidget();
-    gesture_data->source_device = gesture_event->source_device;
-    gesture_data->unique_touch_event_id = gesture_event->unique_touch_event_id;
-    gesture_data->resending_plugin_id = gesture_event->resending_plugin_id;
-
-    switch (gesture_event->GetType()) {
-      default:
-        break;
-      case blink::WebInputEvent::Type::kGestureTapDown:
-        gesture_data->contact_size =
-            gfx::Size(gesture_event->data.tap_down.width,
-                      gesture_event->data.tap_down.height);
-        break;
-      case blink::WebInputEvent::Type::kGestureShowPress:
-        gesture_data->contact_size =
-            gfx::Size(gesture_event->data.show_press.width,
-                      gesture_event->data.show_press.height);
-        break;
-      case blink::WebInputEvent::Type::kGestureTap:
-      case blink::WebInputEvent::Type::kGestureTapUnconfirmed:
-      case blink::WebInputEvent::Type::kGestureDoubleTap:
-        gesture_data->contact_size = gfx::Size(gesture_event->data.tap.width,
-                                               gesture_event->data.tap.height);
-        gesture_data->tap_data =
-            content::mojom::TapData::New(gesture_event->data.tap.tap_count);
-        break;
-      case blink::WebInputEvent::Type::kGestureLongPress:
-        gesture_data->contact_size =
-            gfx::Size(gesture_event->data.long_press.width,
-                      gesture_event->data.long_press.height);
-        break;
-
-      case blink::WebInputEvent::Type::kGestureTwoFingerTap:
-        gesture_data->contact_size =
-            gfx::Size(gesture_event->data.two_finger_tap.first_finger_width,
-                      gesture_event->data.two_finger_tap.first_finger_height);
-        break;
-      case blink::WebInputEvent::Type::kGestureScrollBegin:
-        gesture_data->scroll_data = content::mojom::ScrollData::New(
-            gesture_event->data.scroll_begin.delta_x_hint,
-            gesture_event->data.scroll_begin.delta_y_hint,
-            gesture_event->data.scroll_begin.delta_hint_units,
-            gesture_event->data.scroll_begin.target_viewport,
-            gesture_event->data.scroll_begin.inertial_phase,
-            gesture_event->data.scroll_begin.synthetic,
-            gesture_event->data.scroll_begin.pointer_count, nullptr);
-        break;
-      case blink::WebInputEvent::Type::kGestureScrollEnd:
-        gesture_data->scroll_data = content::mojom::ScrollData::New(
-            0, 0, gesture_event->data.scroll_end.delta_units, false,
-            gesture_event->data.scroll_end.inertial_phase,
-            gesture_event->data.scroll_end.synthetic, 0, nullptr);
-        break;
-      case blink::WebInputEvent::Type::kGestureScrollUpdate:
-        gesture_data->scroll_data = content::mojom::ScrollData::New(
-            gesture_event->data.scroll_update.delta_x,
-            gesture_event->data.scroll_update.delta_y,
-            gesture_event->data.scroll_update.delta_units, false,
-            gesture_event->data.scroll_update.inertial_phase, false, 0,
-            content::mojom::ScrollUpdate::New(
-                gesture_event->data.scroll_update.velocity_x,
-                gesture_event->data.scroll_update.velocity_y,
-                gesture_event->data.scroll_update
-                    .previous_update_in_sequence_prevented,
-                gesture_event->data.scroll_update.prevent_propagation));
-        break;
-      case blink::WebInputEvent::Type::kGestureFlingStart:
-        gesture_data->fling_data = content::mojom::FlingData::New(
-            gesture_event->data.fling_start.velocity_x,
-            gesture_event->data.fling_start.velocity_y,
-            gesture_event->data.fling_start.target_viewport, false);
-        break;
-      case blink::WebInputEvent::Type::kGestureFlingCancel:
-        gesture_data->fling_data = content::mojom::FlingData::New(
-            0, 0, gesture_event->data.fling_cancel.target_viewport,
-            gesture_event->data.fling_cancel.prevent_boosting);
-        break;
-      case blink::WebInputEvent::Type::kGesturePinchUpdate:
-        gesture_data->pinch_data = content::mojom::PinchData::New(
-            gesture_event->data.pinch_update.zoom_disabled,
-            gesture_event->data.pinch_update.scale);
-        break;
-    }
-    return context;
-  }
-  if (blink::WebInputEvent::IsTouchEventType(event->web_event->GetType())) {
-    const blink::WebTouchEvent* touch_event =
-        static_cast<const blink::WebTouchEvent*>(event->web_event.get());
-
-    context->touch_data = content::mojom::TouchData::New(
-        touch_event->dispatch_type, touch_event->moved_beyond_slop_region,
-        touch_event->touch_start_or_first_touch_move,
-        touch_event->unique_touch_event_id,
-        std::vector<content::mojom::TouchPointPtr>());
-
-    for (unsigned i = 0; i < touch_event->touches_length; ++i) {
-      content::mojom::PointerDataPtr pointer_data =
-          PointerDataFromPointerProperties(touch_event->touches[i], nullptr);
-      context->touch_data->touches.emplace_back(content::mojom::TouchPoint::New(
-          touch_event->touches[i].state, touch_event->touches[i].radius_x,
-          touch_event->touches[i].radius_y,
-          touch_event->touches[i].rotation_angle, std::move(pointer_data)));
-    }
-
-    return context;
-  }
-
+    return nullptr;
   bool is_wheel_event =
       event->web_event->GetType() == blink::WebInputEvent::Type::kMouseWheel;
-  if (blink::WebInputEvent::IsMouseEventType(event->web_event->GetType()) ||
-      is_wheel_event) {
-    const blink::WebMouseEvent* mouse_event =
-        static_cast<const blink::WebMouseEvent*>(event->web_event.get());
+  if (!blink::WebInputEvent::IsMouseEventType(event->web_event->GetType()) &&
+      !is_wheel_event) {
+    return nullptr;
+  }
+  const blink::WebMouseEvent* mouse_event =
+      static_cast<const blink::WebMouseEvent*>(event->web_event.get());
 
-    content::mojom::WheelDataPtr wheel_data;
-    if (is_wheel_event) {
-      const blink::WebMouseWheelEvent* wheel_event =
-          static_cast<const blink::WebMouseWheelEvent*>(mouse_event);
-      wheel_data = content::mojom::WheelData::New(
-          wheel_event->delta_x, wheel_event->delta_y,
-          wheel_event->wheel_ticks_x, wheel_event->wheel_ticks_y,
-          wheel_event->acceleration_ratio_x, wheel_event->acceleration_ratio_y,
-          wheel_event->resending_plugin_id, wheel_event->phase,
-          wheel_event->momentum_phase, wheel_event->scroll_by_page,
-          wheel_event->has_precise_scrolling_deltas,
-          wheel_event->dispatch_type);
-    }
-
-    context->pointer_data = PointerDataFromPointerProperties(
-        *mouse_event, content::mojom::MouseData::New(mouse_event->click_count,
-                                                     std::move(wheel_data)));
-    return context;
+  content::mojom::WheelDataPtr wheel_data;
+  if (is_wheel_event) {
+    const blink::WebMouseWheelEvent* wheel_event =
+        static_cast<const blink::WebMouseWheelEvent*>(mouse_event);
+    wheel_data = content::mojom::WheelData::New(
+        wheel_event->delta_x, wheel_event->delta_y, wheel_event->wheel_ticks_x,
+        wheel_event->wheel_ticks_y, wheel_event->acceleration_ratio_x,
+        wheel_event->acceleration_ratio_y, wheel_event->resending_plugin_id,
+        wheel_event->phase, wheel_event->momentum_phase,
+        wheel_event->scroll_by_page, wheel_event->has_precise_scrolling_deltas,
+        wheel_event->dispatch_type);
   }
 
-  return context;
+  return PointerDataFromPointerProperties(
+      *mouse_event, content::mojom::MouseData::New(mouse_event->click_count,
+                                                   std::move(wheel_data)));
 }
 
-void StructTraits<content::mojom::EventDataView, InputEventUniquePtr>::
-    TearDownContext(const InputEventUniquePtr& event, void* context) {
-  delete static_cast<InputEventSerializationContext*>(context);
+// static
+content::mojom::GestureDataPtr
+StructTraits<content::mojom::EventDataView, InputEventUniquePtr>::gesture_data(
+    const InputEventUniquePtr& event) {
+  if (!event->web_event ||
+      !blink::WebInputEvent::IsGestureEventType(event->web_event->GetType()))
+    return nullptr;
+  const blink::WebGestureEvent* gesture_event =
+      static_cast<const blink::WebGestureEvent*>(event->web_event.get());
+  auto gesture_data = content::mojom::GestureData::New();
+  gesture_data->screen_position = gesture_event->PositionInScreen();
+  gesture_data->widget_position = gesture_event->PositionInWidget();
+  gesture_data->source_device = gesture_event->source_device;
+  gesture_data->unique_touch_event_id = gesture_event->unique_touch_event_id;
+  gesture_data->resending_plugin_id = gesture_event->resending_plugin_id;
+  switch (gesture_event->GetType()) {
+    default:
+      break;
+    case blink::WebInputEvent::Type::kGestureTapDown:
+      gesture_data->contact_size =
+          gfx::Size(gesture_event->data.tap_down.width,
+                    gesture_event->data.tap_down.height);
+      break;
+    case blink::WebInputEvent::Type::kGestureShowPress:
+      gesture_data->contact_size =
+          gfx::Size(gesture_event->data.show_press.width,
+                    gesture_event->data.show_press.height);
+      break;
+    case blink::WebInputEvent::Type::kGestureTap:
+    case blink::WebInputEvent::Type::kGestureTapUnconfirmed:
+    case blink::WebInputEvent::Type::kGestureDoubleTap:
+      gesture_data->contact_size = gfx::Size(gesture_event->data.tap.width,
+                                             gesture_event->data.tap.height);
+      gesture_data->tap_data =
+          content::mojom::TapData::New(gesture_event->data.tap.tap_count);
+      break;
+    case blink::WebInputEvent::Type::kGestureLongPress:
+      gesture_data->contact_size =
+          gfx::Size(gesture_event->data.long_press.width,
+                    gesture_event->data.long_press.height);
+      break;
+
+    case blink::WebInputEvent::Type::kGestureTwoFingerTap:
+      gesture_data->contact_size =
+          gfx::Size(gesture_event->data.two_finger_tap.first_finger_width,
+                    gesture_event->data.two_finger_tap.first_finger_height);
+      break;
+    case blink::WebInputEvent::Type::kGestureScrollBegin:
+      gesture_data->scroll_data = content::mojom::ScrollData::New(
+          gesture_event->data.scroll_begin.delta_x_hint,
+          gesture_event->data.scroll_begin.delta_y_hint,
+          gesture_event->data.scroll_begin.delta_hint_units,
+          gesture_event->data.scroll_begin.target_viewport,
+          gesture_event->data.scroll_begin.inertial_phase,
+          gesture_event->data.scroll_begin.synthetic,
+          gesture_event->data.scroll_begin.pointer_count, nullptr);
+      break;
+    case blink::WebInputEvent::Type::kGestureScrollEnd:
+      gesture_data->scroll_data = content::mojom::ScrollData::New(
+          0, 0, gesture_event->data.scroll_end.delta_units, false,
+          gesture_event->data.scroll_end.inertial_phase,
+          gesture_event->data.scroll_end.synthetic, 0, nullptr);
+      break;
+    case blink::WebInputEvent::Type::kGestureScrollUpdate:
+      gesture_data->scroll_data = content::mojom::ScrollData::New(
+          gesture_event->data.scroll_update.delta_x,
+          gesture_event->data.scroll_update.delta_y,
+          gesture_event->data.scroll_update.delta_units, false,
+          gesture_event->data.scroll_update.inertial_phase, false, 0,
+          content::mojom::ScrollUpdate::New(
+              gesture_event->data.scroll_update.velocity_x,
+              gesture_event->data.scroll_update.velocity_y,
+              gesture_event->data.scroll_update
+                  .previous_update_in_sequence_prevented,
+              gesture_event->data.scroll_update.prevent_propagation));
+      break;
+    case blink::WebInputEvent::Type::kGestureFlingStart:
+      gesture_data->fling_data = content::mojom::FlingData::New(
+          gesture_event->data.fling_start.velocity_x,
+          gesture_event->data.fling_start.velocity_y,
+          gesture_event->data.fling_start.target_viewport, false);
+      break;
+    case blink::WebInputEvent::Type::kGestureFlingCancel:
+      gesture_data->fling_data = content::mojom::FlingData::New(
+          0, 0, gesture_event->data.fling_cancel.target_viewport,
+          gesture_event->data.fling_cancel.prevent_boosting);
+      break;
+    case blink::WebInputEvent::Type::kGesturePinchUpdate:
+      gesture_data->pinch_data = content::mojom::PinchData::New(
+          gesture_event->data.pinch_update.zoom_disabled,
+          gesture_event->data.pinch_update.scale);
+      break;
+  }
+  return gesture_data;
 }
 
-StructTraits<content::mojom::EventDataView, InputEventUniquePtr>::
-    InputEventSerializationContext::InputEventSerializationContext() {}
+// static
+content::mojom::TouchDataPtr
+StructTraits<content::mojom::EventDataView, InputEventUniquePtr>::touch_data(
+    const InputEventUniquePtr& event) {
+  if (!event->web_event ||
+      !blink::WebInputEvent::IsTouchEventType(event->web_event->GetType()))
+    return nullptr;
 
-StructTraits<content::mojom::EventDataView, InputEventUniquePtr>::
-    InputEventSerializationContext::~InputEventSerializationContext() {}
+  const blink::WebTouchEvent* touch_event =
+      static_cast<const blink::WebTouchEvent*>(event->web_event.get());
+  auto touch_data = content::mojom::TouchData::New(
+      touch_event->dispatch_type, touch_event->moved_beyond_slop_region,
+      touch_event->touch_start_or_first_touch_move,
+      touch_event->unique_touch_event_id,
+      std::vector<content::mojom::TouchPointPtr>());
+  for (unsigned i = 0; i < touch_event->touches_length; ++i) {
+    content::mojom::PointerDataPtr pointer_data =
+        PointerDataFromPointerProperties(touch_event->touches[i], nullptr);
+    touch_data->touches.emplace_back(content::mojom::TouchPoint::New(
+        touch_event->touches[i].state, touch_event->touches[i].radius_x,
+        touch_event->touches[i].radius_y,
+        touch_event->touches[i].rotation_angle, std::move(pointer_data)));
+  }
+  return touch_data;
+}
 
 }  // namespace mojo
diff --git a/content/common/input/input_event_struct_traits.h b/content/common/input/input_event_struct_traits.h
index 9237448..d63ca68 100644
--- a/content/common/input/input_event_struct_traits.h
+++ b/content/common/input/input_event_struct_traits.h
@@ -33,44 +33,15 @@
     return event->latency_info;
   }
 
-  static const content::mojom::KeyDataPtr& key_data(
-      const InputEventUniquePtr& event,
-      void* context) {
-    return static_cast<InputEventSerializationContext*>(context)->key_data;
-  }
-
-  static const content::mojom::PointerDataPtr& pointer_data(
-      const InputEventUniquePtr& event,
-      void* context) {
-    return static_cast<InputEventSerializationContext*>(context)->pointer_data;
-  }
-
-  static const content::mojom::GestureDataPtr& gesture_data(
-      const InputEventUniquePtr& event,
-      void* context) {
-    return static_cast<InputEventSerializationContext*>(context)->gesture_data;
-  }
-
-  static const content::mojom::TouchDataPtr& touch_data(
-      const InputEventUniquePtr& event,
-      void* context) {
-    return static_cast<InputEventSerializationContext*>(context)->touch_data;
-  }
+  static content::mojom::KeyDataPtr key_data(const InputEventUniquePtr& event);
+  static content::mojom::PointerDataPtr pointer_data(
+      const InputEventUniquePtr& event);
+  static content::mojom::GestureDataPtr gesture_data(
+      const InputEventUniquePtr& event);
+  static content::mojom::TouchDataPtr touch_data(
+      const InputEventUniquePtr& event);
 
   static bool Read(content::mojom::EventDataView r, InputEventUniquePtr* out);
-  static void* SetUpContext(const InputEventUniquePtr& handle);
-  static void TearDownContext(const InputEventUniquePtr& handle, void* context);
-
- private:
-  struct InputEventSerializationContext {
-    content::mojom::KeyDataPtr key_data;
-    content::mojom::GestureDataPtr gesture_data;
-    content::mojom::PointerDataPtr pointer_data;
-    content::mojom::TouchDataPtr touch_data;
-
-    InputEventSerializationContext();
-    ~InputEventSerializationContext();
-  };
 };
 
 }  // namespace mojo
diff --git a/content/common/sandbox_linux/bpf_renderer_policy_linux.cc b/content/common/sandbox_linux/bpf_renderer_policy_linux.cc
index 67c1301..5f76e35b 100644
--- a/content/common/sandbox_linux/bpf_renderer_policy_linux.cc
+++ b/content/common/sandbox_linux/bpf_renderer_policy_linux.cc
@@ -69,6 +69,18 @@
 #if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
     defined(__aarch64__)
     case __NR_getrlimit:
+    case __NR_setrlimit:
+// We allow setrlimit to dynamically adjust the address space limit as
+// needed for WebAssembly memory objects (https://crbug.com/750378). Even
+// with setrlimit being allowed, we cannot raise rlim_max once it's
+// lowered. Thus we generally have the same protection because we normally
+// set rlim_max and rlim_cur together.
+//
+// See LinuxSandbox::LimitAddressSpace() in
+// content/common/sandbox_linux/sandbox_linux.cc and
+// ArrayBufferContents::ReserveMemory,
+// ArrayBufferContents::ReleaseReservedMemory in
+// third_party/WebKit/Source/platform/wtf/typed_arrays/ArrayBufferContents.cpp.
 #endif
 #if defined(__i386__) || defined(__arm__)
     case __NR_ugetrlimit:
diff --git a/content/common/sandbox_linux/sandbox_linux.cc b/content/common/sandbox_linux/sandbox_linux.cc
index c45d826b..530fd5a 100644
--- a/content/common/sandbox_linux/sandbox_linux.cc
+++ b/content/common/sandbox_linux/sandbox_linux.cc
@@ -22,6 +22,7 @@
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/debug/stack_trace.h"
+#include "base/feature_list.h"
 #include "base/files/scoped_file.h"
 #include "base/logging.h"
 #include "base/macros.h"
@@ -33,6 +34,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "content/common/sandbox_linux/sandbox_seccomp_bpf_linux.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/sandbox_linux.h"
 #include "sandbox/linux/services/credentials.h"
@@ -389,6 +391,7 @@
   // This is in the hope of making some kernel exploits more complex and less
   // reliable. It also limits sprays a little on 64-bit.
   rlim_t address_space_limit = std::numeric_limits<uint32_t>::max();
+  rlim_t address_space_limit_max = std::numeric_limits<uint32_t>::max();
 #if defined(__LP64__)
   // On 64 bits, V8 and possibly others will reserve massive memory ranges and
   // rely on on-demand paging for allocation.  Unfortunately, even
@@ -396,11 +399,23 @@
   // See crbug.com/169327 for a discussion.
   // On the GPU process, irrespective of V8, we can exhaust a 4GB address space
   // under normal usage, see crbug.com/271119
-  // For now, increase limit to 16GB for renderer and worker and gpu processes
+  // For now, increase limit to 16GB for renderer and worker and GPU processes
   // to accomodate.
   if (process_type == switches::kRendererProcess ||
       process_type == switches::kGpuProcess) {
     address_space_limit = 1L << 34;
+    // WebAssembly memory objects use a large amount of address space when
+    // trap-based bounds checks are enabled. To accomodate this, we allow the
+    // address space limit to adjust dynamically up to a certain limit. The
+    // limit is currently 4TiB, which should allow enough address space for any
+    // reasonable page. See https://crbug.com/750378
+    if (base::FeatureList::IsEnabled(features::kWebAssemblyTrapHandler)) {
+      address_space_limit_max = 1L << 42;
+    } else {
+      // If we are not using trap-based bounds checks, there's no reason to
+      // allow the address space limit to grow.
+      address_space_limit_max = address_space_limit;
+    }
   }
 #endif  // defined(__LP64__)
 
@@ -408,8 +423,8 @@
   // allocations that can't be index by an int.
   const rlim_t kNewDataSegmentMaxSize = std::numeric_limits<int>::max();
 
-  bool limited_as =
-      sandbox::ResourceLimits::Lower(RLIMIT_AS, address_space_limit);
+  bool limited_as = sandbox::ResourceLimits::LowerSoftAndHardLimits(
+      RLIMIT_AS, address_space_limit, address_space_limit_max);
   bool limited_data =
       sandbox::ResourceLimits::Lower(RLIMIT_DATA, kNewDataSegmentMaxSize);
 
diff --git a/content/common/service_worker/service_worker_fetch_request_struct_traits.cc b/content/common/service_worker/service_worker_fetch_request_struct_traits.cc
index dec7383..f5f94399 100644
--- a/content/common/service_worker/service_worker_fetch_request_struct_traits.cc
+++ b/content/common/service_worker/service_worker_fetch_request_struct_traits.cc
@@ -10,19 +10,6 @@
 
 namespace mojo {
 
-namespace {
-
-// Struct traits context for the FetchAPIRequest type. Since getters are invoked
-// twice when serializing the type, this reduces the load for heavy members.
-struct ServiceWorkerFetchRequestStructTraitsContext {
-  ServiceWorkerFetchRequestStructTraitsContext() = default;
-  ~ServiceWorkerFetchRequestStructTraitsContext() = default;
-
-  std::map<std::string, std::string> headers;
-};
-
-}  // namespace
-
 using blink::mojom::FetchCredentialsMode;
 using blink::mojom::FetchRedirectMode;
 using blink::mojom::FetchRequestMode;
@@ -404,29 +391,13 @@
   return false;
 }
 
-void* StructTraits<blink::mojom::FetchAPIRequestDataView,
-                   content::ServiceWorkerFetchRequest>::
-    SetUpContext(const content::ServiceWorkerFetchRequest& request) {
-  ServiceWorkerFetchRequestStructTraitsContext* context =
-      new ServiceWorkerFetchRequestStructTraitsContext();
-  context->headers.insert(request.headers.begin(), request.headers.end());
-
-  return context;
-}
-
-void StructTraits<blink::mojom::FetchAPIRequestDataView,
-                  content::ServiceWorkerFetchRequest>::
-    TearDownContext(const content::ServiceWorkerFetchRequest& request,
-                    void* context) {
-  delete static_cast<ServiceWorkerFetchRequestStructTraitsContext*>(context);
-}
-
-const std::map<std::string, std::string>&
+std::map<std::string, std::string>
 StructTraits<blink::mojom::FetchAPIRequestDataView,
              content::ServiceWorkerFetchRequest>::
-    headers(const content::ServiceWorkerFetchRequest& request, void* context) {
-  return static_cast<ServiceWorkerFetchRequestStructTraitsContext*>(context)
-      ->headers;
+    headers(const content::ServiceWorkerFetchRequest& request) {
+  std::map<std::string, std::string> header_map;
+  header_map.insert(request.headers.begin(), request.headers.end());
+  return header_map;
 }
 
 bool StructTraits<blink::mojom::FetchAPIRequestDataView,
diff --git a/content/common/service_worker/service_worker_fetch_request_struct_traits.h b/content/common/service_worker/service_worker_fetch_request_struct_traits.h
index 80912e2..c357154 100644
--- a/content/common/service_worker/service_worker_fetch_request_struct_traits.h
+++ b/content/common/service_worker/service_worker_fetch_request_struct_traits.h
@@ -73,10 +73,6 @@
 template <>
 struct StructTraits<blink::mojom::FetchAPIRequestDataView,
                     content::ServiceWorkerFetchRequest> {
-  static void* SetUpContext(const content::ServiceWorkerFetchRequest& request);
-  static void TearDownContext(const content::ServiceWorkerFetchRequest& request,
-                              void* context);
-
   static content::FetchRequestMode mode(
       const content::ServiceWorkerFetchRequest& request) {
     return request.mode;
@@ -106,9 +102,8 @@
     return request.method;
   }
 
-  static const std::map<std::string, std::string>& headers(
-      const content::ServiceWorkerFetchRequest& request,
-      void* context);
+  static std::map<std::string, std::string> headers(
+      const content::ServiceWorkerFetchRequest& request);
 
   static const std::string& blob_uuid(
       const content::ServiceWorkerFetchRequest& request) {
diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc
index 453ee7c..2bea832 100644
--- a/content/gpu/gpu_main.cc
+++ b/content/gpu/gpu_main.cc
@@ -266,7 +266,9 @@
   // exits early, the browser process will never detect it.  For this reason we
   // defer tearing down the GPU process until receiving the initialization
   // message from the browser (through mojom::GpuMain::CreateGpuService()).
-  const bool init_success = gpu_init.InitializeAndStartSandbox(command_line);
+  constexpr bool kInProcessGpu = false;
+  const bool init_success =
+      gpu_init.InitializeAndStartSandbox(command_line, kInProcessGpu);
   const bool dead_on_arrival = !init_success;
 
   logging::SetLogMessageHandler(NULL);
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 1380274..ec03f8c 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -282,6 +282,12 @@
 const base::Feature kSkipCompositingSmallScrollers{
     "SkipCompositingSmallScrollers", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// An experiment to reduce the soft tile memory limit on low-end android
+// devices.
+const base::Feature kReducedSoftTileMemoryLimitOnLowEndAndroid{
+    "kReducedSoftTileMemoryLimitOnLowEndAndroid",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Paint invalidation based on slimming paint. See https://goo.gl/eQczQW
 const base::Feature kSlimmingPaintInvalidation{
     "SlimmingPaintInvalidation", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 371fda8..2d0cfbb 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -31,6 +31,8 @@
 CONTENT_EXPORT extern const base::Feature kCompositorTouchAction;
 CONTENT_EXPORT extern const base::Feature
     kDisablePreferCompositingToLCDTextOnLowEndAndroid;
+CONTENT_EXPORT extern const base::Feature
+    kReducedSoftTileMemoryLimitOnLowEndAndroid;
 CONTENT_EXPORT extern const base::Feature kExpensiveBackgroundTimerThrottling;
 CONTENT_EXPORT extern const base::Feature kFeaturePolicy;
 CONTENT_EXPORT extern const base::Feature kFetchKeepaliveTimeoutSetting;
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc
index 3b28891..eb49179 100644
--- a/content/renderer/gpu/render_widget_compositor.cc
+++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -487,10 +487,21 @@
   // Memory policy on Android WebView does not depend on whether device is
   // low end, so always use default policy.
   if (using_low_memory_policy && !using_synchronous_compositor) {
-    // On low-end we want to be very carefull about killing other
-    // apps. So initially we use 50% more memory to avoid flickering
-    // or raster-on-demand.
-    settings.max_memory_for_prepaint_percentage = 67;
+    // |using_low_memory_policy| includes both 1GB and 512MB devices. We'd like
+    // the change of the following percentage to be applied only to 512MB
+    // devices.
+    if (base::SysInfo::AmountOfPhysicalMemoryMB() <= 512 &&
+        base::FeatureList::IsEnabled(
+            features::kReducedSoftTileMemoryLimitOnLowEndAndroid)) {
+      // The soft tile memroy limit is computed by multiplying hard limit (8MB)
+      // with this percentage. Seeting this to 13% makes the soft limit ~1MB.
+      settings.max_memory_for_prepaint_percentage = 13;
+    } else {
+      // On low-end we want to be very carefull about killing other
+      // apps. So initially we use 50% more memory to avoid flickering
+      // or raster-on-demand.
+      settings.max_memory_for_prepaint_percentage = 67;
+    }
   } else {
     // On other devices we have increased memory excessively to avoid
     // raster-on-demand already, so now we reserve 50% _only_ to avoid
@@ -540,10 +551,10 @@
       settings.preferred_tile_format = viz::RGBA_4444;
     }
 
-    // When running on a low end device, we limit cached bytes to 2MB.
-    // This allows a typical page to fit its images in cache, but prevents
-    // most long-term caching.
-    settings.decoded_image_cache_budget_bytes = 2 * 1024 * 1024;
+    // When running on a low end device, we limit cached bytes to 512KB.
+    // This allows pages which are light on images to stay in cache, but
+    // prevents most long-term caching.
+    settings.decoded_image_cache_budget_bytes = 512 * 1024;
   }
 
   if (cmd.HasSwitch(switches::kEnableLowResTiling))
diff --git a/device/sensors/BUILD.gn b/device/sensors/BUILD.gn
index 3fd38d4..cf68f3d 100644
--- a/device/sensors/BUILD.gn
+++ b/device/sensors/BUILD.gn
@@ -68,6 +68,10 @@
       "//ui/gfx",
     ]
   }
+  if (is_fuchsia) {
+    sources += [ "data_fetcher_shared_memory_fuchsia.cc" ]
+    sources -= [ "data_fetcher_shared_memory_default.cc" ]
+  }
 }
 
 if (is_android) {
diff --git a/device/sensors/data_fetcher_shared_memory_fuchsia.cc b/device/sensors/data_fetcher_shared_memory_fuchsia.cc
new file mode 100644
index 0000000..ce1aee90
--- /dev/null
+++ b/device/sensors/data_fetcher_shared_memory_fuchsia.cc
@@ -0,0 +1,29 @@
+// 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 "device/sensors/data_fetcher_shared_memory.h"
+
+#include "base/logging.h"
+
+namespace device {
+
+DataFetcherSharedMemory::DataFetcherSharedMemory() {}
+
+DataFetcherSharedMemory::~DataFetcherSharedMemory() {}
+
+bool DataFetcherSharedMemory::Start(ConsumerType consumer_type, void* buffer) {
+  // TODO(fuchsia): Implement support for sensor consumer buffers.
+  // (crbug.com/750934)
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool DataFetcherSharedMemory::Stop(ConsumerType consumer_type) {
+  // TODO(fuchsia): Implement support for sensor consumer buffers.
+  // (crbug.com/750934)
+  NOTIMPLEMENTED();
+  return false;
+}
+
+}  // namespace device
diff --git a/extensions/renderer/BUILD.gn b/extensions/renderer/BUILD.gn
index 84fe159..654c8242 100644
--- a/extensions/renderer/BUILD.gn
+++ b/extensions/renderer/BUILD.gn
@@ -106,6 +106,8 @@
     "extensions_render_frame_observer.h",
     "extensions_renderer_client.cc",
     "extensions_renderer_client.h",
+    "feature_cache.cc",
+    "feature_cache.h",
     "file_system_natives.cc",
     "file_system_natives.h",
     "gc_callback.cc",
@@ -342,6 +344,7 @@
     "bindings/event_emitter_unittest.cc",
     "bindings/exception_handler_unittest.cc",
     "event_unittest.cc",
+    "feature_cache_unittest.cc",
     "gc_callback_unittest.cc",
     "json_schema_unittest.cc",
     "messaging_utils_unittest.cc",
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index f0b69a3..80b3710b 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -1129,6 +1129,8 @@
 
   ExtensionsRendererClient::Get()->OnExtensionUnloaded(id);
 
+  bindings_system_->OnExtensionRemoved(id);
+
   active_extension_ids_.erase(id);
 
   script_injection_manager_->OnExtensionUnloaded(id);
@@ -1193,6 +1195,8 @@
     extension->permissions_data()->SetPolicyHostRestrictions(
         params.policy_blocked_hosts, params.policy_allowed_hosts);
   }
+
+  bindings_system_->OnExtensionPermissionsUpdated(params.extension_id);
   UpdateBindings(extension->id());
 }
 
diff --git a/extensions/renderer/extension_bindings_system.h b/extensions/renderer/extension_bindings_system.h
index ff32b4a..6791fcab 100644
--- a/extensions/renderer/extension_bindings_system.h
+++ b/extensions/renderer/extension_bindings_system.h
@@ -7,6 +7,8 @@
 
 #include <string>
 
+#include "extensions/common/extension_id.h"
+
 namespace base {
 class ListValue;
 }
@@ -62,6 +64,12 @@
   // TODO(devlin): Factor this out.
   virtual RequestSender* GetRequestSender() = 0;
 
+  // Called when an extension is removed.
+  virtual void OnExtensionRemoved(const ExtensionId& id) {}
+
+  // Called when an extension's permissions are updated.
+  virtual void OnExtensionPermissionsUpdated(const ExtensionId& id) {}
+
   // Returns true if any portion of the runtime API is available to the given
   // |context|. This is different than just checking features because runtime's
   // availability depends on the installed extensions and the active URL (in the
diff --git a/extensions/renderer/feature_cache.cc b/extensions/renderer/feature_cache.cc
new file mode 100644
index 0000000..22cff87
--- /dev/null
+++ b/extensions/renderer/feature_cache.cc
@@ -0,0 +1,100 @@
+// 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 "extensions/renderer/feature_cache.h"
+
+#include <algorithm>
+
+#include "base/command_line.h"
+#include "content/public/common/content_switches.h"
+#include "extensions/common/extension_api.h"
+#include "extensions/common/features/feature_provider.h"
+#include "extensions/renderer/script_context.h"
+
+namespace extensions {
+
+FeatureCache::FeatureCache() {}
+FeatureCache::~FeatureCache() = default;
+
+FeatureCache::FeatureNameVector FeatureCache::GetAvailableFeatures(
+    ScriptContext* context) {
+  const FeatureVector& features = GetFeaturesFromCache(context);
+  FeatureNameVector names;
+  names.reserve(features.size());
+  for (const Feature* feature : features) {
+    // Since we only cache based on extension id and context type, instead of
+    // all attributes of a context (like URL), we need to double-check if the
+    // feature is actually available to the context. This is still a win, since
+    // we only perform this check on the (much smaller) set of features that
+    // *may* be available, rather than all known features.
+    // TODO(devlin): Optimize this - we should be able to tell if a feature may
+    // change based on additional context attributes.
+    if (context->IsAnyFeatureAvailableToContext(
+            *feature, CheckAliasStatus::NOT_ALLOWED)) {
+      names.push_back(feature->name());
+    }
+  }
+  return names;
+}
+
+void FeatureCache::InvalidateExtension(const ExtensionId& extension_id) {
+  for (auto iter = feature_cache_.begin(); iter != feature_cache_.end();) {
+    if (iter->first.first == extension_id)
+      iter = feature_cache_.erase(iter);
+    else
+      ++iter;
+  }
+}
+
+const FeatureCache::FeatureVector& FeatureCache::GetFeaturesFromCache(
+    ScriptContext* context) {
+  CacheMapKey key(context->GetExtensionID(), context->context_type());
+
+  auto iter = feature_cache_.find(key);
+  if (iter != feature_cache_.end())
+    return iter->second;
+
+  FeatureVector features;
+  const FeatureProvider* api_feature_provider =
+      FeatureProvider::GetAPIFeatures();
+  GURL empty_url;
+  const Extension* extension = context->extension();
+  for (const auto& map_entry : api_feature_provider->GetAllFeatures()) {
+    Feature* feature = map_entry.second.get();
+    // Exclude internal APIs.
+    if (feature->IsInternal())
+      continue;
+
+    // Exclude child features (like events or specific functions).
+    // TODO(devlin): Optimize this - instead of skipping child features and then
+    // checking IsAnyFeatureAvailableToContext() (which checks child features),
+    // we should just check all features directly.
+    if (api_feature_provider->GetParent(feature) != nullptr)
+      continue;
+
+    // Skip chrome.test if this isn't a test.
+    if (map_entry.first == "test" &&
+        !base::CommandLine::ForCurrentProcess()->HasSwitch(
+            ::switches::kTestType)) {
+      continue;
+    }
+
+    if (!ExtensionAPI::GetSharedInstance()->IsAnyFeatureAvailableToContext(
+            *feature, extension, context->context_type(), empty_url,
+            CheckAliasStatus::NOT_ALLOWED)) {
+      continue;
+    }
+
+    features.push_back(feature);
+  }
+
+  std::sort(
+      features.begin(), features.end(),
+      [](const Feature* a, const Feature* b) { return a->name() < b->name(); });
+  DCHECK(std::unique(features.begin(), features.end()) == features.end());
+
+  return feature_cache_.emplace(key, std::move(features)).first->second;
+}
+
+}  // namespace extensions
diff --git a/extensions/renderer/feature_cache.h b/extensions/renderer/feature_cache.h
new file mode 100644
index 0000000..e66b42f
--- /dev/null
+++ b/extensions/renderer/feature_cache.h
@@ -0,0 +1,57 @@
+// 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 EXTENSIONS_RENDERER_FEATURE_CACHE_H_
+#define EXTENSIONS_RENDERER_FEATURE_CACHE_H_
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "extensions/common/extension_id.h"
+#include "extensions/common/features/feature.h"
+
+namespace extensions {
+class ScriptContext;
+
+// Caches features available to different extensions in different context types,
+// and returns features available to a given context. Note: currently, this is
+// only used for non-webpage contexts.
+// TODO(devlin): Use it for all context types?
+class FeatureCache {
+ public:
+  using FeatureNameVector = std::vector<std::string>;
+
+  FeatureCache();
+  ~FeatureCache();
+
+  // Returns the names of features available to the given |context| in a
+  // lexicographically sorted vector.
+  FeatureNameVector GetAvailableFeatures(ScriptContext* context);
+
+  // Invalidates the cache for the specified extension.
+  void InvalidateExtension(const ExtensionId& extension_id);
+
+ private:
+  // Note: We use a key of ExtensionId, Feature::Context to maximize cache hits.
+  // Unfortunately, this won't always be perfectly accurate, since some features
+  // may have other context-dependent restrictions (such as URLs), but caching
+  // by extension id + context + url would result in significantly fewer hits.
+  using FeatureVector = std::vector<const Feature*>;
+  using CacheMapKey = std::pair<ExtensionId, Feature::Context>;
+  using CacheMap = std::map<CacheMapKey, FeatureVector>;
+
+  // Returns the features available to the given context from the cache.
+  const FeatureVector& GetFeaturesFromCache(ScriptContext* context);
+
+  CacheMap feature_cache_;
+
+  DISALLOW_COPY_AND_ASSIGN(FeatureCache);
+};
+
+}  // namespace extensions
+
+#endif  // EXTENSIONS_RENDERER_FEATURE_CACHE_H_
diff --git a/extensions/renderer/feature_cache_unittest.cc b/extensions/renderer/feature_cache_unittest.cc
new file mode 100644
index 0000000..a5a3ccc6
--- /dev/null
+++ b/extensions/renderer/feature_cache_unittest.cc
@@ -0,0 +1,105 @@
+// 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 "extensions/renderer/feature_cache.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
+#include "components/crx_file/id_util.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_builder.h"
+#include "extensions/common/permissions/permissions_data.h"
+#include "extensions/common/value_builder.h"
+#include "extensions/renderer/bindings/api_binding_test.h"
+#include "extensions/renderer/scoped_web_frame.h"
+#include "extensions/renderer/script_context.h"
+#include "v8/include/v8.h"
+
+#include "third_party/WebKit/public/platform/WebData.h"
+#include "third_party/WebKit/public/platform/WebURL.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
+
+namespace extensions {
+
+namespace {
+
+// TODO(devlin): We should really just make ExtensionBuilder better to avoid
+// having so many of these methods lying around.
+scoped_refptr<Extension> CreateExtension(
+    const std::string& name,
+    const std::vector<std::string>& permissions) {
+  DictionaryBuilder manifest;
+  manifest.Set("name", name);
+  manifest.Set("manifest_version", 2);
+  manifest.Set("version", "0.1");
+  manifest.Set("description", "test extension");
+
+  {
+    ListBuilder permissions_builder;
+    for (const std::string& permission : permissions)
+      permissions_builder.Append(permission);
+    manifest.Set("permissions", permissions_builder.Build());
+  }
+
+  return ExtensionBuilder()
+      .SetManifest(manifest.Build())
+      .SetLocation(Manifest::INTERNAL)
+      .SetID(crx_file::id_util::GenerateId(name))
+      .Build();
+}
+
+}  // namespace
+
+using FeatureCacheTest = APIBindingTest;
+
+TEST_F(FeatureCacheTest, Basic) {
+  FeatureCache cache;
+  scoped_refptr<const Extension> extension_a = CreateExtension("a", {});
+  scoped_refptr<const Extension> extension_b =
+      CreateExtension("b", {"storage"});
+
+  v8::HandleScope handle_scope(isolate());
+  v8::Local<v8::Context> v8_context_a = MainContext();
+  v8::Local<v8::Context> v8_context_b = AddContext();
+
+  auto script_context_a = base::MakeUnique<ScriptContext>(
+      v8_context_a, nullptr, extension_a.get(),
+      Feature::BLESSED_EXTENSION_CONTEXT, extension_a.get(),
+      Feature::BLESSED_EXTENSION_CONTEXT);
+  auto script_context_b = base::MakeUnique<ScriptContext>(
+      v8_context_b, nullptr, extension_b.get(),
+      Feature::BLESSED_EXTENSION_CONTEXT, extension_b.get(),
+      Feature::BLESSED_EXTENSION_CONTEXT);
+
+  auto has_feature = [&cache](const std::unique_ptr<ScriptContext>& context,
+                              const std::string& feature) {
+    return base::ContainsValue(cache.GetAvailableFeatures(context.get()),
+                               feature);
+  };
+
+  // To start, context a should not have access to storage, but context b
+  // should.
+  EXPECT_FALSE(has_feature(script_context_a, "storage"));
+  EXPECT_TRUE(has_feature(script_context_b, "storage"));
+
+  // Update extension b's permissions and invalidate the cache.
+  extension_b->permissions_data()->SetPermissions(
+      base::MakeUnique<PermissionSet>(), base::MakeUnique<PermissionSet>());
+  cache.InvalidateExtension(extension_b->id());
+
+  // Now, neither context should have storage access.
+  EXPECT_FALSE(has_feature(script_context_a, "storage"));
+  EXPECT_FALSE(has_feature(script_context_b, "storage"));
+
+  script_context_a->Invalidate();
+  script_context_b->Invalidate();
+}
+
+// TODO(devlin): It'd be nice to test that the FeatureCache properly handles
+// features that are restricted to certain URLs; unfortunately, for that we'd
+// need a stubbed out web frame with a given URL, and there's no good way to do
+// that outside Blink.
+
+}  // namespace extensions
diff --git a/extensions/renderer/native_extension_bindings_system.cc b/extensions/renderer/native_extension_bindings_system.cc
index ceac3305..b1c5987 100644
--- a/extensions/renderer/native_extension_bindings_system.cc
+++ b/extensions/renderer/native_extension_bindings_system.cc
@@ -507,38 +507,13 @@
     return;
   }
 
-  const FeatureProvider* api_feature_provider =
-      FeatureProvider::GetAPIFeatures();
+  FeatureCache::FeatureNameVector features =
+      feature_cache_.GetAvailableFeatures(context);
   base::StringPiece last_accessor;
-  for (const auto& map_entry : api_feature_provider->GetAllFeatures()) {
+  for (const std::string& feature : features) {
     // If we've already set up an accessor for the immediate property of the
     // chrome object, we don't need to do more.
-    if (IsPrefixedAPI(map_entry.first, last_accessor))
-      continue;
-
-    // Internal APIs are included via require(api_name) from internal code
-    // rather than chrome[api_name].
-    if (map_entry.second->IsInternal())
-      continue;
-
-    // If this API has a parent feature (and isn't marked 'noparent'),
-    // then this must be a function or event, so we should not register.
-    if (api_feature_provider->GetParent(map_entry.second.get()) != nullptr)
-      continue;
-
-    // Skip chrome.test if this isn't a test.
-    if (map_entry.first == "test" &&
-        !base::CommandLine::ForCurrentProcess()->HasSwitch(
-            ::switches::kTestType)) {
-      continue;
-    }
-
-    // TODO(devlin): UpdateBindingsForContext can be called during context
-    // creation, but also when e.g. permissions change. We need to be checking
-    // for whether or not the API already exists on the object as well as
-    // if we need to remove any existing APIs.
-    if (!context->IsAnyFeatureAvailableToContext(*map_entry.second,
-                                                 CheckAliasStatus::NOT_ALLOWED))
+    if (IsPrefixedAPI(feature, last_accessor))
       continue;
 
     // We've found an API that's available to the extension. Normally, we will
@@ -546,8 +521,13 @@
     // cases, this will be a prefixed API, such as 'app.runtime'. Find what the
     // property on the chrome object is named, and use that. So in the case of
     // 'app.runtime', we surface a getter for simply 'app'.
+    //
+    // TODO(devlin): UpdateBindingsForContext can be called during context
+    // creation, but also when e.g. permissions change. Do we need to be
+    // checking for whether or not the API already exists on the object as well
+    // as if we need to remove any existing APIs?
     base::StringPiece accessor_name =
-        GetFirstDifferentAPIName(map_entry.first, base::StringPiece());
+        GetFirstDifferentAPIName(feature, base::StringPiece());
     last_accessor = accessor_name;
     if (!set_accessor(accessor_name)) {
       LOG(ERROR) << "Failed to create API on Chrome object.";
@@ -597,6 +577,15 @@
   return ipc_message_sender_.get();
 }
 
+void NativeExtensionBindingsSystem::OnExtensionPermissionsUpdated(
+    const ExtensionId& id) {
+  feature_cache_.InvalidateExtension(id);
+}
+
+void NativeExtensionBindingsSystem::OnExtensionRemoved(const ExtensionId& id) {
+  feature_cache_.InvalidateExtension(id);
+}
+
 v8::Local<v8::Object> NativeExtensionBindingsSystem::GetAPIObjectForTesting(
     ScriptContext* context,
     const std::string& api_name) {
diff --git a/extensions/renderer/native_extension_bindings_system.h b/extensions/renderer/native_extension_bindings_system.h
index bac4e85..64c598b 100644
--- a/extensions/renderer/native_extension_bindings_system.h
+++ b/extensions/renderer/native_extension_bindings_system.h
@@ -13,6 +13,7 @@
 #include "extensions/renderer/bindings/api_bindings_system.h"
 #include "extensions/renderer/bindings/event_emitter.h"
 #include "extensions/renderer/extension_bindings_system.h"
+#include "extensions/renderer/feature_cache.h"
 #include "v8/include/v8.h"
 
 namespace extensions {
@@ -47,6 +48,8 @@
                       const std::string& error) override;
   RequestSender* GetRequestSender() override;
   IPCMessageSender* GetIPCMessageSender() override;
+  void OnExtensionPermissionsUpdated(const ExtensionId& id) override;
+  void OnExtensionRemoved(const ExtensionId& id) override;
 
   APIBindingsSystem* api_system() { return &api_system_; }
 
@@ -97,6 +100,8 @@
   // The APIBindingsSystem associated with this class.
   APIBindingsSystem api_system_;
 
+  FeatureCache feature_cache_;
+
   // A function to acquire an internal API.
   v8::Eternal<v8::FunctionTemplate> get_internal_api_;
 
diff --git a/extensions/renderer/native_extension_bindings_system_unittest.cc b/extensions/renderer/native_extension_bindings_system_unittest.cc
index 9ebd8c36..972779e 100644
--- a/extensions/renderer/native_extension_bindings_system_unittest.cc
+++ b/extensions/renderer/native_extension_bindings_system_unittest.cc
@@ -1024,6 +1024,7 @@
   extension->permissions_data()->SetPermissions(
       base::MakeUnique<PermissionSet>(), base::MakeUnique<PermissionSet>());
 
+  bindings_system()->OnExtensionPermissionsUpdated(extension->id());
   bindings_system()->UpdateBindingsForContext(script_context);
   {
     // TODO(devlin): Neither the native nor JS bindings systems clear the
@@ -1064,6 +1065,7 @@
         base::MakeUnique<PermissionSet>(apis, ManifestPermissionSet(),
                                         URLPatternSet(), URLPatternSet()),
         base::MakeUnique<PermissionSet>());
+    bindings_system()->OnExtensionPermissionsUpdated(extension->id());
     bindings_system()->UpdateBindingsForContext(script_context);
   }
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
index bd0656e..10ffaf7 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -319,8 +319,16 @@
 error::Error GLES2DecoderPassthroughImpl::DoBindBufferBase(GLenum target,
                                                            GLuint index,
                                                            GLuint buffer) {
-  glBindBufferBase(target, index, GetBufferServiceID(buffer, resources_,
-                                                     bind_generates_resource_));
+  FlushErrors();
+  glBindBufferBase(
+      target, index,
+      GetBufferServiceID(buffer, resources_, bind_generates_resource_));
+  if (FlushErrors()) {
+    return error::kNoError;
+  }
+
+  bound_buffers_[target] = buffer;
+
   return error::kNoError;
 }
 
@@ -329,9 +337,17 @@
                                                             GLuint buffer,
                                                             GLintptr offset,
                                                             GLsizeiptr size) {
-  glBindBufferRange(target, index, GetBufferServiceID(buffer, resources_,
-                                                      bind_generates_resource_),
-                    offset, size);
+  FlushErrors();
+  glBindBufferRange(
+      target, index,
+      GetBufferServiceID(buffer, resources_, bind_generates_resource_), offset,
+      size);
+  if (FlushErrors()) {
+    return error::kNoError;
+  }
+
+  bound_buffers_[target] = buffer;
+
   return error::kNoError;
 }
 
diff --git a/gpu/command_buffer/tests/gl_clear_framebuffer_unittest.cc b/gpu/command_buffer/tests/gl_clear_framebuffer_unittest.cc
index 1e99d48b..b3b3ae30 100644
--- a/gpu/command_buffer/tests/gl_clear_framebuffer_unittest.cc
+++ b/gpu/command_buffer/tests/gl_clear_framebuffer_unittest.cc
@@ -58,6 +58,9 @@
   void SetDrawDepth(GLfloat depth);
   void DrawQuad();
 
+  void SetupFramebufferWithDepthStencil();
+  void DestroyFramebuffer();
+
   void TearDown() override {
     GLTestHelper::CheckGLError("no errors", __LINE__);
     gl_.Destroy();
@@ -67,6 +70,10 @@
   GLManager gl_;
   GLuint color_handle_;
   GLuint depth_handle_;
+
+  GLuint framebuffer_handle_;
+  GLuint color_buffer_handle_;
+  GLuint depth_stencil_handle_;
 };
 
 void GLClearFramebufferTest::InitDraw() {
@@ -113,6 +120,40 @@
   glDrawArrays(GL_TRIANGLES, 0, 6);
 }
 
+void GLClearFramebufferTest::SetupFramebufferWithDepthStencil() {
+  glGenFramebuffers(1, &framebuffer_handle_);
+  DCHECK_NE(0u, framebuffer_handle_);
+  glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_handle_);
+
+  static const int kFramebufferSize = 4;
+
+  glGenRenderbuffers(1, &color_buffer_handle_);
+  DCHECK_NE(0u, color_buffer_handle_);
+  glBindRenderbuffer(GL_RENDERBUFFER, color_buffer_handle_);
+  glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, kFramebufferSize,
+                        kFramebufferSize);
+  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                            GL_RENDERBUFFER, color_buffer_handle_);
+
+  glGenRenderbuffers(1, &depth_stencil_handle_);
+  DCHECK_NE(0u, depth_stencil_handle_);
+  glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil_handle_);
+  glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES,
+                        kFramebufferSize, kFramebufferSize);
+  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+                            GL_RENDERBUFFER, depth_stencil_handle_);
+  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+                            GL_RENDERBUFFER, depth_stencil_handle_);
+
+  glViewport(0, 0, kFramebufferSize, kFramebufferSize);
+}
+
+void GLClearFramebufferTest::DestroyFramebuffer() {
+  glDeleteRenderbuffers(1, &color_buffer_handle_);
+  glDeleteRenderbuffers(1, &depth_stencil_handle_);
+  glDeleteFramebuffers(1, &framebuffer_handle_);
+}
+
 INSTANTIATE_TEST_CASE_P(GLClearFramebufferTestWithParam,
                         GLClearFramebufferTest,
                         ::testing::Values(true, false));
@@ -177,6 +218,8 @@
     return;
   }
 
+  SetupFramebufferWithDepthStencil();
+
   const GLuint kStencilRef = 1 << 2;
   InitDraw();
   SetDrawColor(1.0f, 0.0f, 0.0f, 1.0f);
@@ -222,6 +265,8 @@
   // Verify - depth test should have passed, so red.
   EXPECT_TRUE(
       GLTestHelper::CheckPixels(0, 0, 1, 1, 0 /* tolerance */, kRed, nullptr));
+
+  DestroyFramebuffer();
 }
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/tests/occlusion_query_unittest.cc b/gpu/command_buffer/tests/occlusion_query_unittest.cc
index 2f749c7..80331d0e 100644
--- a/gpu/command_buffer/tests/occlusion_query_unittest.cc
+++ b/gpu/command_buffer/tests/occlusion_query_unittest.cc
@@ -12,23 +12,36 @@
 
 namespace gpu {
 
+static const int kBackbufferSize = 512;
+
 class OcclusionQueryTest : public testing::Test {
  protected:
   void SetUp() override {
     GLManager::Options options;
-    options.size = gfx::Size(512, 512);
+    options.size = gfx::Size(kBackbufferSize, kBackbufferSize);
     gl_.Initialize(options);
+    SetupFramebuffer();
   }
 
-  void TearDown() override { gl_.Destroy(); }
+  void TearDown() override {
+    DestroyFramebuffer();
+    gl_.Destroy();
+  }
 
   void DrawRect(float x, float z, float scale, float* color);
 
+  void SetupFramebuffer();
+  void DestroyFramebuffer();
+
   GLManager gl_;
 
   GLint position_loc_;
   GLint matrix_loc_;
   GLint color_loc_;
+
+  GLuint framebuffer_handle_;
+  GLuint color_buffer_handle_;
+  GLuint depth_stencil_handle_;
 };
 
 static void SetMatrix(float x, float z, float scale, float* matrix) {
@@ -65,6 +78,36 @@
   glDrawArrays(GL_TRIANGLES, 0, 6);
 }
 
+void OcclusionQueryTest::SetupFramebuffer() {
+  glGenFramebuffers(1, &framebuffer_handle_);
+  DCHECK_NE(0u, framebuffer_handle_);
+  glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_handle_);
+
+  glGenRenderbuffers(1, &color_buffer_handle_);
+  DCHECK_NE(0u, color_buffer_handle_);
+  glBindRenderbuffer(GL_RENDERBUFFER, color_buffer_handle_);
+  glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, kBackbufferSize,
+                        kBackbufferSize);
+  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                            GL_RENDERBUFFER, color_buffer_handle_);
+
+  glGenRenderbuffers(1, &depth_stencil_handle_);
+  DCHECK_NE(0u, depth_stencil_handle_);
+  glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil_handle_);
+  glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES,
+                        kBackbufferSize, kBackbufferSize);
+  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+                            GL_RENDERBUFFER, depth_stencil_handle_);
+  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+                            GL_RENDERBUFFER, depth_stencil_handle_);
+}
+
+void OcclusionQueryTest::DestroyFramebuffer() {
+  glDeleteRenderbuffers(1, &color_buffer_handle_);
+  glDeleteRenderbuffers(1, &depth_stencil_handle_);
+  glDeleteFramebuffers(1, &framebuffer_handle_);
+}
+
 TEST_F(OcclusionQueryTest, Occlusion) {
 #if defined(OS_MACOSX)
   EXPECT_TRUE(GLTestHelper::HasExtension("GL_EXT_occlusion_query_boolean"))
diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc
index 1cb9b3c..7050908 100644
--- a/gpu/ipc/service/gpu_init.cc
+++ b/gpu/ipc/service/gpu_init.cc
@@ -135,7 +135,8 @@
 
 GpuInit::~GpuInit() {}
 
-bool GpuInit::InitializeAndStartSandbox(const base::CommandLine& command_line) {
+bool GpuInit::InitializeAndStartSandbox(const base::CommandLine& command_line,
+                                        bool in_process_gpu) {
   if (command_line.HasSwitch(switches::kSupportsDualGpus)) {
     std::set<int> workarounds;
     gpu::GpuDriverBugList::AppendWorkaroundsFromCommandLine(&workarounds,
@@ -192,7 +193,7 @@
       gpu_info_.driver_vendor == "NVIDIA" && !CanAccessNvidiaDeviceFile())
     return false;
 #endif
-  gpu_info_.in_process_gpu = false;
+  gpu_info_.in_process_gpu = in_process_gpu;
 
   gpu_info_.passthrough_cmd_decoder =
       gl::UsePassthroughCommandDecoder(&command_line);
@@ -218,7 +219,7 @@
   // Initialize Ozone GPU after the watchdog in case it hangs. The sandbox
   // may also have started at this point.
   ui::OzonePlatform::InitParams params;
-  params.single_process = false;
+  params.single_process = in_process_gpu;
   ui::OzonePlatform::InitializeForGPU(params);
 #endif
 
diff --git a/gpu/ipc/service/gpu_init.h b/gpu/ipc/service/gpu_init.h
index 268fffc61..5617bce 100644
--- a/gpu/ipc/service/gpu_init.h
+++ b/gpu/ipc/service/gpu_init.h
@@ -36,7 +36,8 @@
     sandbox_helper_ = helper;
   }
 
-  bool InitializeAndStartSandbox(const base::CommandLine& command_line);
+  bool InitializeAndStartSandbox(const base::CommandLine& command_line,
+                                 bool in_process_gpu);
 
   const GPUInfo& gpu_info() const { return gpu_info_; }
   const GpuFeatureInfo& gpu_feature_info() const { return gpu_feature_info_; }
diff --git a/headless/BUILD.gn b/headless/BUILD.gn
index 661f806..91987a4 100644
--- a/headless/BUILD.gn
+++ b/headless/BUILD.gn
@@ -804,6 +804,12 @@
   }
 }
 
+if (is_fuchsia) {
+  fuchsia_executable_runner("headless_shell_fuchsia") {
+    exe_name = "headless_shell"
+  }
+}
+
 executable("headless_shell") {
   sources = [
     "app/headless_shell_main.cc",
@@ -817,6 +823,10 @@
   if (is_win) {
     deps += [ "//build/win:default_exe_manifest" ]
   }
+
+  if (is_fuchsia) {
+    deps += [ ":headless_shell_fuchsia" ]
+  }
 }
 
 process_version("version_header") {
diff --git a/infra/config/cq.cfg b/infra/config/cq.cfg
index 54c4888..eec7f9105 100644
--- a/infra/config/cq.cfg
+++ b/infra/config/cq.cfg
@@ -94,7 +94,8 @@
       name: "master.tryserver.chromium.win"
       builders { name: "win_chromium_compile_dbg_ng" }
       builders { name: "win_chromium_rel_ng" }
-      builders { name: "win_chromium_x64_rel_ng" }
+      # crbug.com/753184 - take out of the CQ temporarily.
+      # builders { name: "win_chromium_x64_rel_ng" }
       builders { name: "win_clang" }
     }
   }
diff --git a/ios/chrome/browser/tabs/tab_model.mm b/ios/chrome/browser/tabs/tab_model.mm
index 493c1e6..94993a0 100644
--- a/ios/chrome/browser/tabs/tab_model.mm
+++ b/ios/chrome/browser/tabs/tab_model.mm
@@ -43,6 +43,7 @@
 #import "ios/chrome/browser/tabs/tab_model_synced_window_delegate.h"
 #import "ios/chrome/browser/tabs/tab_model_web_state_list_delegate.h"
 #import "ios/chrome/browser/tabs/tab_parenting_observer.h"
+#import "ios/chrome/browser/web/page_placeholder_tab_helper.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_fast_enumeration_helper.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_metrics_observer.h"
@@ -683,13 +684,13 @@
 
   for (int index = oldCount; index < _webStateList->count(); ++index) {
     web::WebState* webState = _webStateList->GetWebStateAt(index);
-    Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
-    tab.webController.usePlaceholderOverlay = YES;
+    PagePlaceholderTabHelper::FromWebState(webState)
+        ->AddPlaceholderForNextNavigation();
 
     // Restore the CertificatePolicyCache (note that webState is invalid after
     // passing it via move semantic to -initWithWebState:model:).
-    UpdateCertificatePolicyCacheFromWebState(policyCache, [tab webState]);
-    [restoredTabs addObject:tab];
+    UpdateCertificatePolicyCacheFromWebState(policyCache, webState);
+    [restoredTabs addObject:LegacyTabHelper::GetTabForWebState(webState)];
   }
 
   // If there was only one tab and it was the new tab page, clobber it.
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 4b4d9b9..e2ccc99c 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -695,6 +695,15 @@
                 alignment:(BubbleAlignment)alignment
                      text:(NSString*)text;
 
+// Waits to present a bubble associated with the new tab tip in-product help
+// promotion until the feature engagement tracker database is fully initialized.
+// Does not present the bubble if |tabTipBubblePresenter.userEngaged| is |YES|
+// to prevent resetting |tabTipBubblePresenter| and affecting the value of
+// |userEngaged|.
+- (void)presentNewTabTipBubbleOnInitialized;
+// Presents a bubble associated with the new tab tip in-product help promotion.
+- (void)presentNewTabTipBubble;
+
 // Create and show the find bar.
 - (void)initFindBarForTab;
 // Search for find bar query string.
@@ -1260,36 +1269,7 @@
   [super viewDidAppear:animated];
   self.viewVisible = YES;
   [self updateDialogPresenterActiveState];
-
-  // If the tab tip bubble has already been presented and the user is still
-  // considered engaged, it can't be overwritten or set to |nil| or else it will
-  // reset the |userEngaged| property. Once the user is not engaged, the bubble
-  // can be safely overwritten or set to |nil|.
-  if (!self.tabTipBubblePresenter.isUserEngaged) {
-    __weak BrowserViewController* weakSelf = self;
-    void (^onInitializedBlock)(bool) = ^(bool successfullyLoaded) {
-      NSString* text =
-          l10n_util::GetNSStringWithFixup(IDS_IOS_NEW_TAB_IPH_PROMOTION_TEXT);
-      CGPoint tabSwitcherAnchor = [weakSelf.toolbarController
-          anchorPointForTabSwitcherButton:BubbleArrowDirectionUp];
-      weakSelf.tabTipBubblePresenter = [weakSelf
-          bubblePresenterForFeature:feature_engagement::kIPHNewTabTipFeature
-                          direction:BubbleArrowDirectionUp
-                          alignment:BubbleAlignmentTrailing
-                               text:text];
-      [weakSelf.tabTipBubblePresenter
-          presentInViewController:self
-                             view:self.view
-                      anchorPoint:tabSwitcherAnchor];
-    };
-
-    // Because the new tab tip occurs on startup, the feature engagement
-    // tracker's database is not guaranteed to be loaded by this time. For the
-    // bubble to appear properly, a callback is used to guarantee the event data
-    // is loaded before the check to see if the promotion should be displayed.
-    feature_engagement::TrackerFactory::GetForBrowserState(self.browserState)
-        ->AddOnInitializedCallback(base::BindBlockArc(onInitializedBlock));
-  }
+  [self presentNewTabTipBubbleOnInitialized];
 }
 
 - (void)viewWillAppear:(BOOL)animated {
@@ -2071,6 +2051,51 @@
   return bubbleViewControllerPresenter;
 }
 
+- (void)presentNewTabTipBubbleOnInitialized {
+  // If the tab tip bubble has already been presented and the user is still
+  // considered engaged, it can't be overwritten or set to |nil| or else it will
+  // reset the |userEngaged| property. Once the user is not engaged, the bubble
+  // can be safely overwritten or set to |nil|.
+  if (!self.tabTipBubblePresenter.isUserEngaged) {
+    __weak BrowserViewController* weakSelf = self;
+    void (^onInitializedBlock)(bool) = ^(bool successfullyLoaded) {
+      [weakSelf presentNewTabTipBubble];
+    };
+
+    // Because the new tab tip occurs on startup, the feature engagement
+    // tracker's database is not guaranteed to be loaded by this time. For the
+    // bubble to appear properly, a callback is used to guarantee the event data
+    // is loaded before the check to see if the promotion should be displayed.
+    feature_engagement::TrackerFactory::GetForBrowserState(self.browserState)
+        ->AddOnInitializedCallback(base::BindBlockArc(onInitializedBlock));
+  }
+}
+
+- (void)presentNewTabTipBubble {
+  NSString* text =
+      l10n_util::GetNSStringWithFixup(IDS_IOS_NEW_TAB_IPH_PROMOTION_TEXT);
+  CGPoint tabSwitcherAnchor;
+  if (IsIPadIdiom()) {
+    DCHECK([self.tabStripController
+        respondsToSelector:@selector(anchorPointForTabSwitcherButton:)]);
+    tabSwitcherAnchor = [self.tabStripController
+        anchorPointForTabSwitcherButton:BubbleArrowDirectionUp];
+  } else {
+    DCHECK([self.toolbarController
+        respondsToSelector:@selector(anchorPointForTabSwitcherButton:)]);
+    tabSwitcherAnchor = [self.toolbarController
+        anchorPointForTabSwitcherButton:BubbleArrowDirectionUp];
+  }
+  self.tabTipBubblePresenter =
+      [self bubblePresenterForFeature:feature_engagement::kIPHNewTabTipFeature
+                            direction:BubbleArrowDirectionUp
+                            alignment:BubbleAlignmentTrailing
+                                 text:text];
+  [self.tabTipBubblePresenter presentInViewController:self
+                                                 view:self.view
+                                          anchorPoint:tabSwitcherAnchor];
+}
+
 #pragma mark - Tap handling
 
 - (void)setLastTapPoint:(OpenNewTabCommand*)command {
diff --git a/ios/chrome/browser/ui/bubble/bubble_view_anchor_point_provider.h b/ios/chrome/browser/ui/bubble/bubble_view_anchor_point_provider.h
index c16e688..31dd9b2a 100644
--- a/ios/chrome/browser/ui/bubble/bubble_view_anchor_point_provider.h
+++ b/ios/chrome/browser/ui/bubble/bubble_view_anchor_point_provider.h
@@ -11,6 +11,7 @@
 // Points must be in window-coordinates.
 @protocol BubbleViewAnchorPointProvider
 
+@optional
 // Returns either the top-middle or bottom-middle of the tab switcher button
 // based on |direction|. Point is in window-coordinates.
 - (CGPoint)anchorPointForTabSwitcherButton:(BubbleArrowDirection)direction;
diff --git a/ios/chrome/browser/ui/payments/contact_info_edit_mediator.mm b/ios/chrome/browser/ui/payments/contact_info_edit_mediator.mm
index a6f2686..44bf9d4 100644
--- a/ios/chrome/browser/ui/payments/contact_info_edit_mediator.mm
+++ b/ios/chrome/browser/ui/payments/contact_info_edit_mediator.mm
@@ -68,11 +68,17 @@
 #pragma mark - PaymentRequestEditViewControllerDataSource
 
 - (NSString*)title {
-  // TODO(crbug.com/602666): Title varies depending on what field is missing.
-  // e.g., Add Email vs. Add Phone Number.
-  return self.profile
-             ? l10n_util::GetNSString(IDS_PAYMENTS_EDIT_CONTACT_DETAILS_LABEL)
-             : l10n_util::GetNSString(IDS_PAYMENTS_ADD_CONTACT_DETAILS_LABEL);
+  if (!self.profile)
+    return l10n_util::GetNSString(IDS_PAYMENTS_ADD_CONTACT_DETAILS_LABEL);
+
+  if (self.paymentRequest->profile_comparator()->IsContactInfoComplete(
+          self.profile)) {
+    return l10n_util::GetNSString(IDS_PAYMENTS_EDIT_CONTACT_DETAILS_LABEL);
+  }
+
+  return base::SysUTF16ToNSString(
+      self.paymentRequest->profile_comparator()
+          ->GetTitleForMissingContactFields(*self.profile));
 }
 
 - (CollectionViewItem*)headerItem {
diff --git a/ios/chrome/browser/ui/payments/contact_info_edit_mediator_unittest.mm b/ios/chrome/browser/ui/payments/contact_info_edit_mediator_unittest.mm
index 35400c26..45ac83c 100644
--- a/ios/chrome/browser/ui/payments/contact_info_edit_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/contact_info_edit_mediator_unittest.mm
@@ -196,3 +196,26 @@
 
   EXPECT_OCMOCK_VERIFY(consumer);
 }
+
+// Tests that the editor's title is correct in various situations.
+TEST_F(PaymentRequestContactInfoEditMediatorTest, Title) {
+  // No profile, so the title should ask to add contact details.
+  ContactInfoEditMediator* mediator =
+      [[ContactInfoEditMediator alloc] initWithPaymentRequest:payment_request()
+                                                      profile:nil];
+  EXPECT_TRUE([mediator.title
+      isEqualToString:l10n_util::GetNSString(
+                          IDS_PAYMENTS_ADD_CONTACT_DETAILS_LABEL)]);
+
+  // Name missing, so title should ask to add a name.
+  autofill::AutofillProfile autofill_profile = autofill::test::GetFullProfile();
+  autofill::test::SetProfileInfo(&autofill_profile, "", "", "",
+                                 "johndoe@example.com", "", "", "", "", "", "",
+                                 "", "16502111111");
+
+  mediator = [[ContactInfoEditMediator alloc]
+      initWithPaymentRequest:payment_request()
+                     profile:&autofill_profile];
+  EXPECT_TRUE([mediator.title
+      isEqualToString:l10n_util::GetNSString(IDS_PAYMENTS_ADD_NAME)]);
+}
diff --git a/ios/chrome/browser/ui/side_swipe/BUILD.gn b/ios/chrome/browser/ui/side_swipe/BUILD.gn
index 233f2ea..1ea03427 100644
--- a/ios/chrome/browser/ui/side_swipe/BUILD.gn
+++ b/ios/chrome/browser/ui/side_swipe/BUILD.gn
@@ -30,6 +30,7 @@
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/ntp",
     "//ios/chrome/browser/ui/tabs",
+    "//ios/chrome/browser/web",
     "//ios/chrome/common",
     "//ios/web",
     "//ui/base",
diff --git a/ios/chrome/browser/ui/side_swipe/card_side_swipe_view.mm b/ios/chrome/browser/ui/side_swipe/card_side_swipe_view.mm
index 109380d..8023824e 100644
--- a/ios/chrome/browser/ui/side_swipe/card_side_swipe_view.mm
+++ b/ios/chrome/browser/ui/side_swipe/card_side_swipe_view.mm
@@ -22,6 +22,7 @@
 #include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/util/constraints_ui_util.h"
+#import "ios/chrome/browser/web/page_placeholder_tab_helper.h"
 #include "ios/chrome/grit/ios_theme_resources.h"
 #import "ios/web/web_state/ui/crw_web_controller.h"
 #include "url/gurl.h"
@@ -241,7 +242,8 @@
   dispatch_queue_t priorityQueue =
       dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
   [tab retrieveSnapshot:^(UIImage* image) {
-    if (tab.webController.usePlaceholderOverlay &&
+    if (PagePlaceholderTabHelper::FromWebState(tab.webState)
+            ->will_add_placeholder_for_next_navigation() &&
         !ios::device_util::IsSingleCoreDevice()) {
       [card setImage:[CRWWebController defaultSnapshotImage]];
       dispatch_async(priorityQueue, ^{
diff --git a/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm b/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
index 397b5d2..85bbec0 100644
--- a/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
+++ b/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
@@ -21,6 +21,7 @@
 #import "ios/chrome/browser/ui/side_swipe/side_swipe_util.h"
 #import "ios/chrome/browser/ui/side_swipe_gesture_recognizer.h"
 #include "ios/chrome/browser/ui/ui_util.h"
+#import "ios/chrome/browser/web/page_placeholder_tab_helper.h"
 #import "ios/web/public/web_state/web_state_observer_bridge.h"
 #import "ios/web/web_state/ui/crw_web_controller.h"
 
@@ -253,7 +254,8 @@
       break;
 
     Tab* tab = [model_ tabAtIndex:index];
-    if (tab && tab.webController.usePlaceholderOverlay) {
+    if (tab && PagePlaceholderTabHelper::FromWebState(tab.webState)
+                   ->will_add_placeholder_for_next_navigation()) {
       [sessionIDs addObject:tab.tabId];
     }
     index = index + dx;
diff --git a/ios/chrome/browser/ui/tabs/BUILD.gn b/ios/chrome/browser/ui/tabs/BUILD.gn
index 5f90849ff..99bcd06 100644
--- a/ios/chrome/browser/ui/tabs/BUILD.gn
+++ b/ios/chrome/browser/ui/tabs/BUILD.gn
@@ -39,6 +39,7 @@
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/ui",
+    "//ios/chrome/browser/ui/bubble",
     "//ios/chrome/browser/ui/colors",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/util",
diff --git a/ios/chrome/browser/ui/tabs/tab_strip_controller.h b/ios/chrome/browser/ui/tabs/tab_strip_controller.h
index a5c7b11..304e8d4 100644
--- a/ios/chrome/browser/ui/tabs/tab_strip_controller.h
+++ b/ios/chrome/browser/ui/tabs/tab_strip_controller.h
@@ -7,6 +7,8 @@
 
 #import <UIKit/UIKit.h>
 
+#import "ios/chrome/browser/ui/bubble/bubble_view_anchor_point_provider.h"
+
 @protocol ApplicationCommands;
 @protocol BrowserCommands;
 @protocol FullScreenControllerDelegate;
@@ -32,7 +34,7 @@
 // display in sync with the TabModel.  This controller is only instantiated on
 // tablet.  The tab strip view itself is a subclass of UIScrollView, which
 // manages scroll offsets and scroll animations.
-@interface TabStripController : NSObject
+@interface TabStripController : NSObject<BubbleViewAnchorPointProvider>
 
 @property(nonatomic, assign) BOOL highlightsSelectedTab;
 @property(nonatomic, readonly, retain) UIView* view;
diff --git a/ios/chrome/browser/ui/tabs/tab_strip_controller.mm b/ios/chrome/browser/ui/tabs/tab_strip_controller.mm
index 8af3768..23258ac9 100644
--- a/ios/chrome/browser/ui/tabs/tab_strip_controller.mm
+++ b/ios/chrome/browser/ui/tabs/tab_strip_controller.mm
@@ -20,6 +20,9 @@
 #import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/tabs/tab_model_observer.h"
+#import "ios/chrome/browser/ui/bubble/bubble_util.h"
+#import "ios/chrome/browser/ui/bubble/bubble_view.h"
+#import "ios/chrome/browser/ui/bubble/bubble_view_anchor_point_provider.h"
 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
@@ -698,6 +701,16 @@
 }
 
 #pragma mark -
+#pragma mark BubbleViewAnchorPointProvider methods
+
+- (CGPoint)anchorPointForTabSwitcherButton:(BubbleArrowDirection)direction {
+  CGPoint anchorPoint =
+      bubble_util::AnchorPoint(_tabSwitcherButton.frame, direction);
+  return [_tabSwitcherButton.superview convertPoint:anchorPoint
+                                             toView:_tabSwitcherButton.window];
+}
+
+#pragma mark -
 #pragma mark Tab Drag and Drop methods
 
 - (void)beginDrag:(UILongPressGestureRecognizer*)gesture {
diff --git a/ios/chrome/browser/ui/webui/net_export/net_export_ui.cc b/ios/chrome/browser/ui/webui/net_export/net_export_ui.cc
index c6c3966..48314887 100644
--- a/ios/chrome/browser/ui/webui/net_export/net_export_ui.cc
+++ b/ios/chrome/browser/ui/webui/net_export/net_export_ui.cc
@@ -46,8 +46,7 @@
 
 // This class receives javascript messages from the renderer.
 // Note that the WebUI infrastructure runs on the UI thread, therefore all of
-// this class's public methods are expected to run on the UI thread. All static
-// functions except SendEmail run on FILE_USER_BLOCKING thread.
+// this class's member methods are expected to run on the UI thread.
 class NetExportMessageHandler
     : public web::WebUIIOSMessageHandler,
       public base::SupportsWeakPtr<NetExportMessageHandler>,
diff --git a/ios/chrome/browser/web/page_placeholder_tab_helper.h b/ios/chrome/browser/web/page_placeholder_tab_helper.h
index 860783a7..25108b17 100644
--- a/ios/chrome/browser/web/page_placeholder_tab_helper.h
+++ b/ios/chrome/browser/web/page_placeholder_tab_helper.h
@@ -29,6 +29,12 @@
   // will be removed before navigation is finished.
   void AddPlaceholderForNextNavigation();
 
+  // true if placeholder will be displayed between DidStartNavigation and
+  // PageLoaded WebStateObserver callbacks.
+  bool will_add_placeholder_for_next_navigation() {
+    return add_placeholder_for_next_navigation_;
+  }
+
  private:
   PagePlaceholderTabHelper(web::WebState* web_state,
                            id<PagePlaceholderTabHelperDelegate> delegate);
@@ -49,8 +55,6 @@
   // true if placeholder is currently being displayed.
   bool displaying_placeholder_ = false;
 
-  // true if placeholder should be displayed between DidStartNavigation and
-  // PageLoaded WebStateObserver callbacks.
   bool add_placeholder_for_next_navigation_ = false;
 
   base::WeakPtrFactory<PagePlaceholderTabHelper> weak_factory_;
diff --git a/ios/chrome/browser/web/page_placeholder_tab_helper_unittest.mm b/ios/chrome/browser/web/page_placeholder_tab_helper_unittest.mm
index 29c0da2..f6a0533 100644
--- a/ios/chrome/browser/web/page_placeholder_tab_helper_unittest.mm
+++ b/ios/chrome/browser/web/page_placeholder_tab_helper_unittest.mm
@@ -76,30 +76,39 @@
 // requested.
 TEST_F(PagePlaceholderTabHelperTest, NotShown) {
   ASSERT_FALSE(delegate_.displayingPlaceholder);
+  ASSERT_FALSE(tab_helper()->will_add_placeholder_for_next_navigation());
   web_state_->OnNavigationStarted(nullptr);
   EXPECT_FALSE(delegate_.displayingPlaceholder);
+  EXPECT_FALSE(tab_helper()->will_add_placeholder_for_next_navigation());
 }
 
 // Tests that placeholder is shown between DidStartNavigation/PageLoaded
 // WebStateObserver callbacks.
 TEST_F(PagePlaceholderTabHelperTest, Shown) {
+  ASSERT_FALSE(tab_helper()->will_add_placeholder_for_next_navigation());
   tab_helper()->AddPlaceholderForNextNavigation();
   ASSERT_FALSE(delegate_.displayingPlaceholder);
 
+  EXPECT_TRUE(tab_helper()->will_add_placeholder_for_next_navigation());
   web_state_->OnNavigationStarted(nullptr);
   EXPECT_TRUE(delegate_.displayingPlaceholder);
+  EXPECT_FALSE(tab_helper()->will_add_placeholder_for_next_navigation());
 
   web_state_->OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS);
   EXPECT_FALSE(delegate_.displayingPlaceholder);
+  EXPECT_FALSE(tab_helper()->will_add_placeholder_for_next_navigation());
 }
 
 // Tests that destructing WebState removes the placeholder.
 TEST_F(PagePlaceholderTabHelperTest, DestructWebStateWhenShowingPlaceholder) {
+  ASSERT_FALSE(tab_helper()->will_add_placeholder_for_next_navigation());
   tab_helper()->AddPlaceholderForNextNavigation();
   ASSERT_FALSE(delegate_.displayingPlaceholder);
 
+  EXPECT_TRUE(tab_helper()->will_add_placeholder_for_next_navigation());
   web_state_->OnNavigationStarted(nullptr);
   EXPECT_TRUE(delegate_.displayingPlaceholder);
+  EXPECT_FALSE(tab_helper()->will_add_placeholder_for_next_navigation());
 
   web_state_.reset();
   EXPECT_FALSE(delegate_.displayingPlaceholder);
diff --git a/ios/web/web_state/ui/crw_web_controller.h b/ios/web/web_state/ui/crw_web_controller.h
index 2f55229a..2493e20 100644
--- a/ios/web/web_state/ui/crw_web_controller.h
+++ b/ios/web/web_state/ui/crw_web_controller.h
@@ -83,10 +83,6 @@
 // is not supported.
 @property(nonatomic, readonly) UIView* viewForPrinting;
 
-// Content view was reset due to low memory.  Use placeholder overlay view on
-// next creation.
-@property(nonatomic, readwrite, assign) BOOL usePlaceholderOverlay;
-
 // Returns the current page loading phase.
 @property(nonatomic, readonly) web::LoadPhase loadPhase;
 
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 053d100..1750621a 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -320,9 +320,6 @@
   base::scoped_nsobject<NSMutableArray> _webViewToolbars;
   // Flag to say if browsing is enabled.
   BOOL _webUsageEnabled;
-  // Content view was reset due to low memory. Use the placeholder overlay on
-  // next creation.
-  BOOL _usePlaceholderOverlay;
   // The next time the view is requested, reload the page (using the placeholder
   // overlay until it's loaded).
   BOOL _requireReloadOnDisplay;
@@ -939,7 +936,6 @@
 @implementation CRWWebController
 
 @synthesize webUsageEnabled = _webUsageEnabled;
-@synthesize usePlaceholderOverlay = _usePlaceholderOverlay;
 @synthesize loadPhase = _loadPhase;
 @synthesize shouldSuppressDialogs = _shouldSuppressDialogs;
 @synthesize webProcessCrashed = _webProcessCrashed;
@@ -1979,12 +1975,8 @@
 
     // Display overlay view until current url has finished loading or delay and
     // then transition away.
-    if ((_overlayPreviewMode || _usePlaceholderOverlay) && !isChromeScheme)
+    if (_overlayPreviewMode && !isChromeScheme)
       [self addPlaceholderOverlay];
-
-    // Don't reset the overlay flag if in preview mode.
-    if (!_overlayPreviewMode)
-      _usePlaceholderOverlay = NO;
   } else if (_requireReloadOnDisplay && _webView) {
     _requireReloadOnDisplay = NO;
     [self addPlaceholderOverlay];
diff --git a/media/mojo/interfaces/video_frame_struct_traits.cc b/media/mojo/interfaces/video_frame_struct_traits.cc
index c68fed42..bd73cde 100644
--- a/media/mojo/interfaces/video_frame_struct_traits.cc
+++ b/media/mojo/interfaces/video_frame_struct_traits.cc
@@ -56,26 +56,10 @@
 }  // namespace
 
 // static
-void* StructTraits<media::mojom::VideoFrameDataView,
-                   scoped_refptr<media::VideoFrame>>::
-    SetUpContext(const scoped_refptr<media::VideoFrame>& input) {
-  return new media::mojom::VideoFrameDataPtr(MakeVideoFrameData(input));
-}
-
-// static
-void StructTraits<media::mojom::VideoFrameDataView,
-                  scoped_refptr<media::VideoFrame>>::
-    TearDownContext(const scoped_refptr<media::VideoFrame>& input,
-                    void* context) {
-  delete static_cast<media::mojom::VideoFrameDataPtr*>(context);
-}
-
-// static
-media::mojom::VideoFrameDataPtr&
-StructTraits<media::mojom::VideoFrameDataView,
-             scoped_refptr<media::VideoFrame>>::
-    data(const scoped_refptr<media::VideoFrame>& input, void* context) {
-  return *static_cast<media::mojom::VideoFrameDataPtr*>(context);
+media::mojom::VideoFrameDataPtr StructTraits<media::mojom::VideoFrameDataView,
+                                             scoped_refptr<media::VideoFrame>>::
+    data(const scoped_refptr<media::VideoFrame>& input) {
+  return media::mojom::VideoFrameDataPtr(MakeVideoFrameData(input));
 }
 
 // static
diff --git a/media/mojo/interfaces/video_frame_struct_traits.h b/media/mojo/interfaces/video_frame_struct_traits.h
index 3324b09..712a3270 100644
--- a/media/mojo/interfaces/video_frame_struct_traits.h
+++ b/media/mojo/interfaces/video_frame_struct_traits.h
@@ -19,11 +19,6 @@
 template <>
 struct StructTraits<media::mojom::VideoFrameDataView,
                     scoped_refptr<media::VideoFrame>> {
-  static void* SetUpContext(const scoped_refptr<media::VideoFrame>& input);
-
-  static void TearDownContext(const scoped_refptr<media::VideoFrame>&,
-                              void* context);
-
   static bool IsNull(const scoped_refptr<media::VideoFrame>& input) {
     return !input;
   }
@@ -57,9 +52,8 @@
     return input->timestamp();
   }
 
-  static media::mojom::VideoFrameDataPtr& data(
-      const scoped_refptr<media::VideoFrame>& input,
-      void* context);
+  static media::mojom::VideoFrameDataPtr data(
+      const scoped_refptr<media::VideoFrame>& input);
 
   static bool Read(media::mojom::VideoFrameDataView input,
                    scoped_refptr<media::VideoFrame>* output);
diff --git a/mojo/edk/system/ports/node.cc b/mojo/edk/system/ports/node.cc
index 658c9d0e..5931318 100644
--- a/mojo/edk/system/ports/node.cc
+++ b/mojo/edk/system/ports/node.cc
@@ -11,10 +11,13 @@
 
 #include "base/atomicops.h"
 #include "base/containers/stack_container.h"
+#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
 #include "base/optional.h"
 #include "base/synchronization/lock.h"
+#include "base/threading/thread_local.h"
+#include "build/build_config.h"
 #include "mojo/edk/system/ports/event.h"
 #include "mojo/edk/system/ports/node_delegate.h"
 #include "mojo/edk/system/ports/port_locker.h"
@@ -31,6 +34,44 @@
 
 namespace {
 
+constexpr size_t kRandomNameCacheSize = 256;
+
+// Random port name generator which maintains a cache of random bytes to draw
+// from. This amortizes the cost of random name generation on platforms where
+// RandBytes may have significant per-call overhead.
+//
+// Note that the use of this cache means one has to be careful about fork()ing
+// a process once any port names have been generated, as that behavior can lead
+// to collisions between independently generated names in different processes.
+class RandomNameGenerator {
+ public:
+  RandomNameGenerator() = default;
+  ~RandomNameGenerator() = default;
+
+  PortName GenerateRandomPortName() {
+    base::AutoLock lock(lock_);
+    if (cache_index_ == kRandomNameCacheSize) {
+#if defined(OS_NACL)
+      base::RandBytes(cache_, sizeof(PortName) * kRandomNameCacheSize);
+#else
+      crypto::RandBytes(cache_, sizeof(PortName) * kRandomNameCacheSize);
+#endif
+      cache_index_ = 0;
+    }
+    return cache_[cache_index_++];
+  }
+
+ private:
+  base::Lock lock_;
+  PortName cache_[kRandomNameCacheSize];
+  size_t cache_index_ = kRandomNameCacheSize;
+
+  DISALLOW_COPY_AND_ASSIGN(RandomNameGenerator);
+};
+
+base::LazyInstance<RandomNameGenerator>::Leaky g_name_generator =
+    LAZY_INSTANCE_INITIALIZER;
+
 int DebugError(const char* message, int error_code) {
   NOTREACHED() << "Oops: " << message;
   return error_code;
@@ -52,11 +93,7 @@
 }
 
 void GenerateRandomPortName(PortName* name) {
-#if defined(OS_NACL)
-  base::RandBytes(name, sizeof(PortName));
-#else
-  crypto::RandBytes(name, sizeof(PortName));
-#endif
+  *name = g_name_generator.Get().GenerateRandomPortName();
 }
 
 }  // namespace
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.cc b/mojo/public/cpp/bindings/lib/multiplex_router.cc
index c98b7d4..54fae5c8 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.cc
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.cc
@@ -384,6 +384,11 @@
   DCHECK(endpoints_.empty());
 }
 
+void MultiplexRouter::AddIncomingMessageFilter(
+    std::unique_ptr<MessageReceiver> filter) {
+  filters_.Append(std::move(filter));
+}
+
 void MultiplexRouter::SetMasterInterfaceName(const char* name) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   header_validator_->SetDescription(std::string(name) +
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.h b/mojo/public/cpp/bindings/lib/multiplex_router.h
index be6b2cc..ecbf1a1 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.h
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.h
@@ -78,6 +78,10 @@
                   bool set_interface_id_namespace_bit,
                   scoped_refptr<base::SequencedTaskRunner> runner);
 
+  // Adds a MessageReceiver which can filter a message after validation but
+  // before dispatch.
+  void AddIncomingMessageFilter(std::unique_ptr<MessageReceiver> filter);
+
   // Sets the master interface name for this router. Only used when reporting
   // message header or control message validation errors.
   // |name| must be a string literal.
diff --git a/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.cc b/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.cc
index 6b770b1..2586d8d0 100644
--- a/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.cc
+++ b/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.cc
@@ -5,39 +5,11 @@
 #include "mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.h"
 
 namespace mojo {
-namespace {
-
-struct Context {
-  int32_t value;
-};
-
-}  // namespace
-
-// static
-void* StructTraits<test::NestedStructWithTraitsDataView,
-                   test::NestedStructWithTraitsImpl>::
-    SetUpContext(const test::NestedStructWithTraitsImpl& input) {
-  Context* context = new Context;
-  context->value = input.value;
-  return context;
-}
-
-// static
-void StructTraits<test::NestedStructWithTraitsDataView,
-                  test::NestedStructWithTraitsImpl>::
-    TearDownContext(const test::NestedStructWithTraitsImpl& input,
-                    void* context) {
-  Context* context_obj = static_cast<Context*>(context);
-  CHECK_EQ(context_obj->value, input.value);
-  delete context_obj;
-}
 
 // static
 int32_t StructTraits<test::NestedStructWithTraitsDataView,
                      test::NestedStructWithTraitsImpl>::
-    value(const test::NestedStructWithTraitsImpl& input, void* context) {
-  Context* context_obj = static_cast<Context*>(context);
-  CHECK_EQ(context_obj->value, input.value);
+    value(const test::NestedStructWithTraitsImpl& input) {
   return input.value;
 }
 
diff --git a/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.h b/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.h
index 3590bd55..59a5d1a2 100644
--- a/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.h
+++ b/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.h
@@ -20,12 +20,7 @@
 template <>
 struct StructTraits<test::NestedStructWithTraitsDataView,
                     test::NestedStructWithTraitsImpl> {
-  static void* SetUpContext(const test::NestedStructWithTraitsImpl& input);
-  static void TearDownContext(const test::NestedStructWithTraitsImpl& input,
-                              void* context);
-
-  static int32_t value(const test::NestedStructWithTraitsImpl& input,
-                       void* context);
+  static int32_t value(const test::NestedStructWithTraitsImpl& input);
 
   static bool Read(test::NestedStructWithTraitsDataView data,
                    test::NestedStructWithTraitsImpl* output);
diff --git a/mojo/public/tools/fuzzers/fuzz.mojom b/mojo/public/tools/fuzzers/fuzz.mojom
index f4a8275..53c8ac6a 100644
--- a/mojo/public/tools/fuzzers/fuzz.mojom
+++ b/mojo/public/tools/fuzzers/fuzz.mojom
@@ -59,6 +59,10 @@
   array<map<FuzzEnum, map<int8, array<FuzzStruct?>?>>>? fuzz_complex;
 };
 
+interface FuzzDummyInterface {
+  Ping();
+};
+
 interface FuzzInterface {
   FuzzBasic();
   FuzzBasicResp() => ();
@@ -69,4 +73,6 @@
   FuzzArgsResp(FuzzStruct a, FuzzStruct? b) => ();
   [Sync]
   FuzzArgsSyncResp(FuzzStruct a, FuzzStruct? b) => ();
+
+  FuzzAssociated(associated FuzzDummyInterface& request);
 };
diff --git a/mojo/public/tools/fuzzers/fuzz_impl.cc b/mojo/public/tools/fuzzers/fuzz_impl.cc
index 313327c5..d3b1012 100644
--- a/mojo/public/tools/fuzzers/fuzz_impl.cc
+++ b/mojo/public/tools/fuzzers/fuzz_impl.cc
@@ -36,3 +36,10 @@
                                 FuzzArgsSyncRespCallback callback) {
   std::move(callback).Run();
 }
+
+void FuzzImpl::FuzzAssociated(
+    fuzz::mojom::FuzzDummyInterfaceAssociatedRequest req) {
+  associated_bindings_.AddBinding(this, std::move(req));
+}
+
+void FuzzImpl::Ping() {}
diff --git a/mojo/public/tools/fuzzers/fuzz_impl.h b/mojo/public/tools/fuzzers/fuzz_impl.h
index f2d4774..d63a707 100644
--- a/mojo/public/tools/fuzzers/fuzz_impl.h
+++ b/mojo/public/tools/fuzzers/fuzz_impl.h
@@ -5,14 +5,17 @@
 #ifndef MOJO_PUBLIC_TOOLS_FUZZERS_FUZZ_IMPL_H_
 #define MOJO_PUBLIC_TOOLS_FUZZERS_FUZZ_IMPL_H_
 
+#include "mojo/public/cpp/bindings/associated_binding_set.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/tools/fuzzers/fuzz.mojom.h"
 
-class FuzzImpl : public fuzz::mojom::FuzzInterface {
+class FuzzImpl : public fuzz::mojom::FuzzInterface,
+                 public fuzz::mojom::FuzzDummyInterface {
  public:
   explicit FuzzImpl(fuzz::mojom::FuzzInterfaceRequest request);
   ~FuzzImpl() override;
 
+  // fuzz::mojom::FuzzInterface:
   void FuzzBasic() override;
   void FuzzBasicResp(FuzzBasicRespCallback callback) override;
   void FuzzBasicSyncResp(FuzzBasicSyncRespCallback callback) override;
@@ -26,8 +29,17 @@
                         fuzz::mojom::FuzzStructPtr b,
                         FuzzArgsSyncRespCallback callback) override;
 
+  void FuzzAssociated(
+      fuzz::mojom::FuzzDummyInterfaceAssociatedRequest req) override;
+
+  // fuzz::mojom::FuzzDummyInterface:
+  void Ping() override;
+
   /* Expose the binding to the fuzz harness. */
   mojo::Binding<FuzzInterface> binding_;
+
+ private:
+  mojo::AssociatedBindingSet<FuzzDummyInterface> associated_bindings_;
 };
 
 #endif  // MOJO_PUBLIC_TOOLS_FUZZERS_FUZZ_IMPL_H_
diff --git a/mojo/public/tools/fuzzers/message_corpus/message_0.mojomsg b/mojo/public/tools/fuzzers/message_corpus/message_0.mojomsg
index 7b5b302..945b63c4 100644
--- a/mojo/public/tools/fuzzers/message_corpus/message_0.mojomsg
+++ b/mojo/public/tools/fuzzers/message_corpus/message_0.mojomsg
Binary files differ
diff --git a/mojo/public/tools/fuzzers/message_corpus/message_1.mojomsg b/mojo/public/tools/fuzzers/message_corpus/message_1.mojomsg
index 945b63c4..41715c8 100644
--- a/mojo/public/tools/fuzzers/message_corpus/message_1.mojomsg
+++ b/mojo/public/tools/fuzzers/message_corpus/message_1.mojomsg
Binary files differ
diff --git a/mojo/public/tools/fuzzers/message_corpus/message_10.mojomsg b/mojo/public/tools/fuzzers/message_corpus/message_10.mojomsg
new file mode 100644
index 0000000..c19d5c75
--- /dev/null
+++ b/mojo/public/tools/fuzzers/message_corpus/message_10.mojomsg
Binary files differ
diff --git a/mojo/public/tools/fuzzers/message_corpus/message_11.mojomsg b/mojo/public/tools/fuzzers/message_corpus/message_11.mojomsg
new file mode 100644
index 0000000..61ba989
--- /dev/null
+++ b/mojo/public/tools/fuzzers/message_corpus/message_11.mojomsg
Binary files differ
diff --git a/mojo/public/tools/fuzzers/message_corpus/message_2.mojomsg b/mojo/public/tools/fuzzers/message_corpus/message_2.mojomsg
index 41715c8..7b5b302 100644
--- a/mojo/public/tools/fuzzers/message_corpus/message_2.mojomsg
+++ b/mojo/public/tools/fuzzers/message_corpus/message_2.mojomsg
Binary files differ
diff --git a/mojo/public/tools/fuzzers/message_corpus/message_9.mojomsg b/mojo/public/tools/fuzzers/message_corpus/message_9.mojomsg
new file mode 100644
index 0000000..61049cf1
--- /dev/null
+++ b/mojo/public/tools/fuzzers/message_corpus/message_9.mojomsg
Binary files differ
diff --git a/mojo/public/tools/fuzzers/mojo_fuzzer_message_dump.cc b/mojo/public/tools/fuzzers/mojo_fuzzer_message_dump.cc
index 9f2c5077c1..1cc412e 100644
--- a/mojo/public/tools/fuzzers/mojo_fuzzer_message_dump.cc
+++ b/mojo/public/tools/fuzzers/mojo_fuzzer_message_dump.cc
@@ -223,10 +223,11 @@
  * supplied directory. */
 void DumpMessages(std::string output_directory) {
   fuzz::mojom::FuzzInterfacePtr fuzz;
+  fuzz::mojom::FuzzDummyInterfaceAssociatedPtr dummy;
 
   /* Create the impl and add a MessageDumper to the filter chain. */
   env->impl = base::MakeUnique<FuzzImpl>(MakeRequest(&fuzz));
-  env->impl->binding_.AddFilter(
+  env->impl->binding_.RouterForTesting()->AddIncomingMessageFilter(
       base::MakeUnique<MessageDumper>(output_directory));
 
   /* Call methods in various ways to generate interesting messages. */
@@ -244,6 +245,8 @@
                          GetPopulatedFuzzStruct(), base::Bind(FuzzCallback));
   fuzz->FuzzArgsSyncResp(fuzz::mojom::FuzzStruct::New(),
                          GetPopulatedFuzzStruct(), base::Bind(FuzzCallback));
+  fuzz->FuzzAssociated(MakeRequest(&dummy));
+  dummy->Ping();
 }
 
 int main(int argc, char** argv) {
diff --git a/mojo/public/tools/fuzzers/mojo_parse_message_fuzzer.cc b/mojo/public/tools/fuzzers/mojo_parse_message_fuzzer.cc
index 0c4fb90..db84644 100644
--- a/mojo/public/tools/fuzzers/mojo_parse_message_fuzzer.cc
+++ b/mojo/public/tools/fuzzers/mojo_parse_message_fuzzer.cc
@@ -48,10 +48,9 @@
   mojo::internal::ScopedSuppressValidationErrorLoggingForTests log_suppression;
 };
 
-Environment* env = new Environment();
-
 // Entry point for LibFuzzer.
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  static Environment* env = new Environment();
   /* Pass the data along to run on a MessageLoop, and wait for it to finish. */
   base::RunLoop run;
   env->message_loop.task_runner()->PostTask(
diff --git a/net/base/filename_util_internal.cc b/net/base/filename_util_internal.cc
index fa81d18..6cb28e7 100644
--- a/net/base/filename_util_internal.cc
+++ b/net/base/filename_util_internal.cc
@@ -77,7 +77,7 @@
 
 void SanitizeGeneratedFileName(base::FilePath::StringType* filename,
                                bool replace_trailing) {
-  const base::FilePath::CharType kReplace[] = FILE_PATH_LITERAL("-");
+  const base::FilePath::CharType kReplace[] = FILE_PATH_LITERAL("_");
   if (filename->empty())
     return;
   if (replace_trailing) {
@@ -277,7 +277,7 @@
                      : base::FilePath::StringType(kFinalFallbackName);
     overwrite_extension = false;
   }
-  replace_illegal_characters_function(&result_str, '-');
+  replace_illegal_characters_function(&result_str, '_');
   base::FilePath result(result_str);
   // extension should not appended to filename derived from
   // content-disposition, if it does not have one.
diff --git a/net/base/filename_util_unittest.cc b/net/base/filename_util_unittest.cc
index 22cf1e01..b209666f 100644
--- a/net/base/filename_util_unittest.cc
+++ b/net/base/filename_util_unittest.cc
@@ -518,7 +518,7 @@
      "",
      "",
      L"",
-     L"-test.html"},
+     L"_test.html"},
     {__LINE__,
      "http://www.google.com/",
      "attachment; filename=\"..\\test.html\"",
@@ -534,7 +534,7 @@
      "",
      "",
      L"",
-     L"-test.html"},
+     L"_test.html"},
     {// Filename disappears after leading and trailing periods are removed.
      __LINE__,
      "http://www.google.com/",
@@ -802,26 +802,26 @@
     {// Disposition has relative paths, remove directory separators
      __LINE__, "http://www.evil.com/my_download.txt",
      "filename=../../../../././../a_file_name.txt", "", "", "text/plain",
-     L"download", L"-..-..-..-.-.-..-a_file_name.txt"},
+     L"download", L"_.._.._.._._._.._a_file_name.txt"},
     {// Disposition has parent directories, remove directory separators
      __LINE__, "http://www.evil.com/my_download.txt",
      "filename=dir1/dir2/a_file_name.txt", "", "", "text/plain", L"download",
-     L"dir1-dir2-a_file_name.txt"},
+     L"dir1_dir2_a_file_name.txt"},
     {// Disposition has relative paths, remove directory separators
      __LINE__, "http://www.evil.com/my_download.txt",
      "filename=..\\..\\..\\..\\.\\.\\..\\a_file_name.txt", "", "", "text/plain",
-     L"download", L"-..-..-..-.-.-..-a_file_name.txt"},
+     L"download", L"_.._.._.._._._.._a_file_name.txt"},
     {// Disposition has parent directories, remove directory separators
      __LINE__, "http://www.evil.com/my_download.txt",
      "filename=dir1\\dir2\\a_file_name.txt", "", "", "text/plain", L"download",
-     L"dir1-dir2-a_file_name.txt"},
+     L"dir1_dir2_a_file_name.txt"},
     {// No useful information in disposition or URL, use default
      __LINE__, "http://www.truncated.com/path/", "", "", "", "text/plain",
      L"download", L"download" TXT_EXT},
     {// Filename looks like HTML?
      __LINE__, "http://www.evil.com/get/malware/here",
      "filename=\"<blink>Hello kitty</blink>\"", "", "", "text/plain",
-     L"default", L"-blink-Hello kitty--blink-"},
+     L"default", L"_blink_Hello kitty__blink_"},
     {// A normal avi should get .avi and not .avi.avi
      __LINE__, "https://blah.google.com/misc/2.avi", "", "", "",
      "video/x-msvideo", L"download", L"2.avi"},
@@ -854,17 +854,17 @@
     {__LINE__, "http://www.goodguy.com/evil.exe ", "filename=evil.exe ", "", "",
      "binary/octet-stream", L"download", L"evil.exe"},
     {__LINE__, "http://www.goodguy.com/evil.exe.", "filename=evil.exe.", "", "",
-     "binary/octet-stream", L"download", L"evil.exe-"},
+     "binary/octet-stream", L"download", L"evil.exe_"},
     {__LINE__, "http://www.goodguy.com/evil.exe.  .  .",
      "filename=evil.exe.  .  .", "", "", "binary/octet-stream", L"download",
-     L"evil.exe-------"},
+     L"evil.exe_______"},
     {__LINE__, "http://www.goodguy.com/evil.", "filename=evil.", "", "",
-     "binary/octet-stream", L"download", L"evil-"},
+     "binary/octet-stream", L"download", L"evil_"},
     {__LINE__, "http://www.goodguy.com/. . . . .", "filename=. . . . .", "", "",
      "binary/octet-stream", L"download", L"download"},
     {__LINE__, "http://www.badguy.com/attachment?name=meh.exe%C2%A0",
      "attachment; filename=\"meh.exe\xC2\xA0\"", "", "", "binary/octet-stream",
-     L"", L"meh.exe-"},
+     L"", L"meh.exe_"},
 #endif  // OS_WIN
     {__LINE__, "http://www.goodguy.com/utils.js", "filename=utils.js", "", "",
      "application/x-javascript", L"download", L"utils.js"},
@@ -881,15 +881,15 @@
     {__LINE__, "http://www.goodguy.com/program.exe", "filename=program.exe", "",
      "", "application/foo-bar", L"download", L"program.exe"},
     {__LINE__, "http://www.evil.com/../foo.txt", "filename=../foo.txt", "", "",
-     "text/plain", L"download", L"-foo.txt"},
+     "text/plain", L"download", L"_foo.txt"},
     {__LINE__, "http://www.evil.com/..\\foo.txt", "filename=..\\foo.txt", "",
-     "", "text/plain", L"download", L"-foo.txt"},
+     "", "text/plain", L"download", L"_foo.txt"},
     {__LINE__, "http://www.evil.com/.hidden", "filename=.hidden", "", "",
      "text/plain", L"download", L"hidden"},
     {__LINE__, "http://www.evil.com/trailing.", "filename=trailing.", "", "",
      "dance/party", L"download",
 #if defined(OS_WIN)
-     L"trailing-"
+     L"trailing_"
 #else
      L"trailing"
 #endif
@@ -897,7 +897,7 @@
     {__LINE__, "http://www.evil.com/trailing.", "filename=trailing.", "", "",
      "text/plain", L"download",
 #if defined(OS_WIN)
-     L"trailing-"
+     L"trailing_"
 #else
      L"trailing"
 #endif
@@ -1031,7 +1031,7 @@
      __LINE__, "http://www.example.com/image.aspx?id=blargh", "", "", "",
      "image/jpeg", L"download", L"image" JPEG_EXT},
     {__LINE__, "http://www.example.com/image.aspx?id=blargh", "", "", " .foo",
-     "", L"download", L"-.foo"},
+     "", L"download", L"_.foo"},
 
     // Note that the next 4 tests will not fail on all platforms on regression.
     // They only fail if application/[x-]gzip has a default extension, which
diff --git a/net/cert/test_root_certs.h b/net/cert/test_root_certs.h
index 8a87704e..2a8276ca 100644
--- a/net/cert/test_root_certs.h
+++ b/net/cert/test_root_certs.h
@@ -14,6 +14,7 @@
 #if defined(USE_NSS_CERTS)
 #include <cert.h>
 #include <vector>
+#include "net/cert/scoped_nss_types.h"
 #elif defined(OS_WIN)
 #include <windows.h>
 #include "crypto/wincrypt_shim.h"
@@ -100,15 +101,15 @@
    public:
     // Creates a new TrustEntry by incrementing the reference to |certificate|
     // and copying |trust|.
-    TrustEntry(CERTCertificate* certificate, const CERTCertTrust& trust);
+    TrustEntry(ScopedCERTCertificate certificate, const CERTCertTrust& trust);
     ~TrustEntry();
 
-    CERTCertificate* certificate() const { return certificate_; }
+    CERTCertificate* certificate() const { return certificate_.get(); }
     const CERTCertTrust& trust() const { return trust_; }
 
    private:
     // The temporary root certificate.
-    CERTCertificate* certificate_;
+    ScopedCERTCertificate certificate_;
 
     // The original trust settings, before |certificate_| was manipulated to
     // be a temporarily trusted root.
diff --git a/net/cert/test_root_certs_nss.cc b/net/cert/test_root_certs_nss.cc
index f6c489b..8df9a31 100644
--- a/net/cert/test_root_certs_nss.cc
+++ b/net/cert/test_root_certs_nss.cc
@@ -11,26 +11,25 @@
 #include "base/memory/ptr_util.h"
 #include "crypto/nss_util.h"
 #include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util_nss.h"
 
 namespace net {
 
-
-TestRootCerts::TrustEntry::TrustEntry(CERTCertificate* certificate,
+TestRootCerts::TrustEntry::TrustEntry(ScopedCERTCertificate certificate,
                                       const CERTCertTrust& trust)
-    : certificate_(CERT_DupCertificate(certificate)),
-      trust_(trust) {
-}
+    : certificate_(std::move(certificate)), trust_(trust) {}
 
-TestRootCerts::TrustEntry::~TrustEntry() {
-  CERT_DestroyCertificate(certificate_);
-}
+TestRootCerts::TrustEntry::~TrustEntry() = default;
 
 bool TestRootCerts::Add(X509Certificate* certificate) {
-  CERTCertificate* cert_handle = certificate->os_cert_handle();
+  ScopedCERTCertificate cert_handle =
+      x509_util::CreateCERTCertificateFromX509Certificate(certificate);
+  if (!cert_handle)
+    return false;
   // Preserve the original trust bits so that they can be restored when
   // the certificate is removed.
   CERTCertTrust original_trust;
-  SECStatus rv = CERT_GetCertTrust(cert_handle, &original_trust);
+  SECStatus rv = CERT_GetCertTrust(cert_handle.get(), &original_trust);
   if (rv != SECSuccess) {
     // CERT_GetCertTrust will fail if the certificate does not have any
     // particular trust settings associated with it, and attempts to use
@@ -49,14 +48,15 @@
     return false;
   }
 
-  rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert_handle, &new_trust);
+  rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert_handle.get(),
+                            &new_trust);
   if (rv != SECSuccess) {
     LOG(ERROR) << "Cannot change certificate trust.";
     return false;
   }
 
   trust_cache_.push_back(
-      base::MakeUnique<TrustEntry>(cert_handle, original_trust));
+      base::MakeUnique<TrustEntry>(std::move(cert_handle), original_trust));
   return true;
 }
 
@@ -85,7 +85,7 @@
 
 bool TestRootCerts::Contains(CERTCertificate* cert) const {
   for (const auto& item : trust_cache_)
-    if (X509Certificate::IsSameOSCert(cert, item->certificate()))
+    if (x509_util::IsSameCertificate(cert, item->certificate()))
       return true;
 
   return false;
diff --git a/net/cert/test_root_certs_unittest.cc b/net/cert/test_root_certs_unittest.cc
index bc7e09f0..92e7d0f 100644
--- a/net/cert/test_root_certs_unittest.cc
+++ b/net/cert/test_root_certs_unittest.cc
@@ -18,6 +18,8 @@
 
 #if defined(USE_NSS_CERTS)
 #include <nss.h>
+
+#include "net/cert/x509_util_nss.h"
 #endif
 
 using net::test::IsOk;
@@ -135,30 +137,36 @@
   const char kRootCertificateFile2[] = "2048-rsa-root.pem";
 
   TestRootCerts* test_roots = TestRootCerts::GetInstance();
-  ASSERT_NE(static_cast<TestRootCerts*>(NULL), test_roots);
+  ASSERT_TRUE(test_roots);
 
   scoped_refptr<X509Certificate> root_cert_1 =
       ImportCertFromFile(GetTestCertsDirectory(), kRootCertificateFile);
-  ASSERT_NE(static_cast<X509Certificate*>(NULL), root_cert_1.get());
+  ASSERT_TRUE(root_cert_1);
+  ScopedCERTCertificate nss_root_cert_1 =
+      x509_util::CreateCERTCertificateFromX509Certificate(root_cert_1.get());
+  ASSERT_TRUE(nss_root_cert_1);
 
   scoped_refptr<X509Certificate> root_cert_2 =
       ImportCertFromFile(GetTestCertsDirectory(), kRootCertificateFile2);
-  ASSERT_NE(static_cast<X509Certificate*>(NULL), root_cert_2.get());
+  ASSERT_TRUE(root_cert_2);
+  ScopedCERTCertificate nss_root_cert_2 =
+      x509_util::CreateCERTCertificateFromX509Certificate(root_cert_2.get());
+  ASSERT_TRUE(nss_root_cert_2);
 
-  EXPECT_FALSE(test_roots->Contains(root_cert_1->os_cert_handle()));
-  EXPECT_FALSE(test_roots->Contains(root_cert_2->os_cert_handle()));
+  EXPECT_FALSE(test_roots->Contains(nss_root_cert_1.get()));
+  EXPECT_FALSE(test_roots->Contains(nss_root_cert_2.get()));
 
   EXPECT_TRUE(test_roots->Add(root_cert_1.get()));
-  EXPECT_TRUE(test_roots->Contains(root_cert_1->os_cert_handle()));
-  EXPECT_FALSE(test_roots->Contains(root_cert_2->os_cert_handle()));
+  EXPECT_TRUE(test_roots->Contains(nss_root_cert_1.get()));
+  EXPECT_FALSE(test_roots->Contains(nss_root_cert_2.get()));
 
   EXPECT_TRUE(test_roots->Add(root_cert_2.get()));
-  EXPECT_TRUE(test_roots->Contains(root_cert_1->os_cert_handle()));
-  EXPECT_TRUE(test_roots->Contains(root_cert_2->os_cert_handle()));
+  EXPECT_TRUE(test_roots->Contains(nss_root_cert_1.get()));
+  EXPECT_TRUE(test_roots->Contains(nss_root_cert_2.get()));
 
   test_roots->Clear();
-  EXPECT_FALSE(test_roots->Contains(root_cert_1->os_cert_handle()));
-  EXPECT_FALSE(test_roots->Contains(root_cert_2->os_cert_handle()));
+  EXPECT_FALSE(test_roots->Contains(nss_root_cert_1.get()));
+  EXPECT_FALSE(test_roots->Contains(nss_root_cert_2.get()));
 }
 #endif
 
diff --git a/net/cert/x509_certificate_nss.cc b/net/cert/x509_certificate_nss.cc
index 69f2de0..0ae96ca 100644
--- a/net/cert/x509_certificate_nss.cc
+++ b/net/cert/x509_certificate_nss.cc
@@ -294,11 +294,7 @@
 // static
 bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
                                    X509Certificate::OSCertHandle b) {
-  DCHECK(a && b);
-  if (a == b)
-    return true;
-  return a->derCert.len == b->derCert.len &&
-      memcmp(a->derCert.data, b->derCert.data, a->derCert.len) == 0;
+  return x509_util::IsSameCertificate(a, b);
 }
 
 // static
diff --git a/net/cert/x509_util_nss.cc b/net/cert/x509_util_nss.cc
index 38306ab8..e51f7a9 100644
--- a/net/cert/x509_util_nss.cc
+++ b/net/cert/x509_util_nss.cc
@@ -13,11 +13,13 @@
 #include <secder.h>
 #include <secmod.h>
 #include <secport.h>
+#include <string.h>
 
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
 #include "crypto/nss_util.h"
 #include "crypto/scoped_nss_types.h"
+#include "third_party/boringssl/src/include/openssl/pool.h"
 
 namespace net {
 
@@ -130,6 +132,14 @@
 
 }  // namespace
 
+bool IsSameCertificate(CERTCertificate* a, CERTCertificate* b) {
+  DCHECK(a && b);
+  if (a == b)
+    return true;
+  return a->derCert.len == b->derCert.len &&
+         memcmp(a->derCert.data, b->derCert.data, a->derCert.len) == 0;
+}
+
 ScopedCERTCertificate CreateCERTCertificateFromBytes(const uint8_t* data,
                                                      size_t length) {
   crypto::EnsureNSSInit();
@@ -148,6 +158,21 @@
       PR_FALSE /* is_perm */, PR_TRUE /* copyDER */));
 }
 
+ScopedCERTCertificate CreateCERTCertificateFromX509Certificate(
+    const X509Certificate* cert) {
+#if BUILDFLAG(USE_BYTE_CERTS)
+  return CreateCERTCertificateFromBytes(
+      CRYPTO_BUFFER_data(cert->os_cert_handle()),
+      CRYPTO_BUFFER_len(cert->os_cert_handle()));
+#else
+  return DupCERTCertificate(cert->os_cert_handle());
+#endif
+}
+
+ScopedCERTCertificate DupCERTCertificate(CERTCertificate* cert) {
+  return ScopedCERTCertificate(CERT_DupCertificate(cert));
+}
+
 void GetRFC822SubjectAltNames(CERTCertificate* cert_handle,
                               std::vector<std::string>* names) {
   crypto::ScopedSECItem alt_name(SECITEM_AllocItem(NULL, NULL, 0));
diff --git a/net/cert/x509_util_nss.h b/net/cert/x509_util_nss.h
index efcaa04..def75eb 100644
--- a/net/cert/x509_util_nss.h
+++ b/net/cert/x509_util_nss.h
@@ -13,6 +13,7 @@
 #include "net/base/net_export.h"
 #include "net/cert/cert_type.h"
 #include "net/cert/scoped_nss_types.h"
+#include "net/cert/x509_certificate.h"
 
 typedef struct CERTCertificateStr CERTCertificate;
 typedef struct CERTNameStr CERTName;
@@ -23,12 +24,24 @@
 
 namespace x509_util {
 
+// Returns true if two certificate handles refer to identical certificates.
+NET_EXPORT bool IsSameCertificate(CERTCertificate* a, CERTCertificate* b);
+
 // Returns a CERTCertificate handle from the DER-encoded representation. The
 // returned value may reference an already existing CERTCertificate object.
 // Returns NULL on failure.
 NET_EXPORT ScopedCERTCertificate
 CreateCERTCertificateFromBytes(const uint8_t* data, size_t length);
 
+// Returns a CERTCertificate handle from |cert|. The returned value may
+// reference an already existing CERTCertificate object.  Returns NULL on
+// failure.
+NET_EXPORT ScopedCERTCertificate
+CreateCERTCertificateFromX509Certificate(const X509Certificate* cert);
+
+// Increments the refcount of |cert| and returns a handle for that reference.
+NET_EXPORT ScopedCERTCertificate DupCERTCertificate(CERTCertificate* cert);
+
 // Stores the values of all rfc822Name subjectAltNames from |cert_handle|
 // into |names|. If no names are present, clears |names|.
 // WARNING: This method does not validate that the rfc822Name is
diff --git a/net/cert/x509_util_nss_unittest.cc b/net/cert/x509_util_nss_unittest.cc
index 7b49310a..b6ca74c6 100644
--- a/net/cert/x509_util_nss_unittest.cc
+++ b/net/cert/x509_util_nss_unittest.cc
@@ -13,6 +13,34 @@
 
 namespace net {
 
+TEST(X509UtilNSSTest, IsSameCertificate) {
+  ScopedCERTCertificate google_nss_cert(
+      x509_util::CreateCERTCertificateFromBytes(google_der,
+                                                arraysize(google_der)));
+  ASSERT_TRUE(google_nss_cert);
+
+  ScopedCERTCertificate google_nss_cert2(
+      x509_util::CreateCERTCertificateFromBytes(google_der,
+                                                arraysize(google_der)));
+  ASSERT_TRUE(google_nss_cert2);
+
+  ScopedCERTCertificate webkit_nss_cert(
+      x509_util::CreateCERTCertificateFromBytes(webkit_der,
+                                                arraysize(webkit_der)));
+  ASSERT_TRUE(webkit_nss_cert);
+
+  EXPECT_TRUE(x509_util::IsSameCertificate(google_nss_cert.get(),
+                                           google_nss_cert.get()));
+  EXPECT_TRUE(x509_util::IsSameCertificate(google_nss_cert.get(),
+                                           google_nss_cert2.get()));
+
+  EXPECT_TRUE(x509_util::IsSameCertificate(webkit_nss_cert.get(),
+                                           webkit_nss_cert.get()));
+
+  EXPECT_FALSE(x509_util::IsSameCertificate(google_nss_cert.get(),
+                                            webkit_nss_cert.get()));
+}
+
 TEST(X509UtilNSSTest, CreateCERTCertificateFromBytes) {
   ScopedCERTCertificate google_cert(x509_util::CreateCERTCertificateFromBytes(
       google_der, arraysize(google_der)));
@@ -30,6 +58,34 @@
                          garbage_data, arraysize(garbage_data)));
 }
 
+TEST(X509UtilNSSTest, CreateCERTCertificateFromX509Certificate) {
+  scoped_refptr<X509Certificate> x509_cert =
+      ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem");
+  ASSERT_TRUE(x509_cert);
+  ScopedCERTCertificate nss_cert =
+      x509_util::CreateCERTCertificateFromX509Certificate(x509_cert.get());
+  ASSERT_TRUE(nss_cert);
+  EXPECT_STREQ("CN=127.0.0.1,O=Test CA,L=Mountain View,ST=California,C=US",
+               nss_cert->subjectName);
+}
+
+TEST(X509UtilNSSTest, DupCERTCertificate) {
+  ScopedCERTCertificate cert(x509_util::CreateCERTCertificateFromBytes(
+      google_der, arraysize(google_der)));
+  ASSERT_TRUE(cert);
+
+  ScopedCERTCertificate cert2 = x509_util::DupCERTCertificate(cert.get());
+  // Both handles should hold a reference to the same CERTCertificate object.
+  ASSERT_EQ(cert.get(), cert2.get());
+
+  // Release the initial handle.
+  cert.reset();
+  // The duped handle should still be safe to access.
+  EXPECT_STREQ(
+      "CN=www.google.com,O=Google Inc,L=Mountain View,ST=California,C=US",
+      cert2->subjectName);
+}
+
 TEST(X509UtilNSSTest, GetDefaultNickname) {
   base::FilePath certs_dir = GetTestCertsDirectory();
 
diff --git a/net/quic/core/quic_connection.cc b/net/quic/core/quic_connection.cc
index e1f2cbbd..6260660 100644
--- a/net/quic/core/quic_connection.cc
+++ b/net/quic/core/quic_connection.cc
@@ -1105,14 +1105,17 @@
   ScopedPacketBundler ack_bundler(this, SEND_ACK_IF_PENDING);
   // The optimized path may be used for data only packets which fit into a
   // standard buffer and don't need padding.
-  if (id != kCryptoStreamId && !packet_generator_.HasQueuedFrames() &&
+  const bool flag_run_fast_path =
+      FLAGS_quic_reloadable_flag_quic_consuming_data_faster;
+  if (!flag_run_fast_path && id != kCryptoStreamId &&
+      !packet_generator_.HasQueuedFrames() &&
       iov.total_length > kMaxPacketSize && state != FIN_AND_PADDING) {
     // Use the fast path to send full data packets.
     return packet_generator_.ConsumeDataFastPath(
-        id, iov, offset, state != NO_FIN, std::move(ack_listener));
+        id, iov, offset, state != NO_FIN, 0, ack_listener);
   }
-  return packet_generator_.ConsumeData(id, iov, offset, state,
-                                       std::move(ack_listener));
+  return packet_generator_.ConsumeData(
+      id, iov, offset, state, std::move(ack_listener), flag_run_fast_path);
 }
 
 void QuicConnection::SendRstStream(QuicStreamId id,
diff --git a/net/quic/core/quic_flags_list.h b/net/quic/core/quic_flags_list.h
index 467e0eab..2ce2872c 100644
--- a/net/quic/core/quic_flags_list.h
+++ b/net/quic/core/quic_flags_list.h
@@ -201,3 +201,6 @@
 // Add 4 new ack decimation modes to QUIC that are entirely time based at 1/4
 // or 1/8 RTT.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_ack_decimation, false)
+
+// Enables using the ConsumeDataFastPath more often for large transfers.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_consuming_data_faster, false)
diff --git a/net/quic/core/quic_packet_generator.cc b/net/quic/core/quic_packet_generator.cc
index 7487cde..2c36419 100644
--- a/net/quic/core/quic_packet_generator.cc
+++ b/net/quic/core/quic_packet_generator.cc
@@ -55,7 +55,8 @@
     QuicIOVector iov,
     QuicStreamOffset offset,
     StreamSendingState state,
-    QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+    QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener,
+    bool flag_run_fast_path) {
   bool has_handshake = (id == kCryptoStreamId);
   bool fin = state != NO_FIN;
   QUIC_BUG_IF(has_handshake && fin)
@@ -77,9 +78,16 @@
     QUIC_BUG << "Attempt to consume empty data without FIN.";
     return QuicConsumedData(0, false);
   }
+  // We determine if we can enter the fast path before executing
+  // the slow path loop.
+  bool run_fast_path =
+      flag_run_fast_path &&
+      (!has_handshake && state != FIN_AND_PADDING && !HasQueuedFrames() &&
+       iov.total_length - total_bytes_consumed > kMaxPacketSize);
 
-  while (delegate_->ShouldGeneratePacket(
-      HAS_RETRANSMITTABLE_DATA, has_handshake ? IS_HANDSHAKE : NOT_HANDSHAKE)) {
+  while (!run_fast_path && delegate_->ShouldGeneratePacket(
+                               HAS_RETRANSMITTABLE_DATA,
+                               has_handshake ? IS_HANDSHAKE : NOT_HANDSHAKE)) {
     QuicFrame frame;
     if (!packet_creator_.ConsumeData(id, iov, total_bytes_consumed,
                                      offset + total_bytes_consumed, fin,
@@ -115,6 +123,16 @@
     }
     // TODO(ianswett): Move to having the creator flush itself when it's full.
     packet_creator_.Flush();
+
+    run_fast_path =
+        flag_run_fast_path &&
+        (!has_handshake && state != FIN_AND_PADDING && !HasQueuedFrames() &&
+         iov.total_length - total_bytes_consumed > kMaxPacketSize);
+  }
+
+  if (run_fast_path) {
+    return ConsumeDataFastPath(id, iov, offset, state != NO_FIN,
+                               total_bytes_consumed, std::move(ack_listener));
   }
 
   // Don't allow the handshake to be bundled with other retransmittable frames.
@@ -131,9 +149,10 @@
     const QuicIOVector& iov,
     QuicStreamOffset offset,
     bool fin,
-    QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) {
+    size_t total_bytes_consumed,
+    const QuicReferenceCountedPointer<QuicAckListenerInterface>& ack_listener) {
   DCHECK_NE(id, kCryptoStreamId);
-  size_t total_bytes_consumed = 0;
+
   while (total_bytes_consumed < iov.total_length &&
          delegate_->ShouldGeneratePacket(HAS_RETRANSMITTABLE_DATA,
                                          NOT_HANDSHAKE)) {
diff --git a/net/quic/core/quic_packet_generator.h b/net/quic/core/quic_packet_generator.h
index 43bffc1..637653c 100644
--- a/net/quic/core/quic_packet_generator.h
+++ b/net/quic/core/quic_packet_generator.h
@@ -101,17 +101,22 @@
       QuicIOVector iov,
       QuicStreamOffset offset,
       StreamSendingState state,
-      QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+      QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener,
+      bool flag_run_fast_path);
 
   // Sends as many data only packets as allowed by the send algorithm and the
   // available iov.
-  // This path does not support FEC, padding, or bundling pending frames.
+  // This path does not support padding, or bundling pending frames.
+  // In case we access this method from ConsumeData, total_bytes_consumed
+  // keeps track of how many bytes have already been consumed.
   QuicConsumedData ConsumeDataFastPath(
       QuicStreamId id,
       const QuicIOVector& iov,
       QuicStreamOffset offset,
       bool fin,
-      QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
+      size_t total_bytes_consumed,
+      const QuicReferenceCountedPointer<QuicAckListenerInterface>&
+          ack_listener);
 
   // Generates an MTU discovery packet of specified size.
   void GenerateMtuDiscoveryPacket(
diff --git a/net/quic/core/quic_packet_generator_test.cc b/net/quic/core/quic_packet_generator_test.cc
index d965070..5e7acc58 100644
--- a/net/quic/core/quic_packet_generator_test.cc
+++ b/net/quic/core/quic_packet_generator_test.cc
@@ -131,7 +131,7 @@
         producer_->SaveStreamData(id, iov, 0, offset, iov.total_length);
       }
     }
-    return QuicPacketGenerator::ConsumeDataFastPath(id, iov, offset, fin,
+    return QuicPacketGenerator::ConsumeDataFastPath(id, iov, offset, fin, 0,
                                                     std::move(ack_listener));
   }
 
@@ -149,8 +149,9 @@
         producer_->SaveStreamData(id, iov, 0, offset, iov.total_length);
       }
     }
-    return QuicPacketGenerator::ConsumeData(id, iov, offset, state,
-                                            std::move(ack_listener));
+    return QuicPacketGenerator::ConsumeData(
+        id, iov, offset, state, std::move(ack_listener),
+        FLAGS_quic_reloadable_flag_quic_consuming_data_faster);
   }
 
   SimpleDataProducer* producer_;
@@ -598,6 +599,113 @@
   PacketContents contents;
   contents.num_stream_frames = 1;
   CheckPacketContains(contents, 0);
+  EXPECT_FALSE(packets_.empty());
+  SerializedPacket packet = packets_.back();
+  EXPECT_TRUE(!packet.retransmittable_frames.empty());
+  EXPECT_EQ(STREAM_FRAME, packet.retransmittable_frames.front().type);
+  QuicStreamFrame* stream_frame =
+      packet.retransmittable_frames.front().stream_frame;
+  EXPECT_EQ(10000u, stream_frame->data_length + stream_frame->offset);
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeDataLarge) {
+  delegate_.SetCanWriteAnything();
+
+  // Create a 10000 byte IOVector.
+  QuicIOVector iov(CreateData(10000));
+  EXPECT_CALL(delegate_, OnSerializedPacket(_))
+      .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+  QuicConsumedData consumed =
+      generator_.ConsumeData(kHeadersStreamId, iov, 0, FIN, nullptr);
+  EXPECT_EQ(10000u, consumed.bytes_consumed);
+  EXPECT_TRUE(consumed.fin_consumed);
+  EXPECT_FALSE(generator_.HasQueuedFrames());
+  EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+  PacketContents contents;
+  contents.num_stream_frames = 1;
+  CheckPacketContains(contents, 0);
+  EXPECT_FALSE(packets_.empty());
+  SerializedPacket packet = packets_.back();
+  EXPECT_TRUE(!packet.retransmittable_frames.empty());
+  EXPECT_EQ(STREAM_FRAME, packet.retransmittable_frames.front().type);
+  QuicStreamFrame* stream_frame =
+      packet.retransmittable_frames.front().stream_frame;
+  EXPECT_EQ(10000u, stream_frame->data_length + stream_frame->offset);
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeDataLargeSendAckFalse) {
+  delegate_.SetCanNotWrite();
+
+  generator_.SetShouldSendAck(false);
+  generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+  EXPECT_TRUE(generator_.HasQueuedFrames());
+  EXPECT_TRUE(generator_.HasRetransmittableFrames());
+
+  delegate_.SetCanWriteAnything();
+
+  generator_.StartBatchOperations();
+
+  EXPECT_CALL(delegate_, GetUpdatedAckFrame())
+      .WillOnce(Return(QuicFrame(&ack_frame_)));
+
+  // Create a 10000 byte IOVector.
+  QuicIOVector iov(CreateData(10000));
+  EXPECT_CALL(delegate_, OnSerializedPacket(_))
+      .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+  QuicConsumedData consumed =
+      generator_.ConsumeData(kHeadersStreamId, iov, 0, FIN, nullptr);
+  generator_.FinishBatchOperations();
+
+  EXPECT_EQ(10000u, consumed.bytes_consumed);
+  EXPECT_TRUE(consumed.fin_consumed);
+  EXPECT_FALSE(generator_.HasQueuedFrames());
+  EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+  EXPECT_FALSE(packets_.empty());
+  SerializedPacket packet = packets_.back();
+  EXPECT_TRUE(!packet.retransmittable_frames.empty());
+  EXPECT_EQ(STREAM_FRAME, packet.retransmittable_frames.front().type);
+  QuicStreamFrame* stream_frame =
+      packet.retransmittable_frames.front().stream_frame;
+  EXPECT_EQ(10000u, stream_frame->data_length + stream_frame->offset);
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeDataLargeSendAckTrue) {
+  delegate_.SetCanNotWrite();
+  generator_.SetShouldSendAck(true);
+  delegate_.SetCanWriteAnything();
+  generator_.StartBatchOperations();
+
+  // Set up frames to write into the creator when control frames are written.
+  EXPECT_CALL(delegate_, GetUpdatedAckFrame())
+      .WillOnce(Return(QuicFrame(&ack_frame_)));
+  EXPECT_CALL(delegate_, PopulateStopWaitingFrame(_));
+  // Generator should have queued control frames, and creator should be empty.
+  EXPECT_TRUE(generator_.HasQueuedFrames());
+  EXPECT_FALSE(generator_.HasRetransmittableFrames());
+  EXPECT_FALSE(creator_->HasPendingFrames());
+
+  // Create a 10000 byte IOVector.
+  QuicIOVector iov(CreateData(10000));
+  EXPECT_CALL(delegate_, OnSerializedPacket(_))
+      .WillRepeatedly(Invoke(this, &QuicPacketGeneratorTest::SavePacket));
+  QuicConsumedData consumed =
+      generator_.ConsumeData(kHeadersStreamId, iov, 0, FIN, nullptr);
+  generator_.FinishBatchOperations();
+
+  EXPECT_EQ(10000u, consumed.bytes_consumed);
+  EXPECT_TRUE(consumed.fin_consumed);
+  EXPECT_FALSE(generator_.HasQueuedFrames());
+  EXPECT_FALSE(generator_.HasRetransmittableFrames());
+
+  EXPECT_FALSE(packets_.empty());
+  SerializedPacket packet = packets_.back();
+  EXPECT_TRUE(!packet.retransmittable_frames.empty());
+  EXPECT_EQ(STREAM_FRAME, packet.retransmittable_frames.front().type);
+  QuicStreamFrame* stream_frame =
+      packet.retransmittable_frames.front().stream_frame;
+  EXPECT_EQ(10000u, stream_frame->data_length + stream_frame->offset);
 }
 
 TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) {
diff --git a/ppapi/tests/test_file_chooser.cc b/ppapi/tests/test_file_chooser.cc
index dbacc0b6..4ce42a0 100644
--- a/ppapi/tests/test_file_chooser.cc
+++ b/ppapi/tests/test_file_chooser.cc
@@ -131,7 +131,7 @@
 
   const std::vector<pp::FileRef>& output_ref = filechooser_callback.output();
   ASSERT_EQ(1u, output_ref.size());
-  ASSERT_EQ("unsafe.txt-", output_ref.front().GetName().AsString());
+  ASSERT_EQ("unsafe.txt_", output_ref.front().GetName().AsString());
 
   ASSERT_TRUE(WriteDefaultContentsToFile(output_ref.front()));
   PASS();
diff --git a/sandbox/linux/services/resource_limits.cc b/sandbox/linux/services/resource_limits.cc
index 1ec1129..51f13c3 100644
--- a/sandbox/linux/services/resource_limits.cc
+++ b/sandbox/linux/services/resource_limits.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "sandbox/linux/services/resource_limits.h"
+#include "base/numerics/safe_math.h"
 
 #include <sys/resource.h>
 #include <sys/time.h>
@@ -13,14 +14,32 @@
 
 // static
 bool ResourceLimits::Lower(int resource, rlim_t limit) {
+  return LowerSoftAndHardLimits(resource, limit, limit);
+}
+
+// static
+bool ResourceLimits::LowerSoftAndHardLimits(int resource,
+                                            rlim_t soft_limit,
+                                            rlim_t hard_limit) {
   struct rlimit old_rlimit;
   if (getrlimit(resource, &old_rlimit))
     return false;
   // Make sure we don't raise the existing limit.
-  const struct rlimit new_rlimit = {std::min(old_rlimit.rlim_cur, limit),
-                                    std::min(old_rlimit.rlim_max, limit)};
-  int rc = setrlimit(resource, &new_rlimit);
-  return rc == 0;
+  const struct rlimit new_rlimit = {std::min(old_rlimit.rlim_cur, soft_limit),
+                                    std::min(old_rlimit.rlim_max, hard_limit)};
+  return setrlimit(resource, &new_rlimit) == 0;
+}
+
+// static
+bool ResourceLimits::AdjustCurrent(int resource, long long int change) {
+  struct rlimit old_rlimit;
+  if (getrlimit(resource, &old_rlimit))
+    return false;
+  base::CheckedNumeric<rlim_t> limit = old_rlimit.rlim_cur;
+  limit += change;
+  const struct rlimit new_rlimit = {limit.ValueOrDie(), old_rlimit.rlim_max};
+  // setrlimit will fail if limit > old_rlimit.rlim_max.
+  return setrlimit(resource, &new_rlimit) == 0;
 }
 
 }  // namespace sandbox
diff --git a/sandbox/linux/services/resource_limits.h b/sandbox/linux/services/resource_limits.h
index 3464dab..ab949dd 100644
--- a/sandbox/linux/services/resource_limits.h
+++ b/sandbox/linux/services/resource_limits.h
@@ -19,6 +19,16 @@
   // Lower the soft and hard limit of |resource| to |limit|. If the current
   // limit is lower than |limit|, keep it.
   static bool Lower(int resource, rlim_t limit) WARN_UNUSED_RESULT;
+  // Lower the soft limit of |resource| to |limit| and the hard limit to
+  // |max|. If the current limit is lower than the new values, keep it.
+  static bool LowerSoftAndHardLimits(int resource,
+                                     rlim_t soft_limit,
+                                     rlim_t hard_limit) WARN_UNUSED_RESULT;
+  // Change soft limit of |resource| to the current limit plus |change|.  Fails
+  // and returns false if the new soft limit would be greater than the hard
+  // limit.
+  static bool AdjustCurrent(int resource,
+                            long long int change) WARN_UNUSED_RESULT;
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(ResourceLimits);
diff --git a/services/identity/public/cpp/scope_set_struct_traits.h b/services/identity/public/cpp/scope_set_struct_traits.h
index 467b9065..17882b0 100644
--- a/services/identity/public/cpp/scope_set_struct_traits.h
+++ b/services/identity/public/cpp/scope_set_struct_traits.h
@@ -11,22 +11,11 @@
 
 template <>
 struct StructTraits<identity::mojom::ScopeSet::DataView, identity::ScopeSet> {
-  static void* SetUpContext(const identity::ScopeSet& scope_set) {
-    std::vector<std::string>* scopes = new std::vector<std::string>();
+  static std::vector<std::string> scopes(const identity::ScopeSet& scope_set) {
+    std::vector<std::string> entries;
     for (const auto& scope : scope_set)
-      scopes->push_back(scope);
-    return scopes;
-  }
-
-  static void TearDownContext(const identity::ScopeSet& scope_set,
-                              void* context) {
-    delete static_cast<std::vector<std::string>*>(context);
-  }
-
-  static const std::vector<std::string>& scopes(
-      const identity::ScopeSet& scope_set,
-      void* context) {
-    return *(static_cast<std::vector<std::string>*>(context));
+      entries.push_back(scope);
+    return entries;
   }
 
   static bool Read(identity::mojom::ScopeSetDataView data,
diff --git a/services/ui/gpu/gpu_main.cc b/services/ui/gpu/gpu_main.cc
index 09033e8..a329902 100644
--- a/services/ui/gpu/gpu_main.cc
+++ b/services/ui/gpu/gpu_main.cc
@@ -150,10 +150,13 @@
 void GpuMain::InitOnGpuThread(
     scoped_refptr<base::SingleThreadTaskRunner> io_runner,
     scoped_refptr<base::SingleThreadTaskRunner> compositor_runner) {
+  // TODO(kylechar): When process split happens this shouldn't be a constant.
+  constexpr bool kInProcessGpu = true;
+
   gpu_init_.reset(new gpu::GpuInit());
   gpu_init_->set_sandbox_helper(this);
   bool success = gpu_init_->InitializeAndStartSandbox(
-      *base::CommandLine::ForCurrentProcess());
+      *base::CommandLine::ForCurrentProcess(), kInProcessGpu);
   if (!success)
     return;
 
diff --git a/services/ui/service.cc b/services/ui/service.cc
index e8f02c9..7b2b52e 100644
--- a/services/ui/service.cc
+++ b/services/ui/service.cc
@@ -239,7 +239,7 @@
   // in MUS, |InitializeForUI| will load the GL libraries.
   ui::OzonePlatform::InitParams params;
   params.connector = context()->connector();
-  params.single_process = false;
+  params.single_process = true;
   ui::OzonePlatform::InitializeForUI(params);
 
   // Assume a client will change the layout to an appropriate configuration.
diff --git a/services/viz/PRESUBMIT.py b/services/viz/PRESUBMIT.py
new file mode 100644
index 0000000..babbf3f
--- /dev/null
+++ b/services/viz/PRESUBMIT.py
@@ -0,0 +1,16 @@
+# 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.
+
+"""Top-level presubmit script for services/viz."""
+
+def CheckChangeOnUpload(input_api, output_api):
+  import sys
+  original_sys_path = sys.path
+  sys.path = sys.path + [input_api.os_path.join(
+      input_api.change.RepositoryRoot(),
+      'components', 'viz')]
+
+  import presubmit_checks as ps
+  white_list=(r'^services[\\/]viz[\\/].*\.(cc|h)$',)
+  return ps.RunAllChecks(input_api, output_api, white_list)
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 4ec4597..675126e 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -208,6 +208,10 @@
 #define SK_SUPPORT_LEGACY_STREAM_API
 #endif
 
+#ifndef SK_SUPPORT_LEGACY_LOCAL_ROTATE_SHADER
+#define SK_SUPPORT_LEGACY_LOCAL_ROTATE_SHADER
+#endif
+
 #ifndef SK_DISABLE_DEFERRED_PROXIES
 #define SK_DISABLE_DEFERRED_PROXIES
 #endif
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 6e25bc7..19d9833b 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -10567,95 +10567,19 @@
   },
   "Fuchsia (dbg)": {
     "additional_compile_targets": [
+      "base_unittests",
+      "crypto_unittests",
       "gl_unittests",
+      "ipc_tests",
       "media_unittests",
-      "net_unittests"
-    ],
-    "gtest_tests": [
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.base_unittests.filter"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "base_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "crypto_unittests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.ipc_tests.filter"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "ipc_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.media_unittests.filter"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "media_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_common_unittests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.mojo_js_unittests.filter"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_js_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_public_bindings_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_public_system_unittests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.mojo_system_unittests.filter"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "mojo_system_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "skia_unittests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.ui_base_unittests.filter"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "ui_base_unittests"
-      }
+      "mojo_common_unittests",
+      "mojo_js_unittests",
+      "mojo_public_bindings_unittests",
+      "mojo_public_system_unittests",
+      "mojo_system_unittests",
+      "net_unittests",
+      "skia_unittests",
+      "ui_base_unittests"
     ]
   },
   "Fuchsia Compile": {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index b655dcd4..7fa0129 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1296,6 +1296,22 @@
             ]
         }
     ],
+    "MITMSoftwareInterstitial": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "win",
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled"
+                }
+            ]
+        }
+    ],
     "MacAllowBackgroundingProcesses": [
         {
             "platforms": [
@@ -1706,30 +1722,6 @@
             ]
         }
     ],
-    "NoStatePrefetchValidation": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "linux",
-                "mac",
-                "win"
-            ],
-            "experiments": [
-                {
-                    "name": "PrerenderDisabled_R2",
-                    "params": {
-                        "instant_mode": "simple_load",
-                        "mode": "simple_load",
-                        "omnibox_mode": "simple_load"
-                    },
-                    "enable_features": [
-                        "NoStatePrefetch"
-                    ]
-                }
-            ]
-        }
-    ],
     "NonDelayableThrottlesDelayable": [
         {
             "platforms": [
@@ -3002,24 +2994,6 @@
                     "enable_features": [
                         "TranslateCompactUI"
                     ]
-                },
-                {
-                    "name": "CompactUIAndRanker",
-                    "enable_features": [
-                        "TranslateCompactUI",
-                        "TranslateRankerEnforcement",
-                        "TranslateRankerQuery"
-                    ]
-                },
-                {
-                    "name": "RankerOnly",
-                    "enable_features": [
-                        "TranslateRankerEnforcement",
-                        "TranslateRankerQuery"
-                    ],
-                    "disable_features": [
-                        "TranslateCompactUI"
-                    ]
                 }
             ]
         }
@@ -3056,6 +3030,16 @@
             ],
             "experiments": [
                 {
+                    "name": "Enforcement20170329",
+                    "params": {
+                        "translate-ranker-model-url": "https://www.gstatic.com/chrome/intelligence/assist/ranker/models/translate/2017/03/translate_ranker_model_20170329.pb.bin"
+                    },
+                    "enable_features": [
+                        "TranslateRankerEnforcement",
+                        "TranslateRankerQuery"
+                    ]
+                },
+                {
                     "name": "Query20170329",
                     "params": {
                         "translate-ranker-model-url": "https://www.gstatic.com/chrome/intelligence/assist/ranker/models/translate/2017/03/translate_ranker_model_20170329.pb.bin"
diff --git a/third_party/.gitignore b/third_party/.gitignore
index c16a25f..4213f70 100644
--- a/third_party/.gitignore
+++ b/third_party/.gitignore
@@ -9,6 +9,7 @@
 /android_protobuf/src
 /android_support_test_runner/lib/*.aar
 /android_support_test_runner/lib/*.jar
+/android_system_sdk/*.jar
 /android_tools/
 /android_tools_internal/
 /android_webview_glue/src
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
index 77b35744..f7ed034 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-features=NetworkService
@@ -173,6 +173,7 @@
 Bug(none) external/wpt/FileAPI/url/url_xmlhttprequest.html [ Crash Failure Timeout ]
 Bug(none) external/wpt/XMLHttpRequest [ Crash Failure Timeout ]
 Bug(none) external/wpt/background-fetch/interfaces-worker.https.html [ Crash Failure Timeout ]
+Bug(none) external/wpt/beacon/headers/header-content-type.html [ Timeout ]
 Bug(none) external/wpt/clear-site-data/navigation.https.html [ Timeout ]
 Bug(none) external/wpt/content-security-policy/child-src/child-src-about-blank-allowed-by-default.sub.html [ Failure Timeout ]
 Bug(none) external/wpt/content-security-policy/child-src/child-src-about-blank-allowed-by-scheme.sub.html [ Failure Timeout ]
@@ -391,7 +392,7 @@
 Bug(715632) http/tests/appcache/manifest-redirect.html [ Crash ]
 Bug(none) http/tests/appcache/manifest-parsing.html [ Failure ]
 Bug(none) http/tests/appcache/multi-fallback.html [ Failure Timeout ]
-Bug(none) http/tests/appcache/non-html.xhtml [ Timeout ]
+Bug(none) http/tests/appcache/non-html.xhtml [ Crash ]
 Bug(none) http/tests/appcache/offline-access.html [ Timeout ]
 Bug(none) http/tests/appcache/online-fallback-layering.html [ Timeout ]
 Bug(none) http/tests/appcache/online-whitelist.html [ Failure ]
@@ -1288,4 +1289,4 @@
 Bug(none) tables/mozilla_expected_failures/marvin/backgr_fixed-bg.html [ Failure ]
 Bug(none) traversal/node-iterator-009.html [ Failure ]
 Bug(none) traversal/tree-walker-006.html [ Failure ]
-Bug(none) virtual [ Crash Failure Timeout ]
\ No newline at end of file
+Bug(none) virtual [ Crash Failure Timeout ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index d3b1e31d..1d28bc8 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -2666,8 +2666,6 @@
 crbug.com/747751 http/tests/inspector/application-panel/resources-panel-resource-preview.html [ Failure Pass ]
 
 crbug.com/689781 external/wpt/media-source/mediasource-duration.html [ Failure Pass ]
-crbug.com/689781 http/tests/media/media-source/mediasource-duration.html [ Failure Pass ]
-crbug.com/689781 virtual/mojo-loading/http/tests/media/media-source/mediasource-duration.html [ Failure Pass ]
 
 crbug.com/701445 external/wpt/dom/events/EventListener-invoke-legacy.html [ Timeout Pass ]
 
diff --git a/third_party/WebKit/LayoutTests/editing/execCommand/insert-list-items-in-table-cell.html b/third_party/WebKit/LayoutTests/editing/execCommand/insert-list-items-in-table-cell.html
new file mode 100644
index 0000000..2e84db8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/execCommand/insert-list-items-in-table-cell.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../assert_selection.js"></script>
+<script>
+function doubleInsertOrderedList(s) {
+     document.execCommand('insertOrderedList');
+     document.execCommand('insertOrderedList');
+}
+
+test(() => assert_selection(
+     '<div contenteditable><table><tr><td>|fsdf</td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></table></div>',
+     'insertOrderedList',
+     '<div contenteditable><table><tbody><tr><td><ol><li>|fsdf</li></ol></td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></tbody></table></div>'),
+     'insertOrderedList - Insert list items in a single table cell.');
+
+test(() => assert_selection(
+     '<div contenteditable><table><tr><td>^fsdf</td><td>fsdf|</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></table></div>',
+     'insertOrderedList',
+     '<div contenteditable><table><tbody><tr><td><ol><li>^fsdf</li></ol></td><td><ol><li>fsdf|</li></ol></td></tr><tr><td>gghfg</td><td>fsfg</td></tr></tbody></table></div>'),
+     'insertOrderedList - Insert list items in all the cells of a table row.');
+
+test(() => assert_selection(
+     '<div contenteditable><table><tr><td>^fsdf</td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg|</td></tr></table></div>',
+     'insertOrderedList',
+     '<div contenteditable><table><tbody><tr><td><ol><li>^fsdf</li></ol></td><td><ol><li>fsdf</li></ol></td></tr><tr><td><ol><li>gghfg</li></ol></td><td><ol><li>fsfg|</li></ol></td></tr></tbody></table></div>'),
+     'insertOrderedList - Insert list items in all the cells of a table.');
+
+test(() => assert_selection(
+     '<div contenteditable>^<table><tr><td>fsdf</td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></table>|</div>',
+     'insertOrderedList',
+     '<div contenteditable><ol><li>^<table><tbody><tr><td>fsdf</td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></tbody></table>|</li></ol></div>'),
+     'insertOrderedList - Insert a list item in a table.');
+
+test(() => assert_selection(
+     '<div contenteditable><table><tr><td>|fsdf</td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></table></div>',
+     'doubleInsertOrderedList',
+     '<div contenteditable><table><tbody><tr><td>|fsdf</td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></tbody></table></div>'),
+     'doubleInsertOrderedList - Exec insertOrderedList twice in a single cell of a table row does not change the editable area at all.');
+
+test(() => assert_selection(
+     '<div contenteditable><table><tr><td>^fsdf</td><td>fsdf|</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></table></div>',
+     'doubleInsertOrderedList',
+     '<div contenteditable><table><tbody><tr><td>^fsdf</td><td>fsdf|</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></tbody></table></div>'),
+     'doubleInsertOrderedList - Exec insertOrderedList twice in all the cells of a table row does not change the editable area at all.');
+
+test(() => assert_selection(
+     '<div contenteditable><table><tr><td>^fsdf</td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg|</td></tr></table></div>',
+     'doubleInsertOrderedList',
+     '<div contenteditable><table><tbody><tr><td>^fsdf</td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg|</td></tr></tbody></table></div>'),
+     'doubleInsertOrderedList - Exec insertOrderedList twice in all the cells of a table does not change the editable area at all.');
+
+test(() => assert_selection(
+     '<div contenteditable>^<table><tr><td>fsdf</td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></table>|</div>',
+     'doubleInsertOrderedList',
+     '<div contenteditable>^<table><tbody><tr><td>fsdf</td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></tbody></table>|</div>'),
+     'doubleInsertOrderedList - Exec insertOrderedList twice in a table does not change the editable area at all.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/editing/data/insert-list-items-in-table-cells.js b/third_party/WebKit/LayoutTests/external/wpt/editing/data/insert-list-items-in-table-cells.js
new file mode 100644
index 0000000..e1c1e15c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/editing/data/insert-list-items-in-table-cells.js
@@ -0,0 +1,43 @@
+// For documentation of the format, see README in this directory.
+var browserTests = [
+['<div contenteditable="true"><table><tr><td>[fsdf]</td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></table></div>',
+    [["insertOrderedList",""]],
+    '<div contenteditable="true"><table><tbody><tr><td><ol><li>fsdf<br></li></ol></td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></tbody></table></div>',
+    [true],
+    {"insertOrderedList":[false,false,"false",false,true,"true"]}],
+['<div contenteditable="true"><table><tr data-start=0 data-end=2><td>fsdf</td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></table></div>',
+    [["insertOrderedList",""]],
+    '<div contenteditable="true"><table><tbody><tr><td><ol><li>fsdf<br></li></ol></td><td><ol><li>fsdf<br></li></ol></td></tr><tr><td>gghfg</td><td>fsfg</td></tr></tbody></table></div>',
+    [true],
+    {"insertOrderedList":[false,false,"false",false,false,"false"]}],
+['<div contenteditable="true"><table><tr data-start=0><td>fsdf</td><td>fsdf</td></tr><tr data-end=2><td>gghfg</td><td>fsfg</td></tr></table></div>',
+    [["insertOrderedList",""]],
+    '<div contenteditable="true"><table><tbody><tr><td><ol><li>fsdf<br></li></ol></td><td><ol><li>fsdf<br></li></ol></td></tr><tr><td><ol><li>gghfg<br></li></ol></td><td><ol><li>fsfg<br></li></ol></td></tr></tbody></table></div>',
+    [true],
+    {"insertOrderedList":[false,false,"false",false,false,"false"]}],
+['<div contenteditable="true"><table data-start=0 data-end=1><tr><td>fsdf</td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></table></div>',
+    [["insertOrderedList",""]],
+    '<div contenteditable="true"><ol><li><table><tbody><tr><td>fsdf</td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></tbody></table></li></ol></div>',
+    [true],
+    {"insertOrderedList":[false,false,"false",false,true,"true"]}],
+['<div contenteditable="true"><table><tr><td>[fsdf]</td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></table></div>',
+    [["insertOrderedList",""], ["insertOrderedList","1"]],
+    '<div contenteditable="true"><table><tbody><tr><td>fsdf<br></td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></tbody></table></div>',
+    [true, true],
+    {"insertOrderedList":[false,false,"false",false,true,"true"], "insertOrderedList":[false,false,"false",false,false,"false"]}],
+['<div contenteditable="true"><table><tr data-start=0 data-end=2><td>fsdf</td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></table></div>',
+    [["insertOrderedList",""], ["insertOrderedList","1"]],
+    '<div contenteditable="true"><table><tbody><tr><td>fsdf<br></td><td>fsdf<br></td></tr><tr><td>gghfg</td><td>fsfg</td></tr></tbody></table></div>',
+    [true, true],
+    {"insertOrderedList":[false,false,"false",false,true,"true"], "insertOrderedList":[false,false,"false",false,false,"false"]}],
+['<div contenteditable="true"><table><tr data-start=0><td>fsdf</td><td>fsdf</td></tr><tr data-end=2><td>gghfg</td><td>fsfg</td></tr></table></div>',
+    [["insertOrderedList",""], ["insertOrderedList","1"]],
+    '<div contenteditable="true"><table><tbody><tr><td>fsdf<br></td><td>fsdf<br></td></tr><tr><td>gghfg<br></td><td>fsfg<br></td></tr></tbody></table></div>',
+    [true, true],
+    {"insertOrderedList":[false,false,"false",false,true,"true"], "insertOrderedList":[false,false,"false",false,false,"false"]}],
+['<div contenteditable="true"><table data-start=0 data-end=1><tr><td>fsdf</td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></table></div>',
+    [["insertOrderedList",""], ["insertOrderedList","1"]],
+    '<div contenteditable="true"><table><tbody><tr><td>fsdf</td><td>fsdf</td></tr><tr><td>gghfg</td><td>fsfg</td></tr></tbody></table></div>',
+    [true, true],
+    {"insertOrderedList":[false,false,"false",false,true,"true"], "insertOrderedList":[false,false,"false",false,false,"false"]}]
+]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/editing/run/insert-list-items-in-table-cell.html b/third_party/WebKit/LayoutTests/external/wpt/editing/run/insert-list-items-in-table-cell.html
new file mode 100644
index 0000000..ebb14f4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/editing/run/insert-list-items-in-table-cell.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<meta charset=utf-8>
+<link rel=stylesheet href=../include/reset.css>
+<title>Insert list items in table cells - HTML editing conformance tests</title>
+
+<p id=timing></p>
+
+<div id=log></div>
+
+<div id=test-container></div>
+
+<script src=../include/implementation.js></script>
+<script>var testsJsLibraryOnly = true</script>
+<script src=../include/tests.js></script>
+<script src=../data/insert-list-items-in-table-cells.js></script>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+"use strict";
+
+(function() {
+    var startTime = Date.now();
+
+    // Make document.body.innerHTML more tidy by removing unnecessary things.
+    [].forEach.call(document.querySelectorAll("script"), function(node) {
+        node.parentNode.removeChild(node);
+    });
+
+    if (true) {
+        // Silly hack: the CSS styling flag should be true, not false, to match
+        // expected results.  This is because every group of tests except the
+        // last (multitest) sets styleWithCSS automatically, and it sets it
+        // first to false and then to true.  Thus it's left at true at the end
+        // of each group of tests, so in gentest.html it will be true when
+        // starting each group of tests other than the first.  But browsers are
+        // supposed to default it to false when the page loads, so flip it.
+        try { document.execCommand("styleWithCSS", false, "true") } catch(e) {}
+    }
+
+    browserTests.forEach(runConformanceTest);
+
+    document.getElementById("test-container").parentNode
+        .removeChild(document.getElementById("test-container"));
+
+    var elapsed = Math.round(Date.now() - startTime)/1000;
+    document.getElementById("timing").textContent =
+        "Time elapsed: " + Math.floor(elapsed/60) + ":"
+        + ((elapsed % 60) < 10 ? "0" : "")
+        + (elapsed % 60).toFixed(3) + " min.";
+})();
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/wasm/create_multiple_memory.worker.js b/third_party/WebKit/LayoutTests/external/wpt/wasm/create_multiple_memory.worker.js
new file mode 100644
index 0000000..f5733b7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/wasm/create_multiple_memory.worker.js
@@ -0,0 +1,13 @@
+// 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.
+
+// The WebAssembly spec doesn't specify a limit on how many Memory objects can
+// be allocated. This test makes sure we can have at least two at once.
+
+importScripts("/resources/testharness.js");
+test(function () {
+  const mem1 = new WebAssembly.Memory({initial: 1});
+  const mem2 = new WebAssembly.Memory({initial: 1});
+}, "WebAssembly#CreateMultipleMemories.");
+done();
diff --git a/third_party/WebKit/LayoutTests/http/tests/media/media-source/mediasource-duration.html b/third_party/WebKit/LayoutTests/http/tests/media/media-source/mediasource-duration.html
index 45d480d8..9ceb29a 100644
--- a/third_party/WebKit/LayoutTests/http/tests/media/media-source/mediasource-duration.html
+++ b/third_party/WebKit/LayoutTests/http/tests/media/media-source/mediasource-duration.html
@@ -176,7 +176,7 @@
 
               // Append all the segments
               test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer append completed');
-              test.expectEvent(mediaElement, 'playing', 'Playing triggered');
+              test.expectEvent(mediaElement, 'loadedmetadata', 'mediaElement loadedmetadata');
               sourceBuffer.appendBuffer(mediaData);
 
               test.waitForExpectedEvents(function()
@@ -190,30 +190,31 @@
 
                   // Increase duration. This should result in one 'durationchange' fired.
                   mediaSource.duration = newDuration;
-                  assert_false(sourceBuffer.updating, "sourceBuffer.updating after duration set to newDuration");
+                  assert_false(sourceBuffer.updating, 'sourceBuffer.updating after duration set to newDuration');
+                  assert_equals(mediaElement.duration, newDuration, 'mediaElement duration matches newDuration');
 
                   // Set duration again to make sure it does not trigger another 'durationchange' event.
                   mediaSource.duration = newDuration;
 
-                  assert_false(sourceBuffer.updating, "sourceBuffer.updating after duration set again to newDuration");
+                  assert_false(sourceBuffer.updating, 'sourceBuffer.updating after duration set again to newDuration');
+                  assert_equals(mediaElement.duration, newDuration, 'mediaElement duration matches newDuration after mediaSource duration set again to newDuration');
 
                   // Mark endOfStream so that playback can reach 'ended' at the new duration.
                   test.expectEvent(mediaSource, 'sourceended', 'endOfStream acknowledged');
                   mediaSource.endOfStream();
 
-                  // endOfStream can change duration downwards slightly.
-                  // Allow for one more 'durationchange' event only in this case.
-                  var currentDuration = mediaSource.duration;
-                  if (currentDuration != newDuration) {
-                      assert_true(currentDuration > 0 && currentDuration < newDuration, 'adjusted duration check');
-                      newDuration = currentDuration;
-                      ++expectedDurationChangeEventCount;
-                  }
+                  // endOfStream should reduce the duration back to fullDuration.
+                  assert_equals(mediaElement.duration, fullDuration, 'mediaElement duration returns to fullDuration after endOfStream()');
+                  assert_equals(mediaSource.duration, fullDuration, 'mediaSource duration returns to fullDuration after endOfStream()');
+
+                  // Adjust the duration and durationchanged event expectations due to endOfStream()'s duration change.
+                  newDuration = fullDuration;
+                  ++expectedDurationChangeEventCount;
               });
 
               test.waitForExpectedEvents(function()
               {
-                  // Allow any remaining queued durationchange to fire, while counting 'durationchange' them.
+                  // Allow any remaining queued durationchange to fire, while counting them.
                   test.step_timeout(test.step_func(function()
                   {
                       mediaElement.removeEventListener('durationchange', durationchangeEventHandler);
@@ -221,7 +222,7 @@
                       test.done();
                   }), 0);
               });
-          }, 'Test setting same duration multiple times does not fire duplicate durationchange', {timeout: 2500});
+          }, 'Test setting same duration multiple times does not fire duplicate durationchange');
 
           mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
           {
diff --git a/third_party/WebKit/LayoutTests/resize-observer/observe-expected.txt b/third_party/WebKit/LayoutTests/resize-observer/observe-expected.txt
deleted file mode 100644
index 15cc717..0000000
--- a/third_party/WebKit/LayoutTests/resize-observer/observe-expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-CONSOLE ERROR: ResizeObserver loop limit exceeded
-This is a testharness.js-based test.
-PASS guard 
-PASS test0: simple observation 
-PASS test1: multiple observation on same element trigger only one 
-FAIL test2: throw exception when observing non-element Test bug: need to pass exception to assert_throws()
-PASS test3: disconnect stops all notifications 
-PASS test4: unobserve target stops notifications, unobserve non-observed does nothing 
-PASS test5: observe img 
-PASS test6: iframe notifications 
-PASS test7: callback.this 
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/resize-observer/observe.html b/third_party/WebKit/LayoutTests/resize-observer/observe.html
index a34aed8..45ebcd6 100644
--- a/third_party/WebKit/LayoutTests/resize-observer/observe.html
+++ b/third_party/WebKit/LayoutTests/resize-observer/observe.html
@@ -56,7 +56,7 @@
 
 function test2() {
   test(() => {
-      assert_throws(null, _=> {
+      assert_throws({name: "TypeError"}, _=> {
         let ro = new ResizeObserver(() => {});
         ro.observe({});
       });
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8IntersectionObserverDelegate.cpp b/third_party/WebKit/Source/bindings/core/v8/V8IntersectionObserverDelegate.cpp
index f752e4a2..be1f6466 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8IntersectionObserverDelegate.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/V8IntersectionObserverDelegate.cpp
@@ -17,7 +17,7 @@
 V8IntersectionObserverDelegate::V8IntersectionObserverDelegate(
     IntersectionObserverCallback* callback,
     ScriptState* script_state)
-    : callback_(this, callback), script_state_(script_state) {}
+    : callback_(callback), script_state_(script_state) {}
 
 V8IntersectionObserverDelegate::~V8IntersectionObserverDelegate() {}
 
diff --git a/third_party/WebKit/Source/core/clipboard/DataTransferItem.cpp b/third_party/WebKit/Source/core/clipboard/DataTransferItem.cpp
index 24e38bae..566e674 100644
--- a/third_party/WebKit/Source/core/clipboard/DataTransferItem.cpp
+++ b/third_party/WebKit/Source/core/clipboard/DataTransferItem.cpp
@@ -76,7 +76,7 @@
   if (!callback || item_->Kind() != DataObjectItem::kStringKind)
     return;
 
-  callbacks_.emplace_back(this, callback);
+  callbacks_.emplace_back(callback);
   ExecutionContext* context = ExecutionContext::From(script_state);
   probe::AsyncTaskScheduled(context, "DataTransferItem.getAsString", callback);
   TaskRunnerHelper::Get(TaskType::kUserInteraction, script_state)
diff --git a/third_party/WebKit/Source/core/css/PropertySetCSSStyleDeclaration.cpp b/third_party/WebKit/Source/core/css/PropertySetCSSStyleDeclaration.cpp
index ecd21c4..eb470b9 100644
--- a/third_party/WebKit/Source/core/css/PropertySetCSSStyleDeclaration.cpp
+++ b/third_party/WebKit/Source/core/css/PropertySetCSSStyleDeclaration.cpp
@@ -354,7 +354,7 @@
     MutableStylePropertySet& property_set_arg,
     CSSRule* parent_rule)
     : PropertySetCSSStyleDeclaration(property_set_arg),
-      parent_rule_(this, parent_rule) {}
+      parent_rule_(parent_rule) {}
 
 StyleRuleCSSStyleDeclaration::~StyleRuleCSSStyleDeclaration() {}
 
diff --git a/third_party/WebKit/Source/core/dom/Attr.cpp b/third_party/WebKit/Source/core/dom/Attr.cpp
index 1745a700..7213b4b 100644
--- a/third_party/WebKit/Source/core/dom/Attr.cpp
+++ b/third_party/WebKit/Source/core/dom/Attr.cpp
@@ -38,14 +38,13 @@
 
 Attr::Attr(Element& element, const QualifiedName& name)
     : Node(&element.GetDocument(), kCreateOther),
-      element_(this, &element),
+      element_(&element),
       name_(name) {}
 
 Attr::Attr(Document& document,
            const QualifiedName& name,
            const AtomicString& standalone_value)
     : Node(&document, kCreateOther),
-      element_(this, nullptr),
       name_(name),
       standalone_value_or_attached_local_name_(standalone_value) {}
 
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index 3d1d8c1..6bccac9 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -515,11 +515,9 @@
       // TODO(dcheng): Why does this need both a LocalFrame and LocalDOMWindow
       // pointer?
       dom_window_(frame_ ? frame_->DomWindow() : nullptr),
-      imports_controller_(this, initializer.ImportsController()),
-      parser_(this, nullptr),
+      imports_controller_(initializer.ImportsController()),
       context_features_(ContextFeatures::DefaultSwitch()),
       well_formed_(false),
-      implementation_(this, nullptr),
       printing_(kNotPrinting),
       paginated_for_screen_(false),
       compatibility_mode_(kNoQuirksMode),
@@ -533,8 +531,6 @@
       style_version_(0),
       listener_types_(0),
       mutation_observer_types_(0),
-      style_engine_(this, nullptr),
-      style_sheet_list_(this, nullptr),
       visited_link_state_(VisitedLinkState::Create(*this)),
       visually_ordered_(false),
       ready_state_(kComplete),
diff --git a/third_party/WebKit/Source/core/dom/ElementShadow.cpp b/third_party/WebKit/Source/core/dom/ElementShadow.cpp
index 19e159c4..0a1a744 100644
--- a/third_party/WebKit/Source/core/dom/ElementShadow.cpp
+++ b/third_party/WebKit/Source/core/dom/ElementShadow.cpp
@@ -42,10 +42,7 @@
   return new ElementShadow();
 }
 
-ElementShadow::ElementShadow()
-    : element_shadow_v0_(this, nullptr),
-      shadow_root_(this, nullptr),
-      needs_distribution_recalc_(false) {}
+ElementShadow::ElementShadow() : needs_distribution_recalc_(false) {}
 
 ShadowRoot& ElementShadow::YoungestShadowRoot() const {
   ShadowRoot* current = shadow_root_;
diff --git a/third_party/WebKit/Source/core/dom/ModulatorImpl.cpp b/third_party/WebKit/Source/core/dom/ModulatorImpl.cpp
index d0bc44b..6b175eb 100644
--- a/third_party/WebKit/Source/core/dom/ModulatorImpl.cpp
+++ b/third_party/WebKit/Source/core/dom/ModulatorImpl.cpp
@@ -28,9 +28,9 @@
       task_runner_(
           TaskRunnerHelper::Get(TaskType::kNetworking, script_state_.Get())),
       fetcher_(fetcher),
-      map_(this, ModuleMap::Create(this)),
+      map_(ModuleMap::Create(this)),
       loader_registry_(ModuleScriptLoaderRegistry::Create()),
-      tree_linker_registry_(this, ModuleTreeLinkerRegistry::Create()),
+      tree_linker_registry_(ModuleTreeLinkerRegistry::Create()),
       script_module_resolver_(ScriptModuleResolverImpl::Create(
           this,
           ExecutionContext::From(script_state_.Get()))) {
diff --git a/third_party/WebKit/Source/core/dom/ModuleMap.cpp b/third_party/WebKit/Source/core/dom/ModuleMap.cpp
index f5e15a51..6e7b31e 100644
--- a/third_party/WebKit/Source/core/dom/ModuleMap.cpp
+++ b/third_party/WebKit/Source/core/dom/ModuleMap.cpp
@@ -49,8 +49,7 @@
   HeapHashSet<Member<SingleModuleClient>> clients_;
 };
 
-ModuleMap::Entry::Entry(ModuleMap* map)
-    : module_script_(this, nullptr), map_(map) {
+ModuleMap::Entry::Entry(ModuleMap* map) : map_(map) {
   DCHECK(map_);
 }
 
@@ -124,11 +123,10 @@
   // Step 2. If moduleMap[url] is "fetching", wait in parallel until that
   // entry's value changes, then queue a task on the networking task source to
   // proceed with running the following steps.
-  MapImpl::AddResult result =
-      map_.insert(request.Url(), TraceWrapperMember<Entry>(this, nullptr));
+  MapImpl::AddResult result = map_.insert(request.Url(), nullptr);
   TraceWrapperMember<Entry>& entry = result.stored_value->value;
   if (result.is_new_entry) {
-    entry = TraceWrapperMember<Entry>(this, Entry::Create(this));
+    entry = Entry::Create(this);
 
     // Steps 4-9 loads a new single module script.
     // Delegates to ModuleScriptLoader via Modulator.
diff --git a/third_party/WebKit/Source/core/dom/ModulePendingScript.cpp b/third_party/WebKit/Source/core/dom/ModulePendingScript.cpp
index 7367ae6..0a8e452 100644
--- a/third_party/WebKit/Source/core/dom/ModulePendingScript.cpp
+++ b/third_party/WebKit/Source/core/dom/ModulePendingScript.cpp
@@ -9,8 +9,7 @@
 
 namespace blink {
 
-ModulePendingScriptTreeClient::ModulePendingScriptTreeClient()
-    : module_script_(this, nullptr), pending_script_(this, nullptr) {}
+ModulePendingScriptTreeClient::ModulePendingScriptTreeClient() {}
 
 void ModulePendingScriptTreeClient::SetPendingScript(
     ModulePendingScript* pending_script) {
@@ -53,7 +52,7 @@
                                          ModulePendingScriptTreeClient* client,
                                          bool is_external)
     : PendingScript(element, TextPosition()),
-      module_tree_client_(this, client),
+      module_tree_client_(client),
       is_external_(is_external) {
   CHECK(this->GetElement());
   DCHECK(module_tree_client_);
diff --git a/third_party/WebKit/Source/core/dom/MutationObserver.cpp b/third_party/WebKit/Source/core/dom/MutationObserver.cpp
index d37ea672..05e76dc 100644
--- a/third_party/WebKit/Source/core/dom/MutationObserver.cpp
+++ b/third_party/WebKit/Source/core/dom/MutationObserver.cpp
@@ -82,7 +82,7 @@
  private:
   V8DelegateImpl(MutationCallback* callback,
                  ExecutionContext* execution_context)
-      : ContextClient(execution_context), callback_(this, callback) {}
+      : ContextClient(execution_context), callback_(callback) {}
 
   TraceWrapperMember<MutationCallback> callback_;
 };
@@ -112,7 +112,7 @@
 MutationObserver::MutationObserver(ExecutionContext* execution_context,
                                    Delegate* delegate)
     : ContextClient(execution_context),
-      delegate_(this, delegate),
+      delegate_(delegate),
       priority_(g_observer_priority++) {}
 
 MutationObserver::~MutationObserver() {
@@ -271,7 +271,7 @@
 
 void MutationObserver::EnqueueMutationRecord(MutationRecord* mutation) {
   DCHECK(IsMainThread());
-  records_.push_back(TraceWrapperMember<MutationRecord>(this, mutation));
+  records_.push_back(mutation);
   ActivateObserver(this);
   probe::AsyncTaskScheduled(delegate_->GetExecutionContext(), mutation->type(),
                             mutation);
diff --git a/third_party/WebKit/Source/core/dom/MutationObserverRegistration.cpp b/third_party/WebKit/Source/core/dom/MutationObserverRegistration.cpp
index c2c568a..90441a5 100644
--- a/third_party/WebKit/Source/core/dom/MutationObserverRegistration.cpp
+++ b/third_party/WebKit/Source/core/dom/MutationObserverRegistration.cpp
@@ -49,7 +49,7 @@
     Node* registration_node,
     MutationObserverOptions options,
     const HashSet<AtomicString>& attribute_filter)
-    : observer_(this, &observer),
+    : observer_(&observer),
       registration_node_(registration_node),
       options_(options),
       attribute_filter_(attribute_filter) {
diff --git a/third_party/WebKit/Source/core/dom/MutationRecord.cpp b/third_party/WebKit/Source/core/dom/MutationRecord.cpp
index f252deb..e4efd5e 100644
--- a/third_party/WebKit/Source/core/dom/MutationRecord.cpp
+++ b/third_party/WebKit/Source/core/dom/MutationRecord.cpp
@@ -47,9 +47,9 @@
                   StaticNodeList* removed,
                   Node* previous_sibling,
                   Node* next_sibling)
-      : target_(this, target),
-        added_nodes_(this, added),
-        removed_nodes_(this, removed),
+      : target_(target),
+        added_nodes_(added),
+        removed_nodes_(removed),
         previous_sibling_(previous_sibling),
         next_sibling_(next_sibling) {}
 
@@ -87,10 +87,7 @@
 class RecordWithEmptyNodeLists : public MutationRecord {
  public:
   RecordWithEmptyNodeLists(Node* target, const String& old_value)
-      : target_(this, target),
-        old_value_(old_value),
-        added_nodes_(this, nullptr),
-        removed_nodes_(this, nullptr) {}
+      : target_(target), old_value_(old_value) {}
 
   DEFINE_INLINE_VIRTUAL_TRACE() {
     visitor->Trace(target_);
@@ -160,8 +157,7 @@
 
 class MutationRecordWithNullOldValue : public MutationRecord {
  public:
-  MutationRecordWithNullOldValue(MutationRecord* record)
-      : record_(this, record) {}
+  MutationRecordWithNullOldValue(MutationRecord* record) : record_(record) {}
 
   DEFINE_INLINE_VIRTUAL_TRACE() {
     visitor->Trace(record_);
diff --git a/third_party/WebKit/Source/core/dom/NodeIterator.cpp b/third_party/WebKit/Source/core/dom/NodeIterator.cpp
index f74a75bd..f6f1a26 100644
--- a/third_party/WebKit/Source/core/dom/NodeIterator.cpp
+++ b/third_party/WebKit/Source/core/dom/NodeIterator.cpp
@@ -65,7 +65,7 @@
 NodeIterator::NodeIterator(Node* root_node,
                            unsigned what_to_show,
                            V8NodeFilterCondition* filter)
-    : NodeIteratorBase(this, root_node, what_to_show, filter),
+    : NodeIteratorBase(root_node, what_to_show, filter),
       reference_node_(root(), true) {
   // If NodeIterator target is Attr node, don't subscribe for nodeWillBeRemoved,
   // as it would never have child nodes.
diff --git a/third_party/WebKit/Source/core/dom/NodeIteratorBase.cpp b/third_party/WebKit/Source/core/dom/NodeIteratorBase.cpp
index b2c338cd..3af3aa6 100644
--- a/third_party/WebKit/Source/core/dom/NodeIteratorBase.cpp
+++ b/third_party/WebKit/Source/core/dom/NodeIteratorBase.cpp
@@ -31,13 +31,10 @@
 
 namespace blink {
 
-NodeIteratorBase::NodeIteratorBase(void* child_this,
-                                   Node* root_node,
+NodeIteratorBase::NodeIteratorBase(Node* root_node,
                                    unsigned what_to_show,
                                    V8NodeFilterCondition* node_filter)
-    : root_(root_node),
-      what_to_show_(what_to_show),
-      filter_(child_this, node_filter) {}
+    : root_(root_node), what_to_show_(what_to_show), filter_(node_filter) {}
 
 unsigned NodeIteratorBase::AcceptNode(Node* node,
                                       ExceptionState& exception_state) const {
diff --git a/third_party/WebKit/Source/core/dom/NodeIteratorBase.h b/third_party/WebKit/Source/core/dom/NodeIteratorBase.h
index 6d9c8ae..e21d76c5 100644
--- a/third_party/WebKit/Source/core/dom/NodeIteratorBase.h
+++ b/third_party/WebKit/Source/core/dom/NodeIteratorBase.h
@@ -44,13 +44,7 @@
   DECLARE_VIRTUAL_TRACE_WRAPPERS();
 
  protected:
-  // In order to properly trace wrappers it is necessary for TraceWrapperMember
-  // to find the object header from within the mixin. |childThis| is safe to
-  // find the header so we pass it instead of |this|.
-  NodeIteratorBase(void* child_this,
-                   Node*,
-                   unsigned what_to_show,
-                   V8NodeFilterCondition*);
+  NodeIteratorBase(Node*, unsigned what_to_show, V8NodeFilterCondition*);
   unsigned AcceptNode(Node*, ExceptionState&) const;
 
  private:
diff --git a/third_party/WebKit/Source/core/dom/NodeRareData.h b/third_party/WebKit/Source/core/dom/NodeRareData.h
index 1b2fc5b..6e40e4d 100644
--- a/third_party/WebKit/Source/core/dom/NodeRareData.h
+++ b/third_party/WebKit/Source/core/dom/NodeRareData.h
@@ -50,8 +50,7 @@
   }
 
   void AddTransientRegistration(MutationObserverRegistration* registration) {
-    transient_registry_.insert(
-        TraceWrapperMember<MutationObserverRegistration>(this, registration));
+    transient_registry_.insert(registration);
   }
 
   void RemoveTransientRegistration(MutationObserverRegistration* registration) {
@@ -60,8 +59,7 @@
   }
 
   void AddRegistration(MutationObserverRegistration* registration) {
-    registry_.push_back(
-        TraceWrapperMember<MutationObserverRegistration>(this, registration));
+    registry_.push_back(registration);
   }
 
   void RemoveRegistration(MutationObserverRegistration* registration) {
diff --git a/third_party/WebKit/Source/core/dom/ScriptLoader.cpp b/third_party/WebKit/Source/core/dom/ScriptLoader.cpp
index b75fac94..aeb386a 100644
--- a/third_party/WebKit/Source/core/dom/ScriptLoader.cpp
+++ b/third_party/WebKit/Source/core/dom/ScriptLoader.cpp
@@ -78,9 +78,7 @@
       created_during_document_write_(created_during_document_write),
       async_exec_type_(ScriptRunner::kNone),
       document_write_intervention_(
-          DocumentWriteIntervention::kDocumentWriteInterventionNone),
-      pending_script_(this, nullptr),
-      module_tree_client_(this, nullptr) {
+          DocumentWriteIntervention::kDocumentWriteInterventionNone) {
   // https://html.spec.whatwg.org/#already-started
   // "The cloning steps for script elements must set the "already started"
   //  flag on the copy if it is set on the element being cloned."
diff --git a/third_party/WebKit/Source/core/dom/StyleEngine.cpp b/third_party/WebKit/Source/core/dom/StyleEngine.cpp
index 7d8c869..e26e46d 100644
--- a/third_party/WebKit/Source/core/dom/StyleEngine.cpp
+++ b/third_party/WebKit/Source/core/dom/StyleEngine.cpp
@@ -68,7 +68,6 @@
       is_master_(!document.ImportsController() ||
                  document.ImportsController()->Master() == &document),
       document_style_sheet_collection_(
-          this,
           DocumentStyleSheetCollection::Create(document)) {
   if (document.GetFrame()) {
     // We don't need to create CSSFontSelector for imported document or
@@ -141,10 +140,9 @@
 
 WebStyleSheetId StyleEngine::InjectAuthorSheet(
     StyleSheetContents* author_sheet) {
-  injected_author_style_sheets_.push_back(std::make_pair(
-      ++injected_author_sheets_id_count_,
-      TraceWrapperMember<CSSStyleSheet>(
-          this, CSSStyleSheet::Create(author_sheet, *document_))));
+  injected_author_style_sheets_.push_back(
+      std::make_pair(++injected_author_sheets_id_count_,
+                     CSSStyleSheet::Create(author_sheet, *document_)));
 
   MarkDocumentDirty();
   return injected_author_sheets_id_count_;
diff --git a/third_party/WebKit/Source/core/dom/StyleSheetCollection.cpp b/third_party/WebKit/Source/core/dom/StyleSheetCollection.cpp
index 9e92312c..f1fa1cf 100644
--- a/third_party/WebKit/Source/core/dom/StyleSheetCollection.cpp
+++ b/third_party/WebKit/Source/core/dom/StyleSheetCollection.cpp
@@ -59,8 +59,7 @@
 }
 
 void StyleSheetCollection::AppendSheetForList(StyleSheet* sheet) {
-  style_sheets_for_style_sheet_list_.push_back(
-      TraceWrapperMember<StyleSheet>(this, sheet));
+  style_sheets_for_style_sheet_list_.push_back(sheet);
 }
 
 DEFINE_TRACE(StyleSheetCollection) {
diff --git a/third_party/WebKit/Source/core/dom/TreeWalker.cpp b/third_party/WebKit/Source/core/dom/TreeWalker.cpp
index 94e21fb10..c8248d7 100644
--- a/third_party/WebKit/Source/core/dom/TreeWalker.cpp
+++ b/third_party/WebKit/Source/core/dom/TreeWalker.cpp
@@ -35,8 +35,7 @@
 TreeWalker::TreeWalker(Node* root_node,
                        unsigned what_to_show,
                        V8NodeFilterCondition* filter)
-    : NodeIteratorBase(this, root_node, what_to_show, filter),
-      current_(root()) {}
+    : NodeIteratorBase(root_node, what_to_show, filter), current_(root()) {}
 
 void TreeWalker::setCurrentNode(Node* node) {
   DCHECK(node);
diff --git a/third_party/WebKit/Source/core/editing/commands/CompositeEditCommand.cpp b/third_party/WebKit/Source/core/editing/commands/CompositeEditCommand.cpp
index 234f802e..62a98b3 100644
--- a/third_party/WebKit/Source/core/editing/commands/CompositeEditCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/CompositeEditCommand.cpp
@@ -1499,7 +1499,8 @@
   VisiblePosition after_paragraph =
       CreateVisiblePosition(after_paragraph_position.GetPosition());
   if (before_paragraph.IsNotNull() &&
-      (!IsEndOfParagraph(before_paragraph) ||
+      ((!IsStartOfParagraph(before_paragraph) &&
+        !IsEndOfParagraph(before_paragraph)) ||
        before_paragraph.DeepEquivalent() == after_paragraph.DeepEquivalent())) {
     // FIXME: Trim text between beforeParagraph and afterParagraph if they
     // aren't equal.
diff --git a/third_party/WebKit/Source/core/editing/markers/ActiveSuggestionMarkerListImpl.cpp b/third_party/WebKit/Source/core/editing/markers/ActiveSuggestionMarkerListImpl.cpp
index 0cc267c..be35da3 100644
--- a/third_party/WebKit/Source/core/editing/markers/ActiveSuggestionMarkerListImpl.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/ActiveSuggestionMarkerListImpl.cpp
@@ -57,7 +57,8 @@
                                                  length);
 }
 
-bool ActiveSuggestionMarkerListImpl::ShiftMarkers(unsigned offset,
+bool ActiveSuggestionMarkerListImpl::ShiftMarkers(const String&,
+                                                  unsigned offset,
                                                   unsigned old_length,
                                                   unsigned new_length) {
   return DocumentMarkerListEditor::ShiftMarkersContentIndependent(
diff --git a/third_party/WebKit/Source/core/editing/markers/ActiveSuggestionMarkerListImpl.h b/third_party/WebKit/Source/core/editing/markers/ActiveSuggestionMarkerListImpl.h
index f36d21c..2711706 100644
--- a/third_party/WebKit/Source/core/editing/markers/ActiveSuggestionMarkerListImpl.h
+++ b/third_party/WebKit/Source/core/editing/markers/ActiveSuggestionMarkerListImpl.h
@@ -37,7 +37,8 @@
 
   bool MoveMarkers(int length, DocumentMarkerList* dst_list) final;
   bool RemoveMarkers(unsigned start_offset, int length) final;
-  bool ShiftMarkers(unsigned offset,
+  bool ShiftMarkers(const String& node_text,
+                    unsigned offset,
                     unsigned old_length,
                     unsigned new_length) final;
 
diff --git a/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.cpp b/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.cpp
index 75dcf99..b709959 100644
--- a/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.cpp
@@ -55,7 +55,8 @@
                                                  length);
 }
 
-bool CompositionMarkerListImpl::ShiftMarkers(unsigned offset,
+bool CompositionMarkerListImpl::ShiftMarkers(const String&,
+                                             unsigned offset,
                                              unsigned old_length,
                                              unsigned new_length) {
   return DocumentMarkerListEditor::ShiftMarkersContentIndependent(
diff --git a/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.h b/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.h
index dfa4ea8..2c057d4 100644
--- a/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.h
+++ b/third_party/WebKit/Source/core/editing/markers/CompositionMarkerListImpl.h
@@ -36,7 +36,8 @@
 
   bool MoveMarkers(int length, DocumentMarkerList* dst_list) final;
   bool RemoveMarkers(unsigned start_offset, int length) final;
-  bool ShiftMarkers(unsigned offset,
+  bool ShiftMarkers(const String& node_text,
+                    unsigned offset,
                     unsigned old_length,
                     unsigned new_length) final;
 
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp
index 97bc652..fb78832 100644
--- a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp
@@ -789,7 +789,7 @@
     if (!list)
       continue;
 
-    if (list->ShiftMarkers(offset, old_length, new_length))
+    if (list->ShiftMarkers(node->data(), offset, old_length, new_length))
       did_shift_marker = true;
   }
 
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerList.h b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerList.h
index 2387105e..960fcf2e 100644
--- a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerList.h
+++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerList.h
@@ -51,8 +51,13 @@
   // Returns true if at least one marker is removed, false otherwise
   virtual bool RemoveMarkers(unsigned start_offset, int length) = 0;
 
-  // Returns true if at least one marker is shifted or removed, false otherwise
-  virtual bool ShiftMarkers(unsigned offset,
+  // Returns true if at least one marker is shifted or removed, false otherwise.
+  // Called in response to an edit replacing the range
+  // [offset, offset + old_length] by a string of length new_length.
+  // node_text is the full text of the affected node *after* the edit is
+  // applied.
+  virtual bool ShiftMarkers(const String& node_text,
+                            unsigned offset,
                             unsigned old_length,
                             unsigned new_length) = 0;
 
diff --git a/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.cpp b/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.cpp
index ca97ecf..df924c1 100644
--- a/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.cpp
@@ -91,7 +91,8 @@
                                                  length);
 }
 
-bool SpellCheckMarkerListImpl::ShiftMarkers(unsigned offset,
+bool SpellCheckMarkerListImpl::ShiftMarkers(const String&,
+                                            unsigned offset,
                                             unsigned old_length,
                                             unsigned new_length) {
   return DocumentMarkerListEditor::ShiftMarkersContentDependent(
diff --git a/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.h b/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.h
index d56d868a8..c7beb9b 100644
--- a/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.h
+++ b/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.h
@@ -31,7 +31,8 @@
 
   bool MoveMarkers(int length, DocumentMarkerList* dst_list) final;
   bool RemoveMarkers(unsigned start_offset, int length) final;
-  bool ShiftMarkers(unsigned offset,
+  bool ShiftMarkers(const String& node_text,
+                    unsigned offset,
                     unsigned old_length,
                     unsigned new_length) final;
 
diff --git a/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImpl.cpp b/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImpl.cpp
index cd7f3119..a73cb341 100644
--- a/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImpl.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImpl.cpp
@@ -107,7 +107,8 @@
   return did_remove_marker;
 }
 
-bool SuggestionMarkerListImpl::ShiftMarkers(unsigned offset,
+bool SuggestionMarkerListImpl::ShiftMarkers(const String&,
+                                            unsigned offset,
                                             unsigned old_length,
                                             unsigned new_length) {
   // Since suggestion markers are stored unsorted, the quickest way to perform
diff --git a/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImpl.h b/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImpl.h
index 0149e64..29d0e73 100644
--- a/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImpl.h
+++ b/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImpl.h
@@ -35,7 +35,8 @@
 
   bool MoveMarkers(int length, DocumentMarkerList* dst_list) final;
   bool RemoveMarkers(unsigned start_offset, int length) final;
-  bool ShiftMarkers(unsigned offset,
+  bool ShiftMarkers(const String& node_text,
+                    unsigned offset,
                     unsigned old_length,
                     unsigned new_length) final;
 
diff --git a/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImplTest.cpp b/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImplTest.cpp
index 8fa41f6..212b46d78 100644
--- a/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImplTest.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/SuggestionMarkerListImplTest.cpp
@@ -261,7 +261,7 @@
   marker_list_->Add(CreateMarker(0, 10));
   marker_list_->Add(CreateMarker(20, 30));
 
-  EXPECT_TRUE(marker_list_->ShiftMarkers(15, 20, 0));
+  EXPECT_TRUE(marker_list_->ShiftMarkers("", 15, 20, 0));
 
   DocumentMarkerVector markers = marker_list_->GetMarkers();
   std::sort(markers.begin(), markers.end(), compare_markers);
diff --git a/third_party/WebKit/Source/core/editing/markers/TextMatchMarkerListImpl.cpp b/third_party/WebKit/Source/core/editing/markers/TextMatchMarkerListImpl.cpp
index 4adeb5f..1db3b60a 100644
--- a/third_party/WebKit/Source/core/editing/markers/TextMatchMarkerListImpl.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/TextMatchMarkerListImpl.cpp
@@ -59,7 +59,8 @@
                                                  length);
 }
 
-bool TextMatchMarkerListImpl::ShiftMarkers(unsigned offset,
+bool TextMatchMarkerListImpl::ShiftMarkers(const String&,
+                                           unsigned offset,
                                            unsigned old_length,
                                            unsigned new_length) {
   return DocumentMarkerListEditor::ShiftMarkersContentDependent(
diff --git a/third_party/WebKit/Source/core/editing/markers/TextMatchMarkerListImpl.h b/third_party/WebKit/Source/core/editing/markers/TextMatchMarkerListImpl.h
index 084052c..4a953f6 100644
--- a/third_party/WebKit/Source/core/editing/markers/TextMatchMarkerListImpl.h
+++ b/third_party/WebKit/Source/core/editing/markers/TextMatchMarkerListImpl.h
@@ -35,7 +35,8 @@
 
   bool MoveMarkers(int length, DocumentMarkerList* dst_list) final;
   bool RemoveMarkers(unsigned start_offset, int length) final;
-  bool ShiftMarkers(unsigned offset,
+  bool ShiftMarkers(const String& node_text,
+                    unsigned offset,
                     unsigned old_length,
                     unsigned new_length) final;
   DECLARE_VIRTUAL_TRACE();
diff --git a/third_party/WebKit/Source/core/exported/WebPluginContainerImpl.cpp b/third_party/WebKit/Source/core/exported/WebPluginContainerImpl.cpp
index e79abb2..2df5552c 100644
--- a/third_party/WebKit/Source/core/exported/WebPluginContainerImpl.cpp
+++ b/third_party/WebKit/Source/core/exported/WebPluginContainerImpl.cpp
@@ -401,6 +401,11 @@
   return web_plugin_->ExecuteEditCommand(name, value);
 }
 
+bool WebPluginContainerImpl::SupportsCommand(const WebString& name) {
+  return name == "Copy" || name == "Cut" || name == "Paste" ||
+         name == "PasteAndMatchStyle";
+}
+
 WebElement WebPluginContainerImpl::GetElement() {
   return WebElement(element_);
 }
diff --git a/third_party/WebKit/Source/core/exported/WebPluginContainerImpl.h b/third_party/WebKit/Source/core/exported/WebPluginContainerImpl.h
index 7508e1a0..4e517327 100644
--- a/third_party/WebKit/Source/core/exported/WebPluginContainerImpl.h
+++ b/third_party/WebKit/Source/core/exported/WebPluginContainerImpl.h
@@ -74,6 +74,9 @@
                                         WebPlugin* web_plugin) {
     return new WebPluginContainerImpl(element, web_plugin);
   }
+  // Check if plugins support a given command |name|.
+  static bool SupportsCommand(const WebString& name);
+
   ~WebPluginContainerImpl() override;
 
   // PluginView methods
diff --git a/third_party/WebKit/Source/core/exported/WebPluginContainerTest.cpp b/third_party/WebKit/Source/core/exported/WebPluginContainerTest.cpp
index 49169c9..5ba0f825 100644
--- a/third_party/WebKit/Source/core/exported/WebPluginContainerTest.cpp
+++ b/third_party/WebKit/Source/core/exported/WebPluginContainerTest.cpp
@@ -147,6 +147,9 @@
 
   bool HasSelection() const override { return true; }
   bool CanEditText() const override { return true; }
+  bool ExecuteEditCommand(const WebString& name) {
+    return ExecuteEditCommand(name, WebString());
+  }
   bool ExecuteEditCommand(const WebString& name,
                           const WebString& value) override {
     if (name == "Cut") {
@@ -657,6 +660,87 @@
   EXPECT_TRUE(test_plugin->IsPasteCalled());
 }
 
+TEST_F(WebPluginContainerTest, CutFromContextMenu) {
+  RegisterMockedURL("plugin_container.html");
+  // Must outlive |web_view_helper|.
+  TestPluginWebFrameClient plugin_web_frame_client;
+  FrameTestHelpers::WebViewHelper web_view_helper;
+
+  // Use TestPluginWithEditableText for testing "Cut".
+  plugin_web_frame_client.SetHasEditableText(true);
+
+  WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
+      base_url_ + "plugin_container.html", &plugin_web_frame_client);
+  EnablePlugins(web_view, WebSize(300, 300));
+
+  WebElement plugin_container_one_element =
+      web_view->MainFrameImpl()->GetDocument().GetElementById(
+          WebString::FromUTF8("translated-plugin"));
+
+  WebPlugin* plugin =
+      ToWebPluginContainerImpl(plugin_container_one_element.PluginContainer())
+          ->Plugin();
+  TestPluginWithEditableText* test_plugin =
+      static_cast<TestPluginWithEditableText*>(plugin);
+
+  ExecuteContextMenuCommand(web_view, "Cut");
+  EXPECT_TRUE(test_plugin->IsCutCalled());
+}
+
+TEST_F(WebPluginContainerTest, PasteFromContextMenu) {
+  RegisterMockedURL("plugin_container.html");
+  // Must outlive |web_view_helper|.
+  TestPluginWebFrameClient plugin_web_frame_client;
+  FrameTestHelpers::WebViewHelper web_view_helper;
+
+  // Use TestPluginWithEditableText for testing "Paste".
+  plugin_web_frame_client.SetHasEditableText(true);
+
+  WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
+      base_url_ + "plugin_container.html", &plugin_web_frame_client);
+  EnablePlugins(web_view, WebSize(300, 300));
+
+  WebElement plugin_container_one_element =
+      web_view->MainFrameImpl()->GetDocument().GetElementById(
+          WebString::FromUTF8("translated-plugin"));
+
+  WebPlugin* plugin =
+      ToWebPluginContainerImpl(plugin_container_one_element.PluginContainer())
+          ->Plugin();
+  TestPluginWithEditableText* test_plugin =
+      static_cast<TestPluginWithEditableText*>(plugin);
+
+  ExecuteContextMenuCommand(web_view, "Paste");
+  EXPECT_TRUE(test_plugin->IsPasteCalled());
+}
+
+TEST_F(WebPluginContainerTest, PasteAndMatchStyleFromContextMenu) {
+  RegisterMockedURL("plugin_container.html");
+  // Must outlive |web_view_helper|.
+  TestPluginWebFrameClient plugin_web_frame_client;
+  FrameTestHelpers::WebViewHelper web_view_helper;
+
+  // Use TestPluginWithEditableText for testing "Paste".
+  plugin_web_frame_client.SetHasEditableText(true);
+
+  WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
+      base_url_ + "plugin_container.html", &plugin_web_frame_client);
+  EnablePlugins(web_view, WebSize(300, 300));
+
+  WebElement plugin_container_one_element =
+      web_view->MainFrameImpl()->GetDocument().GetElementById(
+          WebString::FromUTF8("translated-plugin"));
+
+  WebPlugin* plugin =
+      ToWebPluginContainerImpl(plugin_container_one_element.PluginContainer())
+          ->Plugin();
+  TestPluginWithEditableText* test_plugin =
+      static_cast<TestPluginWithEditableText*>(plugin);
+
+  ExecuteContextMenuCommand(web_view, "PasteAndMatchStyle");
+  EXPECT_TRUE(test_plugin->IsPasteCalled());
+}
+
 // A class to facilitate testing that events are correctly received by plugins.
 class EventTestPlugin : public FakeWebPlugin {
  public:
diff --git a/third_party/WebKit/Source/core/exported/WebViewImpl.cpp b/third_party/WebKit/Source/core/exported/WebViewImpl.cpp
index 5c52e7a..41353c58 100644
--- a/third_party/WebKit/Source/core/exported/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/core/exported/WebViewImpl.cpp
@@ -1187,7 +1187,7 @@
   if ((is_unmodified_menu_key &&
        event.GetType() == kContextMenuKeyTriggeringEventType) ||
       (is_shift_f10 && event.GetType() == kShiftF10TriggeringEventType)) {
-    SendContextMenuEvent(event);
+    SendContextMenuEvent();
     return WebInputEventResult::kHandledSystem;
   }
 #endif  // !defined(OS_MACOSX)
@@ -1616,8 +1616,7 @@
 
 #if !defined(OS_MACOSX)
 // Mac has no way to open a context menu based on a keyboard event.
-WebInputEventResult WebViewImpl::SendContextMenuEvent(
-    const WebKeyboardEvent& event) {
+WebInputEventResult WebViewImpl::SendContextMenuEvent() {
   // The contextMenuController() holds onto the last context menu that was
   // popped up on the page until a new one is created. We need to clear
   // this menu before propagating the event through the DOM so that we can
@@ -1642,8 +1641,7 @@
   }
 }
 #else
-WebInputEventResult WebViewImpl::SendContextMenuEvent(
-    const WebKeyboardEvent& event) {
+WebInputEventResult WebViewImpl::SendContextMenuEvent() {
   return WebInputEventResult::kNotHandled;
 }
 #endif
diff --git a/third_party/WebKit/Source/core/exported/WebViewImpl.h b/third_party/WebKit/Source/core/exported/WebViewImpl.h
index e9f8485..dde3c42 100644
--- a/third_party/WebKit/Source/core/exported/WebViewImpl.h
+++ b/third_party/WebKit/Source/core/exported/WebViewImpl.h
@@ -325,7 +325,7 @@
   // wParam, LPARAM lParam) in webkit\webkit\win\WebView.cpp. The only
   // significant change in this function is the code to convert from a
   // Keyboard event to the Right Mouse button down event.
-  WebInputEventResult SendContextMenuEvent(const WebKeyboardEvent&);
+  WebInputEventResult SendContextMenuEvent();
 
   void ShowContextMenuAtPoint(float x, float y, ContextMenuProvider*);
 
diff --git a/third_party/WebKit/Source/core/frame/DOMWindow.cpp b/third_party/WebKit/Source/core/frame/DOMWindow.cpp
index 5a43c8c..84dd3c7 100644
--- a/third_party/WebKit/Source/core/frame/DOMWindow.cpp
+++ b/third_party/WebKit/Source/core/frame/DOMWindow.cpp
@@ -35,7 +35,6 @@
 DOMWindow::DOMWindow(Frame& frame)
     : frame_(frame),
       window_proxy_manager_(frame.GetWindowProxyManager()),
-      location_(this, nullptr),
       window_is_closing_(false) {}
 
 DOMWindow::~DOMWindow() {
diff --git a/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp b/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
index f61635b..ee7e241 100644
--- a/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
@@ -249,15 +249,12 @@
 
 LocalDOMWindow::LocalDOMWindow(LocalFrame& frame)
     : DOMWindow(frame),
-      document_(this, nullptr),
       visualViewport_(DOMVisualViewport::Create(this)),
       unused_preloads_timer_(
           TaskRunnerHelper::Get(TaskType::kUnspecedTimer, &frame),
           this,
           &LocalDOMWindow::WarnUnusedPreloads),
-      should_print_when_finished_loading_(false),
-      custom_elements_(this, nullptr),
-      modulator_(this, nullptr) {}
+      should_print_when_finished_loading_(false) {}
 
 void LocalDOMWindow::ClearDocument() {
   if (!document_)
diff --git a/third_party/WebKit/Source/core/frame/WebFrameWidgetImpl.cpp b/third_party/WebKit/Source/core/frame/WebFrameWidgetImpl.cpp
index 88b3c20..eab267d 100644
--- a/third_party/WebKit/Source/core/frame/WebFrameWidgetImpl.cpp
+++ b/third_party/WebKit/Source/core/frame/WebFrameWidgetImpl.cpp
@@ -987,7 +987,7 @@
   if ((is_unmodified_menu_key &&
        event.GetType() == kContextMenuKeyTriggeringEventType) ||
       (is_shift_f10 && event.GetType() == kShiftF10TriggeringEventType)) {
-    View()->SendContextMenuEvent(event);
+    View()->SendContextMenuEvent();
     return WebInputEventResult::kHandledSystem;
   }
 #endif  // !defined(OS_MACOSX)
diff --git a/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.cpp
index 7ddba14..e652c8c5 100644
--- a/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.cpp
+++ b/third_party/WebKit/Source/core/frame/WebLocalFrameImpl.cpp
@@ -1033,7 +1033,10 @@
     command = command.Substring(0, command.length() - 1);
 
   Node* plugin_lookup_context_node =
-      context_menu_node_ && name == "Copy" ? context_menu_node_ : nullptr;
+      context_menu_node_ && WebPluginContainerImpl::SupportsCommand(name)
+          ? context_menu_node_
+          : nullptr;
+
   WebPluginContainerImpl* plugin_container =
       GetFrame()->GetWebPluginContainer(plugin_lookup_context_node);
   if (plugin_container && plugin_container->ExecuteEditCommand(name))
diff --git a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
index 0af7065..ab5533e 100644
--- a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
@@ -136,7 +136,6 @@
       ContextLifecycleObserver(&document),
       PageVisibilityObserver(document.GetPage()),
       size_(kDefaultWidth, kDefaultHeight),
-      context_(this, nullptr),
       ignore_reset_(false),
       externally_allocated_memory_(0),
       origin_clean_(true),
diff --git a/third_party/WebKit/Source/core/html/HTMLLinkElement.cpp b/third_party/WebKit/Source/core/html/HTMLLinkElement.cpp
index 058fa55..3e5451d 100644
--- a/third_party/WebKit/Source/core/html/HTMLLinkElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLLinkElement.cpp
@@ -53,7 +53,7 @@
     : HTMLElement(linkTag, document),
       link_loader_(LinkLoader::Create(this)),
       sizes_(DOMTokenList::Create(*this, HTMLNames::sizesAttr)),
-      rel_list_(this, RelList::Create(this)),
+      rel_list_(RelList::Create(this)),
       created_by_parser_(created_by_parser) {}
 
 HTMLLinkElement* HTMLLinkElement::Create(Document& document,
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
index 08d68c7..ae921cd 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
@@ -498,9 +498,8 @@
       playing_remotely_(false),
       in_overlay_fullscreen_video_(false),
       mostly_filling_viewport_(false),
-      audio_tracks_(this, AudioTrackList::Create(*this)),
-      video_tracks_(this, VideoTrackList::Create(*this)),
-      text_tracks_(this, nullptr),
+      audio_tracks_(AudioTrackList::Create(*this)),
+      video_tracks_(VideoTrackList::Create(*this)),
       audio_source_node_(nullptr),
       autoplay_policy_(new AutoplayPolicy(this)),
       remote_playback_client_(nullptr),
diff --git a/third_party/WebKit/Source/core/html/HTMLScriptElement.cpp b/third_party/WebKit/Source/core/html/HTMLScriptElement.cpp
index 2787cca..da2d7e2d 100644
--- a/third_party/WebKit/Source/core/html/HTMLScriptElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLScriptElement.cpp
@@ -45,8 +45,7 @@
                                             bool already_started,
                                             bool created_during_document_write)
     : HTMLElement(scriptTag, document),
-      loader_(this,
-              InitializeScriptLoader(was_inserted_by_parser,
+      loader_(InitializeScriptLoader(was_inserted_by_parser,
                                      already_started,
                                      created_during_document_write)) {}
 
diff --git a/third_party/WebKit/Source/core/html/HTMLTemplateElement.cpp b/third_party/WebKit/Source/core/html/HTMLTemplateElement.cpp
index d91e600..b4a0c79 100644
--- a/third_party/WebKit/Source/core/html/HTMLTemplateElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLTemplateElement.cpp
@@ -39,7 +39,7 @@
 using namespace HTMLNames;
 
 inline HTMLTemplateElement::HTMLTemplateElement(Document& document)
-    : HTMLElement(templateTag, document), content_(this, nullptr) {}
+    : HTMLElement(templateTag, document) {}
 
 DEFINE_NODE_FACTORY(HTMLTemplateElement)
 
diff --git a/third_party/WebKit/Source/core/html/custom/CustomElementReactionStack.cpp b/third_party/WebKit/Source/core/html/custom/CustomElementReactionStack.cpp
index 83d9b424..d507666 100644
--- a/third_party/WebKit/Source/core/html/custom/CustomElementReactionStack.cpp
+++ b/third_party/WebKit/Source/core/html/custom/CustomElementReactionStack.cpp
@@ -77,7 +77,7 @@
   CustomElementReactionQueue* reactions = map_.at(element);
   if (!reactions) {
     reactions = new CustomElementReactionQueue();
-    map_.insert(TraceWrapperMember<Element>(this, element), reactions);
+    map_.insert(element, reactions);
   }
 
   reactions->Add(reaction);
diff --git a/third_party/WebKit/Source/core/html/custom/CustomElementRegistry.cpp b/third_party/WebKit/Source/core/html/custom/CustomElementRegistry.cpp
index a405197..eafc297 100644
--- a/third_party/WebKit/Source/core/html/custom/CustomElementRegistry.cpp
+++ b/third_party/WebKit/Source/core/html/custom/CustomElementRegistry.cpp
@@ -196,8 +196,7 @@
   CustomElementDefinition* definition = builder.Build(descriptor, id);
   CHECK(!exception_state.HadException());
   CHECK(definition->Descriptor() == descriptor);
-  definitions_.emplace_back(
-      TraceWrapperMember<CustomElementDefinition>(this, definition));
+  definitions_.emplace_back(definition);
   NameIdMap::AddResult result = name_id_map_.insert(descriptor.GetName(), id);
   CHECK(result.is_new_entry);
 
diff --git a/third_party/WebKit/Source/core/html/imports/HTMLImportTreeRoot.cpp b/third_party/WebKit/Source/core/html/imports/HTMLImportTreeRoot.cpp
index 47b69713..1443ff3 100644
--- a/third_party/WebKit/Source/core/html/imports/HTMLImportTreeRoot.cpp
+++ b/third_party/WebKit/Source/core/html/imports/HTMLImportTreeRoot.cpp
@@ -17,7 +17,7 @@
 
 HTMLImportTreeRoot::HTMLImportTreeRoot(Document* document)
     : HTMLImport(HTMLImport::kSync),
-      document_(this, document),
+      document_(document),
       recalc_timer_(
           TaskRunnerHelper::Get(TaskType::kUnspecedTimer, document->GetFrame()),
           this,
diff --git a/third_party/WebKit/Source/core/html/imports/HTMLImportsController.cpp b/third_party/WebKit/Source/core/html/imports/HTMLImportsController.cpp
index c3625ef..b353969 100644
--- a/third_party/WebKit/Source/core/html/imports/HTMLImportsController.cpp
+++ b/third_party/WebKit/Source/core/html/imports/HTMLImportsController.cpp
@@ -41,7 +41,7 @@
 namespace blink {
 
 HTMLImportsController::HTMLImportsController(Document& master)
-    : root_(this, HTMLImportTreeRoot::Create(&master)) {
+    : root_(HTMLImportTreeRoot::Create(&master)) {
   UseCounter::Count(master, WebFeature::kHTMLImports);
 }
 
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp
index 29d1842..4a3dde3 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp
@@ -131,7 +131,6 @@
       tokenizer_(sync_policy == kForceSynchronousParsing
                      ? HTMLTokenizer::Create(options_)
                      : nullptr),
-      script_runner_(this, nullptr),
       loading_task_runner_(
           TaskRunnerHelper::Get(TaskType::kNetworking, &document)),
       parser_scheduler_(
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.cpp b/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.cpp
index e7e992f..b4e5b028 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.cpp
@@ -159,10 +159,7 @@
     HTMLParserReentryPermit* reentry_permit,
     Document* document,
     HTMLParserScriptRunnerHost* host)
-    : reentry_permit_(reentry_permit),
-      document_(document),
-      host_(host),
-      parser_blocking_script_(this, nullptr) {
+    : reentry_permit_(reentry_permit), document_(document), host_(host) {
   DCHECK(host_);
 }
 
@@ -553,8 +550,7 @@
   // "Add the element to the end of the list of scripts that will execute
   //  when the document has finished parsing associated with the Document
   //  of the parser that created the element."
-  scripts_to_execute_after_parsing_.push_back(
-      TraceWrapperMember<PendingScript>(this, pending_script));
+  scripts_to_execute_after_parsing_.push_back(pending_script);
 }
 
 // The initial steps for 'An end tag whose tag name is "script"'
diff --git a/third_party/WebKit/Source/core/html/track/TextTrack.cpp b/third_party/WebKit/Source/core/html/track/TextTrack.cpp
index 7845313..a6f27f7 100644
--- a/third_party/WebKit/Source/core/html/track/TextTrack.cpp
+++ b/third_party/WebKit/Source/core/html/track/TextTrack.cpp
@@ -88,7 +88,6 @@
                      const AtomicString& id,
                      TextTrackType type)
     : TrackBase(WebMediaPlayer::kTextTrack, kind, label, language, id),
-      cues_(this, nullptr),
       active_cues_(nullptr),
       track_list_(nullptr),
       mode_(DisabledKeyword()),
diff --git a/third_party/WebKit/Source/core/html/track/TextTrackCueList.cpp b/third_party/WebKit/Source/core/html/track/TextTrackCueList.cpp
index 43076f5..103bc18c 100644
--- a/third_party/WebKit/Source/core/html/track/TextTrackCueList.cpp
+++ b/third_party/WebKit/Source/core/html/track/TextTrackCueList.cpp
@@ -70,7 +70,7 @@
   if (!list_.IsEmpty() && (index > 0) && (list_[index - 1].Get() == cue))
     return false;
 
-  list_.insert(index, TraceWrapperMember<TextTrackCue>(this, cue));
+  list_.insert(index, cue);
   InvalidateCueIndex(index);
   return true;
 }
diff --git a/third_party/WebKit/Source/core/html/track/TextTrackList.cpp b/third_party/WebKit/Source/core/html/track/TextTrackList.cpp
index 66bdda3..74f8b9e 100644
--- a/third_party/WebKit/Source/core/html/track/TextTrackList.cpp
+++ b/third_party/WebKit/Source/core/html/track/TextTrackList.cpp
@@ -169,13 +169,13 @@
 
 void TextTrackList::Append(TextTrack* track) {
   if (track->TrackType() == TextTrack::kAddTrack) {
-    add_track_tracks_.push_back(TraceWrapperMember<TextTrack>(this, track));
+    add_track_tracks_.push_back(track);
   } else if (track->TrackType() == TextTrack::kTrackElement) {
     // Insert tracks added for <track> element in tree order.
     size_t index = ToLoadableTextTrack(track)->TrackElementIndex();
-    element_tracks_.insert(index, TraceWrapperMember<TextTrack>(this, track));
+    element_tracks_.insert(index, track);
   } else if (track->TrackType() == TextTrack::kInBand) {
-    inband_tracks_.push_back(TraceWrapperMember<TextTrack>(this, track));
+    inband_tracks_.push_back(track);
   } else {
     NOTREACHED();
   }
diff --git a/third_party/WebKit/Source/core/html/track/TrackListBase.h b/third_party/WebKit/Source/core/html/track/TrackListBase.h
index 9eb7734..df11e6d 100644
--- a/third_party/WebKit/Source/core/html/track/TrackListBase.h
+++ b/third_party/WebKit/Source/core/html/track/TrackListBase.h
@@ -50,7 +50,7 @@
 
   void Add(T* track) {
     track->SetMediaElement(media_element_);
-    tracks_.push_back(TraceWrapperMember<T>(this, track));
+    tracks_.push_back(track);
     ScheduleEvent(TrackEvent::Create(EventTypeNames::addtrack, track));
   }
 
diff --git a/third_party/WebKit/Source/core/input/KeyboardEventManager.cpp b/third_party/WebKit/Source/core/input/KeyboardEventManager.cpp
index 4d7e534..bd9a08b 100644
--- a/third_party/WebKit/Source/core/input/KeyboardEventManager.cpp
+++ b/third_party/WebKit/Source/core/input/KeyboardEventManager.cpp
@@ -192,14 +192,12 @@
 
   // To be meaningful enough to indicate user intention, a keyboard event needs
   // - not to be a modifier event
-  // - not to be a browser shortcut
   // https://crbug.com/709765
   bool is_modifier =
       Platform::Current()->IsDomKeyForModifier(initial_key_event.dom_key);
-  bool is_browser_shortcut = initial_key_event.is_browser_shortcut;
 
   std::unique_ptr<UserGestureIndicator> gesture_indicator;
-  if (!is_modifier && !is_browser_shortcut) {
+  if (!is_modifier) {
     gesture_indicator.reset(new UserGestureIndicator(
         UserGestureToken::Create(frame_->GetDocument())));
   }
diff --git a/third_party/WebKit/Source/core/inspector/InspectorLayerTreeAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorLayerTreeAgent.cpp
index 934ae43..aec02ab 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorLayerTreeAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorLayerTreeAgent.cpp
@@ -48,6 +48,7 @@
 #include "core/paint/compositing/PaintLayerCompositor.h"
 #include "platform/geometry/IntRect.h"
 #include "platform/graphics/CompositingReasons.h"
+#include "platform/graphics/CompositorElementId.h"
 #include "platform/graphics/GraphicsLayer.h"
 #include "platform/graphics/PictureSnapshot.h"
 #include "platform/transforms/TransformationMatrix.h"
@@ -120,8 +121,22 @@
   return scroll_rects->length() ? std::move(scroll_rects) : nullptr;
 }
 
+// TODO(flackr): We should be getting the sticky position constraints from the
+// property tree once blink is able to access them. https://crbug.com/754339
+static GraphicsLayer* FindLayerByElementId(GraphicsLayer* root,
+                                           CompositorElementId element_id) {
+  if (root->PlatformLayer()->GetElementId() == element_id)
+    return root;
+  for (size_t i = 0, size = root->Children().size(); i < size; ++i) {
+    if (GraphicsLayer* layer =
+            FindLayerByElementId(root->Children()[i], element_id))
+      return layer;
+  }
+  return nullptr;
+}
+
 static std::unique_ptr<protocol::LayerTree::StickyPositionConstraint>
-BuildStickyInfoForLayer(WebLayer* layer) {
+BuildStickyInfoForLayer(GraphicsLayer* root, WebLayer* layer) {
   WebLayerStickyPositionConstraint constraints =
       layer->StickyPositionConstraint();
   if (!constraints.is_sticky)
@@ -140,19 +155,26 @@
               .setStickyBoxRect(std::move(sticky_box_rect))
               .setContainingBlockRect(std::move(containing_block_rect))
               .build();
-  if (constraints.nearest_layer_shifting_sticky_box >= 0) {
-    constraints_obj->setNearestLayerShiftingStickyBox(
-        String::Number(constraints.nearest_layer_shifting_sticky_box));
+  if (constraints.nearest_element_shifting_sticky_box) {
+    constraints_obj->setNearestLayerShiftingStickyBox(String::Number(
+        FindLayerByElementId(root,
+                             constraints.nearest_element_shifting_sticky_box)
+            ->PlatformLayer()
+            ->Id()));
   }
-  if (constraints.nearest_layer_shifting_containing_block >= 0) {
-    constraints_obj->setNearestLayerShiftingContainingBlock(
-        String::Number(constraints.nearest_layer_shifting_containing_block));
+  if (constraints.nearest_element_shifting_containing_block) {
+    constraints_obj->setNearestLayerShiftingContainingBlock(String::Number(
+        FindLayerByElementId(
+            root, constraints.nearest_element_shifting_containing_block)
+            ->PlatformLayer()
+            ->Id()));
   }
 
   return constraints_obj;
 }
 
 static std::unique_ptr<protocol::LayerTree::Layer> BuildObjectForLayer(
+    GraphicsLayer* root,
     GraphicsLayer* graphics_layer,
     int node_id,
     bool report_wheel_event_listeners) {
@@ -203,7 +225,7 @@
   if (scroll_rects)
     layer_object->setScrollRects(std::move(scroll_rects));
   std::unique_ptr<protocol::LayerTree::StickyPositionConstraint> sticky_info =
-      BuildStickyInfoForLayer(web_layer);
+      BuildStickyInfoForLayer(root, web_layer);
   if (sticky_info)
     layer_object->setStickyPositionConstraint(std::move(sticky_info));
   return layer_object;
@@ -320,19 +342,19 @@
 }
 
 void InspectorLayerTreeAgent::GatherGraphicsLayers(
-    GraphicsLayer* root,
+    GraphicsLayer* layer,
     HashMap<int, int>& layer_id_to_node_id_map,
     std::unique_ptr<Array<protocol::LayerTree::Layer>>& layers,
     bool has_wheel_event_handlers,
     int scrolling_layer_id) {
-  if (client_->IsInspectorLayer(root))
+  if (client_->IsInspectorLayer(layer))
     return;
-  int layer_id = root->PlatformLayer()->Id();
+  int layer_id = layer->PlatformLayer()->Id();
   layers->addItem(BuildObjectForLayer(
-      root, layer_id_to_node_id_map.at(layer_id),
+      RootGraphicsLayer(), layer, layer_id_to_node_id_map.at(layer_id),
       has_wheel_event_handlers && layer_id == scrolling_layer_id));
-  for (size_t i = 0, size = root->Children().size(); i < size; ++i)
-    GatherGraphicsLayers(root->Children()[i], layer_id_to_node_id_map, layers,
+  for (size_t i = 0, size = layer->Children().size(); i < size; ++i)
+    GatherGraphicsLayers(layer->Children()[i], layer_id_to_node_id_map, layers,
                          has_wheel_event_handlers, scrolling_layer_id);
 }
 
diff --git a/third_party/WebKit/Source/core/intersection_observer/ElementIntersectionObserverData.cpp b/third_party/WebKit/Source/core/intersection_observer/ElementIntersectionObserverData.cpp
index 0996438..b3a337e 100644
--- a/third_party/WebKit/Source/core/intersection_observer/ElementIntersectionObserverData.cpp
+++ b/third_party/WebKit/Source/core/intersection_observer/ElementIntersectionObserverData.cpp
@@ -34,9 +34,7 @@
 void ElementIntersectionObserverData::AddObservation(
     IntersectionObservation& observation) {
   DCHECK(observation.Observer());
-  intersection_observations_.insert(
-      TraceWrapperMember<IntersectionObserver>(this, observation.Observer()),
-      &observation);
+  intersection_observations_.insert(observation.Observer(), &observation);
 }
 
 void ElementIntersectionObserverData::RemoveObservation(
diff --git a/third_party/WebKit/Source/core/intersection_observer/IntersectionObserver.cpp b/third_party/WebKit/Source/core/intersection_observer/IntersectionObserver.cpp
index 49e94415..e075155 100644
--- a/third_party/WebKit/Source/core/intersection_observer/IntersectionObserver.cpp
+++ b/third_party/WebKit/Source/core/intersection_observer/IntersectionObserver.cpp
@@ -177,7 +177,7 @@
     Element* root,
     const Vector<Length>& root_margin,
     const Vector<float>& thresholds)
-    : delegate_(this, &delegate),
+    : delegate_(&delegate),
       root_(root),
       thresholds_(thresholds),
       top_margin_(kFixed),
diff --git a/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinker.cpp b/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinker.cpp
index 0af80648..f054d9ab 100644
--- a/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinker.cpp
+++ b/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinker.cpp
@@ -83,8 +83,7 @@
       registry_(registry),
       client_(client),
       ancestor_list_with_url_(ancestor_list_with_url),
-      level_(level),
-      module_script_(this, nullptr) {
+      level_(level) {
   CHECK(modulator);
   CHECK(reached_url_set_);
   CHECK(registry);
diff --git a/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinkerRegistry.cpp b/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinkerRegistry.cpp
index 9fb1ebb..7bf7bfa 100644
--- a/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinkerRegistry.cpp
+++ b/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinkerRegistry.cpp
@@ -29,8 +29,7 @@
   ModuleTreeLinker* fetcher = ModuleTreeLinker::Fetch(
       request, ancestor_list, level, modulator, reached_url_set, this, client);
   DCHECK(fetcher->IsFetching());
-  active_tree_linkers_.insert(
-      TraceWrapperMember<ModuleTreeLinker>(this, fetcher));
+  active_tree_linkers_.insert(fetcher);
   return fetcher;
 }
 
@@ -41,8 +40,7 @@
   ModuleTreeLinker* fetcher = ModuleTreeLinker::FetchDescendantsForInlineScript(
       module_script, modulator, this, client);
   DCHECK(fetcher->IsFetching());
-  active_tree_linkers_.insert(
-      TraceWrapperMember<ModuleTreeLinker>(this, fetcher));
+  active_tree_linkers_.insert(fetcher);
   return fetcher;
 }
 
@@ -50,8 +48,7 @@
     ModuleTreeLinker* fetcher) {
   DCHECK(fetcher->HasFinished());
 
-  auto it = active_tree_linkers_.find(
-      TraceWrapperMember<ModuleTreeLinker>(this, fetcher));
+  auto it = active_tree_linkers_.find(fetcher);
   DCHECK_NE(it, active_tree_linkers_.end());
   active_tree_linkers_.erase(it);
 }
diff --git a/third_party/WebKit/Source/core/mojo/MojoWatcher.cpp b/third_party/WebKit/Source/core/mojo/MojoWatcher.cpp
index 6033a80..902c00b 100644
--- a/third_party/WebKit/Source/core/mojo/MojoWatcher.cpp
+++ b/third_party/WebKit/Source/core/mojo/MojoWatcher.cpp
@@ -73,7 +73,7 @@
 MojoWatcher::MojoWatcher(ExecutionContext* context, MojoWatchCallback* callback)
     : ContextLifecycleObserver(context),
       task_runner_(TaskRunnerHelper::Get(TaskType::kUnspecedTimer, context)),
-      callback_(this, callback) {}
+      callback_(callback) {}
 
 MojoResult MojoWatcher::Watch(mojo::Handle handle,
                               const MojoHandleSignals& signals_dict) {
diff --git a/third_party/WebKit/Source/core/page/ContextMenuClient.cpp b/third_party/WebKit/Source/core/page/ContextMenuClient.cpp
index 36e6717..f2f92c7a9 100644
--- a/third_party/WebKit/Source/core/page/ContextMenuClient.cpp
+++ b/third_party/WebKit/Source/core/page/ContextMenuClient.cpp
@@ -302,6 +302,16 @@
           data.selected_text = text;
           data.edit_flags |= WebContextMenuData::kCanCopy;
         }
+        bool plugin_can_edit_text = plugin->Plugin()->CanEditText();
+        if (plugin_can_edit_text) {
+          data.is_editable = true;
+          if (!!(data.edit_flags & WebContextMenuData::kCanCopy))
+            data.edit_flags |= WebContextMenuData::kCanCut;
+          data.edit_flags |= WebContextMenuData::kCanPaste;
+          // TODO(bug 753216): Implement "SelectAll" command and enable when
+          // focus is within an editable text area.
+          data.edit_flags &= ~WebContextMenuData::kCanSelectAll;
+        }
         data.edit_flags &= ~WebContextMenuData::kCanTranslate;
         data.link_url = plugin->Plugin()->LinkAtPosition(data.mouse_position);
         if (plugin->Plugin()->SupportsPaginatedPrint())
@@ -313,7 +323,9 @@
         data.media_flags |= WebContextMenuData::kMediaCanSave;
 
         // Add context menu commands that are supported by the plugin.
-        if (plugin->Plugin()->CanRotateView())
+        // Only show rotate view options if focus is not in an editable text
+        // area.
+        if (!plugin_can_edit_text && plugin->Plugin()->CanRotateView())
           data.media_flags |= WebContextMenuData::kMediaCanRotate;
       }
     }
diff --git a/third_party/WebKit/Source/core/paint/BoxPainterBase.cpp b/third_party/WebKit/Source/core/paint/BoxPainterBase.cpp
index 6d2205c..6030f85 100644
--- a/third_party/WebKit/Source/core/paint/BoxPainterBase.cpp
+++ b/third_party/WebKit/Source/core/paint/BoxPainterBase.cpp
@@ -378,8 +378,7 @@
   if (geometry.CellUsingContainerBackground())
     return false;
   // Complex cases not handled on the fast path.
-  if (!info.is_bottom_layer || !info.is_border_fill ||
-      info.is_clipped_with_local_scrolling)
+  if (!info.is_bottom_layer || !info.is_border_fill)
     return false;
 
   // Transparent layer, nothing to paint.
diff --git a/third_party/WebKit/Source/core/paint/ViewPainter.cpp b/third_party/WebKit/Source/core/paint/ViewPainter.cpp
index 64f0ffa..b3fb6d7 100644
--- a/third_party/WebKit/Source/core/paint/ViewPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/ViewPainter.cpp
@@ -60,7 +60,8 @@
   GraphicsContext& context = paint_info.context;
 
   // The background rect always includes at least the visible content size.
-  IntRect background_rect(IntRect(layout_view_.ViewRect()));
+  IntRect background_rect(
+      IntRect(layout_view_.OverflowClipRect(LayoutPoint())));
 
   if (!RuntimeEnabledFeatures::RootLayerScrollingEnabled())
     background_rect.Unite(layout_view_.DocumentRect());
diff --git a/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp
index 9a8886b..074a9c2 100644
--- a/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp
+++ b/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp
@@ -338,21 +338,19 @@
       constraints.NearestStickyLayerShiftingStickyBox();
   if (sticky_box_shifting_ancestor &&
       sticky_box_shifting_ancestor->GetCompositedLayerMapping()) {
-    web_constraint.nearest_layer_shifting_sticky_box =
+    web_constraint.nearest_element_shifting_sticky_box =
         sticky_box_shifting_ancestor->GetCompositedLayerMapping()
             ->MainGraphicsLayer()
-            ->PlatformLayer()
-            ->Id();
+            ->GetElementId();
   }
   PaintLayer* containing_block_shifting_ancestor =
       constraints.NearestStickyLayerShiftingContainingBlock();
   if (containing_block_shifting_ancestor &&
       containing_block_shifting_ancestor->GetCompositedLayerMapping()) {
-    web_constraint.nearest_layer_shifting_containing_block =
+    web_constraint.nearest_element_shifting_containing_block =
         containing_block_shifting_ancestor->GetCompositedLayerMapping()
             ->MainGraphicsLayer()
-            ->PlatformLayer()
-            ->Id();
+            ->GetElementId();
   }
 
   graphics_layer_->SetStickyPositionConstraint(web_constraint);
diff --git a/third_party/WebKit/Source/core/resize_observer/ResizeObserver.cpp b/third_party/WebKit/Source/core/resize_observer/ResizeObserver.cpp
index cc9e5ad..25ecb05 100644
--- a/third_party/WebKit/Source/core/resize_observer/ResizeObserver.cpp
+++ b/third_party/WebKit/Source/core/resize_observer/ResizeObserver.cpp
@@ -24,7 +24,7 @@
 
 ResizeObserver::ResizeObserver(ResizeObserverCallback* callback,
                                Document& document)
-    : callback_(this, callback),
+    : callback_(callback),
       skipped_observations_(false),
       element_size_changed_(false) {
   DCHECK(callback_);
@@ -33,8 +33,7 @@
 }
 
 ResizeObserver::ResizeObserver(Delegate* delegate, Document& document)
-    : callback_(this, nullptr),
-      delegate_(delegate),
+    : delegate_(delegate),
       skipped_observations_(false),
       element_size_changed_(false) {
   DCHECK(delegate_);
diff --git a/third_party/WebKit/Source/core/svg/SVGMatrixTearOff.cpp b/third_party/WebKit/Source/core/svg/SVGMatrixTearOff.cpp
index 1497831..8397b698 100644
--- a/third_party/WebKit/Source/core/svg/SVGMatrixTearOff.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGMatrixTearOff.cpp
@@ -38,10 +38,10 @@
 namespace blink {
 
 SVGMatrixTearOff::SVGMatrixTearOff(const AffineTransform& static_value)
-    : static_value_(static_value), context_transform_(this, nullptr) {}
+    : static_value_(static_value) {}
 
 SVGMatrixTearOff::SVGMatrixTearOff(SVGTransformTearOff* transform)
-    : context_transform_(this, transform) {
+    : context_transform_(transform) {
   DCHECK(transform);
 }
 
diff --git a/third_party/WebKit/Source/core/svg/SVGScriptElement.cpp b/third_party/WebKit/Source/core/svg/SVGScriptElement.cpp
index 41b555a..35c8f35 100644
--- a/third_party/WebKit/Source/core/svg/SVGScriptElement.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGScriptElement.cpp
@@ -37,8 +37,7 @@
                                           bool already_started)
     : SVGElement(SVGNames::scriptTag, document),
       SVGURIReference(this),
-      loader_(this,
-              InitializeScriptLoader(was_inserted_by_parser,
+      loader_(InitializeScriptLoader(was_inserted_by_parser,
                                      already_started,
                                      false)) {}
 
diff --git a/third_party/WebKit/Source/core/testing/DeathAwareScriptWrappable.h b/third_party/WebKit/Source/core/testing/DeathAwareScriptWrappable.h
index 743ef0a..02a38e2 100644
--- a/third_party/WebKit/Source/core/testing/DeathAwareScriptWrappable.h
+++ b/third_party/WebKit/Source/core/testing/DeathAwareScriptWrappable.h
@@ -68,17 +68,16 @@
   }
 
   void AddWrappedVectorDependency(DeathAwareScriptWrappable* dependency) {
-    wrapped_vector_dependency_.push_back(Wrapper(this, dependency));
+    wrapped_vector_dependency_.push_back(dependency);
   }
 
   void AddWrappedHashMapDependency(DeathAwareScriptWrappable* key,
                                    DeathAwareScriptWrappable* value) {
-    wrapped_hash_map_dependency_.insert(Wrapper(this, key),
-                                        Wrapper(this, value));
+    wrapped_hash_map_dependency_.insert(key, value);
   }
 
  private:
-  DeathAwareScriptWrappable() : wrapped_dependency_(this, nullptr) {}
+  DeathAwareScriptWrappable() {}
 
   Member<DeathAwareScriptWrappable> raw_dependency_;
   Wrapper wrapped_dependency_;
diff --git a/third_party/WebKit/Source/core/timing/PerformanceObserver.cpp b/third_party/WebKit/Source/core/timing/PerformanceObserver.cpp
index 7efae8a..596f2277 100644
--- a/third_party/WebKit/Source/core/timing/PerformanceObserver.cpp
+++ b/third_party/WebKit/Source/core/timing/PerformanceObserver.cpp
@@ -50,7 +50,7 @@
                                          PerformanceObserverCallback* callback)
     : ContextClient(execution_context),
       execution_context_(execution_context),
-      callback_(this, callback),
+      callback_(callback),
       performance_(performance),
       filter_options_(PerformanceEntry::kInvalid),
       is_registered_(false) {
diff --git a/third_party/WebKit/Source/core/workers/WorkletGlobalScope.cpp b/third_party/WebKit/Source/core/workers/WorkletGlobalScope.cpp
index 788d89e..99e935b 100644
--- a/third_party/WebKit/Source/core/workers/WorkletGlobalScope.cpp
+++ b/third_party/WebKit/Source/core/workers/WorkletGlobalScope.cpp
@@ -26,8 +26,7 @@
                                        WorkerClients* worker_clients)
     : WorkerOrWorkletGlobalScope(isolate, worker_clients),
       url_(url),
-      user_agent_(user_agent),
-      modulator_(this, nullptr) {
+      user_agent_(user_agent) {
   SetSecurityOrigin(std::move(security_origin));
 }
 
diff --git a/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp b/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
index de90dc0..efea764 100644
--- a/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
+++ b/third_party/WebKit/Source/core/xmlhttprequest/XMLHttpRequest.cpp
@@ -286,11 +286,8 @@
     RefPtr<SecurityOrigin> isolated_world_security_origin)
     : SuspendableObject(context),
       timeout_milliseconds_(0),
-      response_blob_(this, nullptr),
       state_(kUnsent),
-      response_document_(this, nullptr),
       length_downloaded_to_file_(0),
-      response_array_buffer_(this, nullptr),
       received_length_(0),
       exception_code_(0),
       progress_event_throttle_(
diff --git a/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletGlobalScope.cpp b/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletGlobalScope.cpp
index 6f94703..bc8a13f 100644
--- a/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletGlobalScope.cpp
+++ b/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletGlobalScope.cpp
@@ -150,13 +150,12 @@
   AnimatorDefinition* definition =
       new AnimatorDefinition(isolate, constructor, animate);
 
-  animator_definitions_.Set(
-      name, TraceWrapperMember<AnimatorDefinition>(this, definition));
+  animator_definitions_.Set(name, definition);
 
   // Immediately instantiate an animator for the registered definition.
   // TODO(majidvp): Remove this once you add alternative way to instantiate
   if (Animator* animator = CreateInstance(name))
-    animators_.push_back(TraceWrapperMember<Animator>(this, animator));
+    animators_.push_back(animator);
 }
 
 Animator* AnimationWorkletGlobalScope::CreateInstance(const String& name) {
diff --git a/third_party/WebKit/Source/modules/compositorworker/Animator.cpp b/third_party/WebKit/Source/modules/compositorworker/Animator.cpp
index 0fa9dc6..373b22f3 100644
--- a/third_party/WebKit/Source/modules/compositorworker/Animator.cpp
+++ b/third_party/WebKit/Source/modules/compositorworker/Animator.cpp
@@ -15,7 +15,7 @@
 Animator::Animator(v8::Isolate* isolate,
                    AnimatorDefinition* definition,
                    v8::Local<v8::Object> instance)
-    : definition_(this, definition), instance_(isolate, this, instance) {}
+    : definition_(definition), instance_(isolate, this, instance) {}
 
 Animator::~Animator() {}
 
diff --git a/third_party/WebKit/Source/modules/csspaint/PaintWorkletGlobalScope.cpp b/third_party/WebKit/Source/modules/csspaint/PaintWorkletGlobalScope.cpp
index cb4fb7f..eba1519 100644
--- a/third_party/WebKit/Source/modules/csspaint/PaintWorkletGlobalScope.cpp
+++ b/third_party/WebKit/Source/modules/csspaint/PaintWorkletGlobalScope.cpp
@@ -200,8 +200,7 @@
       ScriptController()->GetScriptState(), constructor, paint,
       native_invalidation_properties, custom_invalidation_properties,
       input_argument_types, has_alpha);
-  paint_definitions_.Set(
-      name, TraceWrapperMember<CSSPaintDefinition>(this, definition));
+  paint_definitions_.Set(name, definition);
 
   // TODO(xidachen): the following steps should be done with a postTask when
   // we move PaintWorklet off main thread.
@@ -233,9 +232,7 @@
   } else {
     DocumentPaintDefinition* document_definition =
         new DocumentPaintDefinition(definition);
-    document_definition_map.Set(
-        name,
-        TraceWrapperMember<DocumentPaintDefinition>(this, document_definition));
+    document_definition_map.Set(name, document_definition);
   }
 }
 
diff --git a/third_party/WebKit/Source/modules/mediasession/MediaSession.cpp b/third_party/WebKit/Source/modules/mediasession/MediaSession.cpp
index 2e650b0..1ae8faf 100644
--- a/third_party/WebKit/Source/modules/mediasession/MediaSession.cpp
+++ b/third_party/WebKit/Source/modules/mediasession/MediaSession.cpp
@@ -153,8 +153,7 @@
 void MediaSession::setActionHandler(const String& action,
                                     MediaSessionActionHandler* handler) {
   if (handler) {
-    auto add_result = action_handlers_.Set(
-        action, TraceWrapperMember<MediaSessionActionHandler>(this, handler));
+    auto add_result = action_handlers_.Set(action, handler);
 
     if (!add_result.is_new_entry)
       return;
diff --git a/third_party/WebKit/Source/modules/remoteplayback/AvailabilityCallbackWrapper.cpp b/third_party/WebKit/Source/modules/remoteplayback/AvailabilityCallbackWrapper.cpp
index c889db3..b2a2bf4 100644
--- a/third_party/WebKit/Source/modules/remoteplayback/AvailabilityCallbackWrapper.cpp
+++ b/third_party/WebKit/Source/modules/remoteplayback/AvailabilityCallbackWrapper.cpp
@@ -11,10 +11,10 @@
 
 AvailabilityCallbackWrapper::AvailabilityCallbackWrapper(
     RemotePlaybackAvailabilityCallback* callback)
-    : bindings_cb_(this, callback) {}
+    : bindings_cb_(callback) {}
 
 AvailabilityCallbackWrapper::AvailabilityCallbackWrapper(WTF::Closure callback)
-    : bindings_cb_(nullptr, nullptr), internal_cb_(std::move(callback)) {}
+    : internal_cb_(std::move(callback)) {}
 
 void AvailabilityCallbackWrapper::Run(RemotePlayback* remote_playback,
                                       bool new_availability) {
diff --git a/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp b/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp
index 71c6b5b..7f12c1c 100644
--- a/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp
+++ b/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp
@@ -270,10 +270,7 @@
   int id;
   do {
     id = GetExecutionContext()->CircularSequentialID();
-  } while (!availability_callbacks_
-                .insert(id, TraceWrapperMember<AvailabilityCallbackWrapper>(
-                                this, callback))
-                .is_new_entry);
+  } while (!availability_callbacks_.insert(id, callback).is_new_entry);
 
   // Report the current availability via the callback.
   // TODO(yuryu): Wrapping notifyInitialAvailability with WTF::Closure as
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp b/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp
index 325d95f..358d129 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScope.cpp
@@ -139,9 +139,7 @@
     definition->SetAudioParamDescriptors(audio_param_descriptors);
   }
 
-  processor_definition_map_.Set(
-      name,
-      TraceWrapperMember<AudioWorkletProcessorDefinition>(this, definition));
+  processor_definition_map_.Set(name, definition);
 }
 
 AudioWorkletProcessor* AudioWorkletGlobalScope::CreateInstance(
@@ -166,8 +164,7 @@
   DCHECK(processor);
 
   processor->SetInstance(isolate, instance_local);
-  processor_instances_.push_back(
-      TraceWrapperMember<AudioWorkletProcessor>(this, processor));
+  processor_instances_.push_back(processor);
 
   return processor;
 }
diff --git a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp
index 92349db..a50a9a8 100644
--- a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp
@@ -342,10 +342,10 @@
     // they don't get collected prematurely before decodeAudioData
     // calls them.
     if (success_callback) {
-      success_callbacks_.emplace_back(this, success_callback);
+      success_callbacks_.emplace_back(success_callback);
     }
     if (error_callback) {
-      error_callbacks_.emplace_back(this, error_callback);
+      error_callbacks_.emplace_back(error_callback);
     }
 
     audio_decoder_.DecodeAsync(audio, rate, success_callback, error_callback,
diff --git a/third_party/WebKit/Source/modules/webdatabase/Database.cpp b/third_party/WebKit/Source/modules/webdatabase/Database.cpp
index f3d2844..b50f332 100644
--- a/third_party/WebKit/Source/modules/webdatabase/Database.cpp
+++ b/third_party/WebKit/Source/modules/webdatabase/Database.cpp
@@ -236,7 +236,7 @@
       guid_(0),
       opened_(0),
       new_(false),
-      creation_callback_(this, creation_callback),
+      creation_callback_(creation_callback),
       transaction_in_progress_(false),
       is_transaction_queue_enabled_(true) {
   DCHECK(IsMainThread());
diff --git a/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp
index ed81f39f..8774f175 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGL2RenderingContextBase.cpp
@@ -147,17 +147,7 @@
     : WebGLRenderingContextBase(host,
                                 std::move(context_provider),
                                 requested_attributes,
-                                2),
-      read_framebuffer_binding_(this, nullptr),
-      transform_feedback_binding_(this, nullptr),
-      bound_copy_read_buffer_(this, nullptr),
-      bound_copy_write_buffer_(this, nullptr),
-      bound_pixel_pack_buffer_(this, nullptr),
-      bound_pixel_unpack_buffer_(this, nullptr),
-      bound_uniform_buffer_(this, nullptr),
-      current_boolean_occlusion_query_(this, nullptr),
-      current_transform_feedback_primitives_written_query_(this, nullptr),
-      current_elapsed_query_(this, nullptr) {
+                                2) {
   supported_internal_formats_storage_.insert(
       kSupportedInternalFormatsStorage,
       kSupportedInternalFormatsStorage +
@@ -3908,7 +3898,7 @@
     return;
   }
 
-  sampler_units_[unit] = TraceWrapperMember<WebGLSampler>(this, sampler);
+  sampler_units_[unit] = sampler;
 
   ContextGL()->BindSampler(unit, ObjectOrZero(sampler));
 }
@@ -5125,8 +5115,7 @@
                           "index out of range");
         return false;
       }
-      bound_indexed_uniform_buffers_[index] =
-          TraceWrapperMember<WebGLBuffer>(this, buffer);
+      bound_indexed_uniform_buffers_[index] = buffer;
       bound_uniform_buffer_ = buffer;
 
       // Keep track of what the maximum bound uniform buffer index is
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLContextGroup.cpp b/third_party/WebKit/Source/modules/webgl/WebGLContextGroup.cpp
index bafb9ad7..7fe6968 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLContextGroup.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLContextGroup.cpp
@@ -35,8 +35,7 @@
 }
 
 void WebGLContextGroup::AddContext(WebGLRenderingContextBase* context) {
-  contexts_.insert(
-      TraceWrapperMember<WebGLRenderingContextBase>(this, context));
+  contexts_.insert(context);
 }
 
 void WebGLContextGroup::LoseContextGroup(
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLContextObject.cpp b/third_party/WebKit/Source/modules/webgl/WebGLContextObject.cpp
index defeecc6..3582cd1 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLContextObject.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLContextObject.cpp
@@ -30,7 +30,7 @@
 namespace blink {
 
 WebGLContextObject::WebGLContextObject(WebGLRenderingContextBase* context)
-    : WebGLObject(context), context_(this, context) {}
+    : WebGLObject(context), context_(context) {}
 
 bool WebGLContextObject::Validate(
     const WebGLContextGroup*,
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLFramebuffer.cpp b/third_party/WebKit/Source/modules/webgl/WebGLFramebuffer.cpp
index 2bbaf7e..ed43eb9 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLFramebuffer.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLFramebuffer.cpp
@@ -73,7 +73,7 @@
 
 WebGLRenderbufferAttachment::WebGLRenderbufferAttachment(
     WebGLRenderbuffer* renderbuffer)
-    : renderbuffer_(this, renderbuffer) {}
+    : renderbuffer_(renderbuffer) {}
 
 WebGLSharedObject* WebGLRenderbufferAttachment::Object() const {
   return renderbuffer_->Object() ? renderbuffer_.Get() : 0;
@@ -155,7 +155,7 @@
                                                GLenum target,
                                                GLint level,
                                                GLint layer)
-    : texture_(this, texture), target_(target), level_(level), layer_(layer) {}
+    : texture_(texture), target_(target), level_(level), layer_(layer) {}
 
 WebGLSharedObject* WebGLTextureAttachment::Object() const {
   return texture_->Object() ? texture_.Get() : 0;
@@ -433,10 +433,8 @@
   DCHECK(object_);
   RemoveAttachmentInternal(target, attachment);
   if (texture && texture->Object()) {
-    attachments_.insert(attachment,
-                        TraceWrapperMember<WebGLAttachment>(
-                            this, WebGLTextureAttachment::Create(
-                                      texture, tex_target, level, layer)));
+    attachments_.insert(attachment, WebGLTextureAttachment::Create(
+                                        texture, tex_target, level, layer));
     DrawBuffersIfNecessary(false);
     texture->OnAttached();
   }
@@ -449,10 +447,8 @@
   DCHECK(object_);
   RemoveAttachmentInternal(target, attachment);
   if (renderbuffer && renderbuffer->Object()) {
-    attachments_.insert(
-        attachment,
-        TraceWrapperMember<WebGLAttachment>(
-            this, WebGLRenderbufferAttachment::Create(renderbuffer)));
+    attachments_.insert(attachment,
+                        WebGLRenderbufferAttachment::Create(renderbuffer));
     DrawBuffersIfNecessary(false);
     renderbuffer->OnAttached();
   }
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLProgram.cpp b/third_party/WebKit/Source/modules/webgl/WebGLProgram.cpp
index ead9fc11..4505dd7b 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLProgram.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLProgram.cpp
@@ -40,8 +40,6 @@
       link_status_(false),
       link_count_(0),
       active_transform_feedback_count_(0),
-      vertex_shader_(this, nullptr),
-      fragment_shader_(this, nullptr),
       info_valid_(true) {
   SetObject(ctx->ContextGL()->CreateProgram());
 }
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
index bdb0c063..d818073b 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
@@ -993,7 +993,7 @@
     const CanvasContextCreationAttributes& requested_attributes,
     unsigned version)
     : CanvasRenderingContext(host, requested_attributes),
-      context_group_(this, new WebGLContextGroup()),
+      context_group_(new WebGLContextGroup()),
       is_hidden_(false),
       context_lost_mode_(kNotLostContext),
       auto_recovery_method_(kManual),
@@ -1005,11 +1005,6 @@
       restore_timer_(task_runner,
                      this,
                      &WebGLRenderingContextBase::MaybeRestoreContext),
-      bound_array_buffer_(this, nullptr),
-      bound_vertex_array_object_(this, nullptr),
-      current_program_(this, nullptr),
-      framebuffer_binding_(this, nullptr),
-      renderbuffer_binding_(this, nullptr),
       generated_image_cache_(4),
       synthesized_errors_to_console_(true),
       num_gl_errors_to_console_allowed_(kMaxGLErrorsAllowedToConsole),
@@ -1735,17 +1730,13 @@
   }
 
   if (target == GL_TEXTURE_2D) {
-    texture_units_[active_texture_unit_].texture2d_binding_ =
-        TraceWrapperMember<WebGLTexture>(this, texture);
+    texture_units_[active_texture_unit_].texture2d_binding_ = texture;
   } else if (target == GL_TEXTURE_CUBE_MAP) {
-    texture_units_[active_texture_unit_].texture_cube_map_binding_ =
-        TraceWrapperMember<WebGLTexture>(this, texture);
+    texture_units_[active_texture_unit_].texture_cube_map_binding_ = texture;
   } else if (IsWebGL2OrHigher() && target == GL_TEXTURE_2D_ARRAY) {
-    texture_units_[active_texture_unit_].texture2d_array_binding_ =
-        TraceWrapperMember<WebGLTexture>(this, texture);
+    texture_units_[active_texture_unit_].texture2d_array_binding_ = texture;
   } else if (IsWebGL2OrHigher() && target == GL_TEXTURE_3D) {
-    texture_units_[active_texture_unit_].texture3d_binding_ =
-        TraceWrapperMember<WebGLTexture>(this, texture);
+    texture_units_[active_texture_unit_].texture3d_binding_ = texture;
   } else {
     SynthesizeGLError(GL_INVALID_ENUM, "bindTexture", "invalid target");
     return;
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
index 5e0b7952..5076744 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.h
@@ -877,8 +877,7 @@
                           ExtensionFlags flags,
                           const char* const* prefixes)
         : ExtensionTracker(flags, prefixes),
-          extension_field_(extension_field),
-          extension_(this, nullptr) {}
+          extension_field_(extension_field) {}
 
     GC_PLUGIN_IGNORE("http://crbug.com/519953")
     Member<T>& extension_field_;
@@ -894,9 +893,8 @@
   void RegisterExtension(Member<T>& extension_ptr,
                          ExtensionFlags flags = kApprovedExtension,
                          const char* const* prefixes = nullptr) {
-    extensions_.push_back(TraceWrapperMember<ExtensionTracker>(
-        this,
-        TypedExtensionTracker<T>::Create(extension_ptr, flags, prefixes)));
+    extensions_.push_back(
+        TypedExtensionTracker<T>::Create(extension_ptr, flags, prefixes));
   }
 
   bool ExtensionSupportedAndAllowed(const ExtensionTracker*);
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLSharedObject.cpp b/third_party/WebKit/Source/modules/webgl/WebGLSharedObject.cpp
index 5cb1f2f..d9224f4 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLSharedObject.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLSharedObject.cpp
@@ -31,7 +31,7 @@
 namespace blink {
 
 WebGLSharedObject::WebGLSharedObject(WebGLRenderingContextBase* context)
-    : WebGLObject(context), context_group_(this, context->ContextGroup()) {}
+    : WebGLObject(context), context_group_(context->ContextGroup()) {}
 
 bool WebGLSharedObject::Validate(const WebGLContextGroup* context_group,
                                  const WebGLRenderingContextBase*) const {
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLTransformFeedback.cpp b/third_party/WebKit/Source/modules/webgl/WebGLTransformFeedback.cpp
index 5a230d3..196f518 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLTransformFeedback.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLTransformFeedback.cpp
@@ -21,7 +21,6 @@
       object_(0),
       type_(type),
       target_(0),
-      bound_transform_feedback_buffer_(this, nullptr),
       program_(nullptr) {
   GLint max_attribs = ctx->GetMaxTransformFeedbackSeparateAttribs();
   DCHECK_GE(max_attribs, 0);
@@ -107,8 +106,7 @@
     bound_indexed_transform_feedback_buffers_[index]->OnDetached(
         Context()->ContextGL());
   }
-  bound_indexed_transform_feedback_buffers_[index] =
-      TraceWrapperMember<WebGLBuffer>(this, buffer);
+  bound_indexed_transform_feedback_buffers_[index] = buffer;
   // This also sets the generic binding point in the OpenGL state.
   SetBoundTransformFeedbackBuffer(buffer);
   return true;
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLVertexArrayObjectBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGLVertexArrayObjectBase.cpp
index d9bf8f87..56681254 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLVertexArrayObjectBase.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLVertexArrayObjectBase.cpp
@@ -16,7 +16,6 @@
       object_(0),
       type_(type),
       has_ever_been_bound_(false),
-      bound_element_array_buffer_(this, nullptr),
       is_all_enabled_attrib_buffer_bound_(true) {
   array_buffer_list_.resize(ctx->MaxVertexAttribs());
   attrib_enabled_.resize(ctx->MaxVertexAttribs());
@@ -87,7 +86,7 @@
   if (array_buffer_list_[index])
     array_buffer_list_[index]->OnDetached(Context()->ContextGL());
 
-  array_buffer_list_[index] = TraceWrapperMember<WebGLBuffer>(this, buffer);
+  array_buffer_list_[index] = buffer;
   UpdateAttribBufferBoundStatus();
 }
 
diff --git a/third_party/WebKit/Source/modules/webmidi/MIDIPort.cpp b/third_party/WebKit/Source/modules/webmidi/MIDIPort.cpp
index 11aa48f..8a617f731 100644
--- a/third_party/WebKit/Source/modules/webmidi/MIDIPort.cpp
+++ b/third_party/WebKit/Source/modules/webmidi/MIDIPort.cpp
@@ -55,7 +55,7 @@
       name_(name),
       type_(type),
       version_(version),
-      access_(this, access),
+      access_(access),
       connection_(kConnectionStateClosed) {
   DCHECK(access);
   DCHECK(type == kTypeInput || type == kTypeOutput);
diff --git a/third_party/WebKit/Source/platform/bindings/TraceWrapperMember.h b/third_party/WebKit/Source/platform/bindings/TraceWrapperMember.h
index e4c968c6..54bf8e2 100644
--- a/third_party/WebKit/Source/platform/bindings/TraceWrapperMember.h
+++ b/third_party/WebKit/Source/platform/bindings/TraceWrapperMember.h
@@ -22,19 +22,12 @@
   DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
 
  public:
-  // TODO(mlippautz): Remove constructor taking |parent|.
-  TraceWrapperMember(void* parent, T* raw) : Member<T>(raw) {}
+  TraceWrapperMember() : Member<T>(nullptr) {}
 
   TraceWrapperMember(T* raw) : Member<T>(raw) {
-    // We don't require a write barrier here as TraceWrapperMember is used for
-    // the following scenarios:
-    // - Initial initialization: The write barrier will not fire as the parent
-    //   is initially white.
-    // - Wrapping when inserting into a container: The write barrier will fire
-    //   upon establishing the move into the container.
-    // - Assignment to a field: The regular assignment operator will fire the
-    //   write barrier.
-    // Note that support for black allocation would require a barrier here.
+    // We have to use a write barrier here because of in-place construction
+    // in containers, such as HeapVector::push_back.
+    ScriptWrappableVisitor::WriteBarrier(raw);
   }
 
   TraceWrapperMember(WTF::HashTableDeletedValueType x) : Member<T>(x) {}
diff --git a/third_party/WebKit/Source/platform/heap/HeapAllocator.h b/third_party/WebKit/Source/platform/heap/HeapAllocator.h
index 9e7113d..42dd648 100644
--- a/third_party/WebKit/Source/platform/heap/HeapAllocator.h
+++ b/third_party/WebKit/Source/platform/heap/HeapAllocator.h
@@ -690,9 +690,7 @@
     return value;
   }
 
-  static blink::TraceWrapperMember<T> EmptyValue() {
-    return blink::TraceWrapperMember<T>(nullptr, nullptr);
-  }
+  static blink::TraceWrapperMember<T> EmptyValue() { return nullptr; }
 };
 
 template <typename T>
diff --git a/third_party/WebKit/Source/platform/mojo/FetchAPIRequestStructTraits.cpp b/third_party/WebKit/Source/platform/mojo/FetchAPIRequestStructTraits.cpp
index aa67d0c..ccb34a29 100644
--- a/third_party/WebKit/Source/platform/mojo/FetchAPIRequestStructTraits.cpp
+++ b/third_party/WebKit/Source/platform/mojo/FetchAPIRequestStructTraits.cpp
@@ -14,19 +14,6 @@
 
 namespace mojo {
 
-namespace {
-
-// Struct traits context for the FetchAPIRequest type. Since getters are invoked
-// twice when serializing the type, this reduces the load for heavy members.
-struct FetchAPIRequestStructTraitsContext {
-  FetchAPIRequestStructTraitsContext() = default;
-  ~FetchAPIRequestStructTraitsContext() = default;
-
-  WTF::HashMap<WTF::String, WTF::String> headers;
-};
-
-}  // namespace
-
 using blink::mojom::FetchCredentialsMode;
 using blink::mojom::FetchRedirectMode;
 using blink::mojom::FetchRequestMode;
@@ -380,26 +367,6 @@
 }
 
 // static
-void* StructTraits<blink::mojom::FetchAPIRequestDataView,
-                   blink::WebServiceWorkerRequest>::
-    SetUpContext(const blink::WebServiceWorkerRequest& request) {
-  FetchAPIRequestStructTraitsContext* context =
-      new FetchAPIRequestStructTraitsContext();
-  for (const auto& pair : request.Headers())
-    context->headers.insert(pair.key, pair.value);
-
-  return context;
-}
-
-// static
-void StructTraits<blink::mojom::FetchAPIRequestDataView,
-                  blink::WebServiceWorkerRequest>::
-    TearDownContext(const blink::WebServiceWorkerRequest& request,
-                    void* context) {
-  delete static_cast<FetchAPIRequestStructTraitsContext*>(context);
-}
-
-// static
 blink::KURL StructTraits<blink::mojom::FetchAPIRequestDataView,
                          blink::WebServiceWorkerRequest>::
     url(const blink::WebServiceWorkerRequest& request) {
@@ -414,12 +381,14 @@
 }
 
 // static
-const WTF::HashMap<WTF::String, WTF::String>&
+WTF::HashMap<WTF::String, WTF::String>
 StructTraits<blink::mojom::FetchAPIRequestDataView,
              blink::WebServiceWorkerRequest>::
-    headers(const blink::WebServiceWorkerRequest& request, void* context) {
-  DCHECK(context);
-  return static_cast<FetchAPIRequestStructTraitsContext*>(context)->headers;
+    headers(const blink::WebServiceWorkerRequest& request) {
+  WTF::HashMap<WTF::String, WTF::String> header_map;
+  for (const auto& pair : request.Headers())
+    header_map.insert(pair.key, pair.value);
+  return header_map;
 }
 
 // static
diff --git a/third_party/WebKit/Source/platform/mojo/FetchAPIRequestStructTraits.h b/third_party/WebKit/Source/platform/mojo/FetchAPIRequestStructTraits.h
index a28b1e5c..e054128 100644
--- a/third_party/WebKit/Source/platform/mojo/FetchAPIRequestStructTraits.h
+++ b/third_party/WebKit/Source/platform/mojo/FetchAPIRequestStructTraits.h
@@ -68,10 +68,6 @@
 template <>
 struct StructTraits<blink::mojom::FetchAPIRequestDataView,
                     blink::WebServiceWorkerRequest> {
-  static void* SetUpContext(const blink::WebServiceWorkerRequest&);
-  static void TearDownContext(const blink::WebServiceWorkerRequest&,
-                              void* context);
-
   static blink::WebURLRequest::FetchRequestMode mode(
       const blink::WebServiceWorkerRequest& request) {
     return request.Mode();
@@ -96,9 +92,8 @@
 
   static WTF::String method(const blink::WebServiceWorkerRequest&);
 
-  static const WTF::HashMap<WTF::String, WTF::String>& headers(
-      const blink::WebServiceWorkerRequest&,
-      void* context);
+  static WTF::HashMap<WTF::String, WTF::String> headers(
+      const blink::WebServiceWorkerRequest&);
 
   static WTF::String blob_uuid(const blink::WebServiceWorkerRequest&);
 
diff --git a/third_party/WebKit/Source/platform/wtf/BUILD.gn b/third_party/WebKit/Source/platform/wtf/BUILD.gn
index cfd461f2..0d1d76ea 100644
--- a/third_party/WebKit/Source/platform/wtf/BUILD.gn
+++ b/third_party/WebKit/Source/platform/wtf/BUILD.gn
@@ -275,6 +275,7 @@
 
   public_deps = [
     "//base",
+    "//sandbox",
     "//third_party/icu",
   ]
 
diff --git a/third_party/WebKit/Source/platform/wtf/DEPS b/third_party/WebKit/Source/platform/wtf/DEPS
index a6f31d9..2454af23 100644
--- a/third_party/WebKit/Source/platform/wtf/DEPS
+++ b/third_party/WebKit/Source/platform/wtf/DEPS
@@ -23,5 +23,6 @@
     # platform files. Think carefully if you want to relax this restriction.
     "-platform",
     "+platform/wtf",
+    "+sandbox/linux/services/resource_limits.h",
     "-v8",
 ]
diff --git a/third_party/WebKit/Source/platform/wtf/typed_arrays/ArrayBufferContents.cpp b/third_party/WebKit/Source/platform/wtf/typed_arrays/ArrayBufferContents.cpp
index 336a685..900324a 100644
--- a/third_party/WebKit/Source/platform/wtf/typed_arrays/ArrayBufferContents.cpp
+++ b/third_party/WebKit/Source/platform/wtf/typed_arrays/ArrayBufferContents.cpp
@@ -25,8 +25,12 @@
  */
 
 #include "platform/wtf/typed_arrays/ArrayBufferContents.h"
+#include "build/build_config.h"
 
 #include <string.h>
+#if defined(OS_LINUX)
+#include "sandbox/linux/services/resource_limits.h"
+#endif
 #include "base/allocator/partition_allocator/partition_alloc.h"
 #include "platform/wtf/Assertions.h"
 #include "platform/wtf/allocator/Partitions.h"
@@ -129,6 +133,14 @@
 void* ArrayBufferContents::ReserveMemory(size_t size) {
   void* const hint = nullptr;
   const size_t align = 64 << 10;  // Wasm page size
+
+#if defined(OS_LINUX)
+  // Linux by default has a small address space limit, which we chew up pretty
+  // quickly with large memory reservations. To mitigate this, we bump up the
+  // limit for array buffer reservations. See https://crbug.com/750378
+  CHECK(sandbox::ResourceLimits::AdjustCurrent(RLIMIT_AS, size));
+#endif
+
   // TODO(crbug.com/735209): On Windows this commits all the memory, rather than
   // just reserving it. This is very bad and should be fixed, but we don't use
   // this feature on Windows at all yet.
@@ -140,6 +152,13 @@
 }
 
 void ArrayBufferContents::ReleaseReservedMemory(void* data, size_t size) {
+#if defined(OS_LINUX)
+  // Linux by default has a small address space limit, which we chew up pretty
+  // quickly with large memory reservations. To mitigate this, we bump up the
+  // limit for array buffer reservations. Here we need to lower it back down.
+  // See https://crbug.com/750378
+  CHECK(sandbox::ResourceLimits::AdjustCurrent(RLIMIT_AS, -size));
+#endif
   base::FreePages(data, size);
 }
 
diff --git a/third_party/WebKit/public/platform/WebLayer.h b/third_party/WebKit/public/platform/WebLayer.h
index 2727efc..bca2777 100644
--- a/third_party/WebKit/public/platform/WebLayer.h
+++ b/third_party/WebKit/public/platform/WebLayer.h
@@ -62,8 +62,6 @@
  public:
   virtual ~WebLayer() {}
 
-  static constexpr int kInvalidLayerId = cc::Layer::INVALID_ID;
-
   // Returns a positive ID that will be unique across all WebLayers allocated in
   // this process.
   virtual int Id() const = 0;
diff --git a/third_party/WebKit/public/platform/WebLayerStickyPositionConstraint.h b/third_party/WebKit/public/platform/WebLayerStickyPositionConstraint.h
index 798083a..456f6b66 100644
--- a/third_party/WebKit/public/platform/WebLayerStickyPositionConstraint.h
+++ b/third_party/WebKit/public/platform/WebLayerStickyPositionConstraint.h
@@ -59,10 +59,11 @@
   // element should not be shifted beyond.
   WebRect scroll_container_relative_containing_block_rect;
 
-  // The nearest ancestor sticky layers that affect the sticky box constraint
-  // rect and the containing block constraint rect respectively.
-  int nearest_layer_shifting_sticky_box;
-  int nearest_layer_shifting_containing_block;
+  // The element ids of the nearest ancestor sticky layers which affect the
+  // sticky box constraint rect and the containing block constraint rect
+  // respectively.
+  cc::ElementId nearest_element_shifting_sticky_box;
+  cc::ElementId nearest_element_shifting_containing_block;
 
   WebLayerStickyPositionConstraint()
       : is_sticky(false),
@@ -73,9 +74,7 @@
         left_offset(0.f),
         right_offset(0.f),
         top_offset(0.f),
-        bottom_offset(0.f),
-        nearest_layer_shifting_sticky_box(WebLayer::kInvalidLayerId),
-        nearest_layer_shifting_containing_block(WebLayer::kInvalidLayerId) {}
+        bottom_offset(0.f) {}
 };
 
 }  // namespace blink
diff --git a/third_party/android_system_sdk/LICENSE b/third_party/android_system_sdk/LICENSE
new file mode 100644
index 0000000..3c84bc6
--- /dev/null
+++ b/third_party/android_system_sdk/LICENSE
@@ -0,0 +1,350 @@
+GNU General Public License, version 2,
+with the Classpath Exception
+
+The GNU General Public License (GPL)
+
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it.  By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users.  This General Public License applies to
+most of the Free Software Foundation's software and to any other program whose
+authors commit to using it.  (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price.  Our
+General Public Licenses are designed to make sure that you have the freedom to
+distribute copies of free software (and charge for this service if you wish),
+that you receive source code or can get it if you want it, that you can change
+the software or use pieces of it in new free programs; and that you know you
+can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights.  These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for
+a fee, you must give the recipients all the rights that you have.  You must
+make sure that they, too, receive or can get the source code.  And you must
+show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2)
+offer you this license which gives you legal permission to copy, distribute
+and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software.  If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced
+by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents.  We
+wish to avoid the danger that redistributors of a free program will
+individually obtain patent licenses, in effect making the program proprietary.
+To prevent this, we have made it clear that any patent must be licensed for
+everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License.  The "Program", below, refers to any such program
+or work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or
+translated into another language.  (Hereinafter, translation is included
+without limitation in the term "modification".) Each licensee is addressed as
+"you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope.  The act of running the Program is
+not restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program).  Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as
+you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this License
+and to the absence of any warranty; and give any other recipients of the
+Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may
+at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus
+forming a work based on the Program, and copy and distribute such modifications
+or work under the terms of Section 1 above, provided that you also meet all of
+these conditions:
+
+    a) You must cause the modified files to carry prominent notices stating
+    that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in whole or
+    in part contains or is derived from the Program or any part thereof, to be
+    licensed as a whole at no charge to all third parties under the terms of
+    this License.
+
+    c) If the modified program normally reads commands interactively when run,
+    you must cause it, when started running for such interactive use in the
+    most ordinary way, to print or display an announcement including an
+    appropriate copyright notice and a notice that there is no warranty (or
+    else, saying that you provide a warranty) and that users may redistribute
+    the program under these conditions, and telling the user how to view a copy
+    of this License.  (Exception: if the Program itself is interactive but does
+    not normally print such an announcement, your work based on the Program is
+    not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License, and
+its terms, do not apply to those sections when you distribute them as separate
+works.  But when you distribute the same sections as part of a whole which is a
+work based on the Program, the distribution of the whole must be on the terms
+of this License, whose permissions for other licensees extend to the entire
+whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your
+rights to work written entirely by you; rather, the intent is to exercise the
+right to control the distribution of derivative or collective works based on
+the Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and
+2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable source
+    code, which must be distributed under the terms of Sections 1 and 2 above
+    on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three years, to
+    give any third party, for a charge no more than your cost of physically
+    performing source distribution, a complete machine-readable copy of the
+    corresponding source code, to be distributed under the terms of Sections 1
+    and 2 above on a medium customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer to
+    distribute corresponding source code.  (This alternative is allowed only
+    for noncommercial distribution and only if you received the program in
+    object code or executable form with such an offer, in accord with
+    Subsection b above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it.  For an executable work, complete source code means all
+the source code for all modules it contains, plus any associated interface
+definition files, plus the scripts used to control compilation and installation
+of the executable.  However, as a special exception, the source code
+distributed need not include anything that is normally distributed (in either
+source or binary form) with the major components (compiler, kernel, and so on)
+of the operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source
+code from the same place counts as distribution of the source code, even though
+third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License.  Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License.  However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so
+long as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the Program
+or its derivative works.  These actions are prohibited by law if you do not
+accept this License.  Therefore, by modifying or distributing the Program (or
+any work based on the Program), you indicate your acceptance of this License to
+do so, and all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program),
+the recipient automatically receives a license from the original licensor to
+copy, distribute or modify the Program subject to these terms and conditions.
+You may not impose any further restrictions on the recipients' exercise of the
+rights granted herein.  You are not responsible for enforcing compliance by
+third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues), conditions
+are imposed on you (whether by court order, agreement or otherwise) that
+contradict the conditions of this License, they do not excuse you from the
+conditions of this License.  If you cannot distribute so as to satisfy
+simultaneously your obligations under this License and any other pertinent
+obligations, then as a consequence you may not distribute the Program at all.
+For example, if a patent license would not permit royalty-free redistribution
+of the Program by all those who receive copies directly or indirectly through
+you, then the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply and
+the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or
+other property right claims or to contest validity of any such claims; this
+section has the sole purpose of protecting the integrity of the free software
+distribution system, which is implemented by public license practices.  Many
+people have made generous contributions to the wide range of software
+distributed through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing to
+distribute software through any other system and a licensee cannot impose that
+choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain
+countries either by patents or by copyrighted interfaces, the original
+copyright holder who places the Program under this License may add an explicit
+geographical distribution limitation excluding those countries, so that
+distribution is permitted only in or among countries not thus excluded.  In
+such case, this License incorporates the limitation as if written in the body
+of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the
+General Public License from time to time.  Such new versions will be similar in
+spirit to the present version, but may differ in detail to address new problems
+or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any later
+version", you have the option of following the terms and conditions either of
+that version or of any later version published by the Free Software Foundation.
+If the Program does not specify a version number of this License, you may
+choose any version ever published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission.  For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of
+all derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
+THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN OTHERWISE
+STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE
+PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND
+PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE,
+YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
+ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE
+PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
+INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
+BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER
+OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible
+use to the public, the best way to achieve this is to make it free software
+which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program.  It is safest to attach
+them to the start of each source file to most effectively convey the exclusion
+of warranty; and each file should have at least the "copyright" line and a
+pointer to where the full notice is found.
+
+    One line to give the program's name and a brief idea of what it does.
+
+    Copyright (C) <year> <name of author>
+
+    This program is free software; you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by the Free
+    Software Foundation; either version 2 of the License, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+    more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc., 59
+    Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this when it
+starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author Gnomovision comes
+    with ABSOLUTELY NO WARRANTY; for details type 'show w'.  This is free
+    software, and you are welcome to redistribute it under certain conditions;
+    type 'show c' for details.
+
+The hypothetical commands 'show w' and 'show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may be
+called something other than 'show w' and 'show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.  Here
+is a sample; alter the names:
+
+    Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+    'Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+    signature of Ty Coon, 1 April 1989
+
+    Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General Public
+License instead of this License.
+
+
+"CLASSPATH" EXCEPTION TO THE GPL
+
+Certain source files distributed by Oracle America and/or its affiliates are
+subject to the following clarification and special exception to the GPL, but
+only where Oracle has expressly included in the particular source file's header
+the words "Oracle designates this particular file as subject to the "Classpath"
+exception as provided by Oracle in the LICENSE file that accompanied this code."
+
+    Linking this library statically or dynamically with other modules is making
+    a combined work based on this library.  Thus, the terms and conditions of
+    the GNU General Public License cover the whole combination.
+
+    As a special exception, the copyright holders of this library give you
+    permission to link this library with independent modules to produce an
+    executable, regardless of the license terms of these independent modules,
+    and to copy and distribute the resulting executable under terms of your
+    choice, provided that you also meet, for each linked independent module,
+    the terms and conditions of the license of that module.  An independent
+    module is a module which is not derived from or based on this library.  If
+    you modify this library, you may extend this exception to your version of
+    the library, but you are not obligated to do so.  If you do not wish to do
+    so, delete this exception statement from your version.
diff --git a/third_party/android_system_sdk/OWNERS b/third_party/android_system_sdk/OWNERS
new file mode 100644
index 0000000..7db1875
--- /dev/null
+++ b/third_party/android_system_sdk/OWNERS
@@ -0,0 +1,6 @@
+sgurun@chromium.org
+torne@chromium.org
+tobiasjs@chromium.org
+
+# TEAM: android-webview-dev@chromium.org
+# COMPONENT: Mobile>WebView
diff --git a/third_party/android_system_sdk/README.chromium b/third_party/android_system_sdk/README.chromium
new file mode 100644
index 0000000..4674a80
--- /dev/null
+++ b/third_party/android_system_sdk/README.chromium
@@ -0,0 +1,14 @@
+Name: Android System SDK
+Short Name:  Android System SDK
+Version: 0
+Revision: 3562008
+License: GPL v2
+License File: LICENSE
+Security Critical: No
+
+Description:
+System SDK stubs for compiling Android Webview and Monochrome targets.
+Revision is the BUILD ID of the Android release build.
+
+Local Modifications:
+None
diff --git a/third_party/android_system_sdk/android-stubs-src.jar.sha1 b/third_party/android_system_sdk/android-stubs-src.jar.sha1
new file mode 100644
index 0000000..10316dc
--- /dev/null
+++ b/third_party/android_system_sdk/android-stubs-src.jar.sha1
@@ -0,0 +1 @@
+b75f623c98f2b671b3ddedc14c7f94818b969bc7
diff --git a/third_party/android_system_sdk/android_system.jar.sha1 b/third_party/android_system_sdk/android_system.jar.sha1
new file mode 100644
index 0000000..e85537e
--- /dev/null
+++ b/third_party/android_system_sdk/android_system.jar.sha1
@@ -0,0 +1 @@
+797f9a2146d33272fafb8582c3aea0550ac20891
diff --git a/tools/memory_inspector/chrome_app/template/main_window.html b/tools/memory_inspector/chrome_app/template/main_window.html
index a876e03..a919c82f 100644
--- a/tools/memory_inspector/chrome_app/template/main_window.html
+++ b/tools/memory_inspector/chrome_app/template/main_window.html
@@ -20,9 +20,9 @@
       <div id="load_overlay">
         <div id="load_message">
           <div id="load_animation">
-            <img id="load_animation_body" src="images/body.png" alt=""></img>
-            <img id="load_animation_cog1" src="images/cog1.png" alt=""></img>
-            <img id="load_animation_cog2" src="images/cog2.png" alt=""></img>
+            <img id="load_animation_body" src="images/body.png" alt="">
+            <img id="load_animation_cog1" src="images/cog1.png" alt="">
+            <img id="load_animation_cog2" src="images/cog2.png" alt="">
           </div>
           <div id="load_message_title">
             <span id="phantom_load_dots"></span>Loading<span id="load_dots"></span>
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 58c83e0..78abe36 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -1237,6 +1237,14 @@
   </description>
 </action>
 
+<action name="Android.ChromeHome.OpenedByStartup">
+  <owner>thildebr@chromium.org</owner>
+  <description>
+    The Chrome Home bottom sheet was automatically opened on startup after a
+    period of inactivity.
+  </description>
+</action>
+
 <action name="Android.ChromeHome.OpenedBySwipe">
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 45bcd10..a2250a4 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -35798,6 +35798,7 @@
   <int value="8" label="SHOW_BAD_CLOCK"/>
   <int value="9" label="CAPTIVE_PORTAL_CERT_FOUND"/>
   <int value="10" label="WWW_MISMATCH_FOUND_IN_SAN"/>
+  <int value="11" label="SHOW_MITM_SOFTWARE_INTERSTITIAL"/>
 </enum>
 
 <enum name="SSLErrorTypes">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 1a64d98..f42bdddd 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -80540,6 +80540,39 @@
   </summary>
 </histogram>
 
+<histogram
+    name="TabManager.Experimental.SessionRestore.ForegroundTab.FirstContentfulPaint"
+    units="ms">
+  <owner>fmeawad@chromium.org</owner>
+  <owner>ducbui@google.com</owner>
+  <summary>
+    Elapsed time between the start of the loading and the first contentful paint
+    of foreground tabs when the browser loads tabs in session restore.
+  </summary>
+</histogram>
+
+<histogram
+    name="TabManager.Experimental.SessionRestore.ForegroundTab.FirstMeaningfulPaint"
+    units="ms">
+  <owner>fmeawad@chromium.org</owner>
+  <owner>ducbui@google.com</owner>
+  <summary>
+    Elapsed time between the start of the loading and the first meaningful paint
+    of foreground tabs when the browser loads tabs in session restore.
+  </summary>
+</histogram>
+
+<histogram
+    name="TabManager.Experimental.SessionRestore.ForegroundTab.FirstPaint"
+    units="ms">
+  <owner>fmeawad@chromium.org</owner>
+  <owner>ducbui@google.com</owner>
+  <summary>
+    Elapsed time between the start of the loading and the first paint of
+    foreground tabs when the browser loads tabs in session restore.
+  </summary>
+</histogram>
+
 <histogram name="TabManager.Heuristics.FromBackgroundedToFirstAudioStarts"
     units="ms">
   <owner>chrisha@chromium.org</owner>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index d470c01..4ef64bd5 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -8,6 +8,9 @@
 This file is used to generate a comprehensive list of Chrome UKM metrics
 along with a detailed description for each metric.
 
+See the following doc for details on how to add entries to this file:
+https://chromium.googlesource.com/chromium/src.git/+/master/services/metrics/ukm_api.md
+
 Events may be marked with the attribute singular="True" to indicate that
 the event will only occur once per source, and multiple entries will just
 be describing additional metrics about the same event.
diff --git a/ui/aura/env.cc b/ui/aura/env.cc
index 4493d68..5c040ed 100644
--- a/ui/aura/env.cc
+++ b/ui/aura/env.cc
@@ -174,7 +174,13 @@
   // The ozone platform can provide its own event source. So initialize the
   // platform before creating the default event source. If running inside mus
   // let the mus process initialize ozone instead.
-  ui::OzonePlatform::InitializeForUI();
+  ui::OzonePlatform::InitParams params;
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  // TODO(kylechar): Pass in single process information to Env::CreateInstance()
+  // instead of checking flags here.
+  params.single_process = command_line->HasSwitch("single-process") ||
+                          command_line->HasSwitch("in-process-gpu");
+  ui::OzonePlatform::InitializeForUI(params);
   gfx::ClientNativePixmapFactory::SetInstance(native_pixmap_factory_.get());
 #endif
   if (!ui::PlatformEventSource::GetInstance())
diff --git a/ui/events/ozone/evdev/device_event_dispatcher_evdev.cc b/ui/events/ozone/evdev/device_event_dispatcher_evdev.cc
index a299af3..7819568 100644
--- a/ui/events/ozone/evdev/device_event_dispatcher_evdev.cc
+++ b/ui/events/ozone/evdev/device_event_dispatcher_evdev.cc
@@ -121,13 +121,15 @@
                                    EventType type,
                                    const gfx::PointF& location,
                                    const PointerDetails& details,
-                                   const base::TimeTicks& timestamp)
+                                   const base::TimeTicks& timestamp,
+                                   int flags)
     : device_id(device_id),
       slot(slot),
       type(type),
       location(location),
       pointer_details(details),
-      timestamp(timestamp) {}
+      timestamp(timestamp),
+      flags(flags) {}
 
 TouchEventParams::TouchEventParams(const TouchEventParams& other) = default;
 
diff --git a/ui/events/ozone/evdev/device_event_dispatcher_evdev.h b/ui/events/ozone/evdev/device_event_dispatcher_evdev.h
index 4bf3fdf..f37d66b7 100644
--- a/ui/events/ozone/evdev/device_event_dispatcher_evdev.h
+++ b/ui/events/ozone/evdev/device_event_dispatcher_evdev.h
@@ -138,7 +138,8 @@
                    EventType type,
                    const gfx::PointF& location,
                    const PointerDetails& pointer_details,
-                   const base::TimeTicks& timestamp);
+                   const base::TimeTicks& timestamp,
+                   int flags);
   TouchEventParams(const TouchEventParams& other);
   TouchEventParams() {}
   ~TouchEventParams();
@@ -149,6 +150,7 @@
   gfx::PointF location;
   PointerDetails pointer_details;
   base::TimeTicks timestamp;
+  int flags;
 };
 
 // Interface used by device objects for event dispatch.
diff --git a/ui/events/ozone/evdev/event_device_test_util.cc b/ui/events/ozone/evdev/event_device_test_util.cc
index 2477945..5f032d9d 100644
--- a/ui/events/ozone/evdev/event_device_test_util.cc
+++ b/ui/events/ozone/evdev/event_device_test_util.cc
@@ -520,6 +520,36 @@
   arraysize(kWilsonBeachActiveStylusAbsAxes),
 };
 
+// Captured from Eve Chromebook
+const DeviceAbsoluteAxis kEveStylusAbsAxes[] = {
+    {ABS_X, {0, 0, 25920, 0, 0, 100}},     {ABS_Y, {0, 0, 17280, 0, 0, 100}},
+    {ABS_PRESSURE, {0, 0, 2047, 0, 0, 0}}, {ABS_TILT_X, {0, -90, 90, 0, 0, 57}},
+    {ABS_TILT_Y, {0, -90, 90, 0, 0, 57}},  {ABS_MISC, {0, 0, 255, 0, 0, 0}},
+};
+const DeviceCapabilities kEveStylus = {
+    /* path */
+    "/sys/devices/pci0000:00/0000:00:15.0/i2c_designware.0/i2c-6/"
+    "i2c-WCOM50C1:00/0018:2D1F:5134.0001/input/input7/event7",
+    /* name */ "WCOM50C1:00 2D1F:5134 Pen",
+    /* phys */ "",
+    /* uniq */ "",
+    /* bustype */ "0018",
+    /* vendor */ "2d1f",
+    /* product */ "5134",
+    /* version */ "0100",
+    /* prop */ "0",
+    /* ev */ "1b",
+    /* key */ "1c03 1 0 0 0 0",
+    /* rel */ "0",
+    /* abs */ "1000d000003",
+    /* msc */ "11",
+    /* sw */ "0",
+    /* led */ "0",
+    /* ff */ "0",
+    kEveStylusAbsAxes,
+    arraysize(kEveStylusAbsAxes),
+};
+
 ui::InputDeviceType InputDeviceTypeFromBusType(int bustype) {
   switch (bustype) {
     case BUS_I8042:
diff --git a/ui/events/ozone/evdev/event_device_test_util.h b/ui/events/ozone/evdev/event_device_test_util.h
index a8de08f0..145ca4b 100644
--- a/ui/events/ozone/evdev/event_device_test_util.h
+++ b/ui/events/ozone/evdev/event_device_test_util.h
@@ -74,6 +74,7 @@
 extern const DeviceCapabilities kLogitechTouchKeyboardK400;
 extern const DeviceCapabilities kElo_TouchSystems_2700;
 extern const DeviceCapabilities kWilsonBeachActiveStylus;
+extern const DeviceCapabilities kEveStylus;
 
 }  // namspace ui
 
diff --git a/ui/events/ozone/evdev/event_factory_evdev.cc b/ui/events/ozone/evdev/event_factory_evdev.cc
index 26fbb47..6d5aeb6 100644
--- a/ui/events/ozone/evdev/event_factory_evdev.cc
+++ b/ui/events/ozone/evdev/event_factory_evdev.cc
@@ -350,7 +350,8 @@
       params.device_id * kNumTouchEvdevSlots + params.slot);
   details.id = touch_id;
   TouchEvent touch_event(params.type, gfx::Point(), params.timestamp, details,
-                         modifiers_.GetModifierFlags(), /* angle */ 0.f);
+                         modifiers_.GetModifierFlags() | params.flags,
+                         /* angle */ 0.f);
   touch_event.set_location_f(location);
   touch_event.set_root_location_f(location);
   touch_event.set_source_device_id(params.device_id);
diff --git a/ui/events/ozone/evdev/touch_evdev_types.h b/ui/events/ozone/evdev/touch_evdev_types.h
index cd54f74..edb9add 100644
--- a/ui/events/ozone/evdev/touch_evdev_types.h
+++ b/ui/events/ozone/evdev/touch_evdev_types.h
@@ -57,9 +57,7 @@
     bool down = false;
     bool changed = false;
   };
-  ButtonState btn_left;
-  ButtonState btn_right;
-  ButtonState btn_middle;
+  ButtonState btn_stylus;
 };
 
 }  // namespace ui
diff --git a/ui/events/ozone/evdev/touch_event_converter_evdev.cc b/ui/events/ozone/evdev/touch_event_converter_evdev.cc
index f27da19b..ca5a9a0 100644
--- a/ui/events/ozone/evdev/touch_event_converter_evdev.cc
+++ b/ui/events/ozone/evdev/touch_event_converter_evdev.cc
@@ -359,19 +359,10 @@
 
 void TouchEventConverterEvdev::ProcessKey(const input_event& input) {
   switch (input.code) {
-    case BTN_TOUCH:
-    case BTN_LEFT:
-    case BTN_0:
-      events_[current_slot_].btn_left.down = input.value;
-      events_[current_slot_].btn_left.changed = true;
-      break;
     case BTN_STYLUS:
-      events_[current_slot_].btn_right.down = input.value;
-      events_[current_slot_].btn_right.changed = true;
-      break;
-    case BTN_STYLUS2:
-      events_[current_slot_].btn_middle.down = input.value;
-      events_[current_slot_].btn_middle.changed = true;
+      events_[current_slot_].btn_stylus.down = input.value;
+      events_[current_slot_].btn_stylus.changed = true;
+      events_[current_slot_].altered = true;
       break;
     case BTN_TOOL_PEN:
     case BTN_TOOL_RUBBER:
@@ -382,6 +373,11 @@
       }
       events_[current_slot_].altered = true;
       break;
+    case BTN_LEFT:
+    case BTN_0:
+    case BTN_STYLUS2:
+    case BTN_TOUCH:
+      break;
     default:
       NOTIMPLEMENTED() << "invalid code for EV_KEY: " << input.code;
   }
@@ -494,9 +490,10 @@
   ui::PointerDetails details(event.reported_tool_type, /* pointer_id*/ 0,
                              event.radius_x, event.radius_y, event.pressure,
                              event.tilt_x, event.tilt_y);
-  dispatcher_->DispatchTouchEvent(
-      TouchEventParams(input_device_.id, event.slot, event_type,
-                       gfx::PointF(event.x, event.y), details, timestamp));
+  int flags = event.btn_stylus.down ? ui::EventFlags::EF_LEFT_MOUSE_BUTTON : 0;
+  dispatcher_->DispatchTouchEvent(TouchEventParams(
+      input_device_.id, event.slot, event_type, gfx::PointF(event.x, event.y),
+      details, timestamp, flags));
 }
 
 void TouchEventConverterEvdev::CancelAllTouches() {
@@ -552,9 +549,7 @@
     event->was_touching = event->touching;
     event->was_delayed = event->delayed;
     event->altered = false;
-    event->btn_left.changed = false;
-    event->btn_right.changed = false;
-    event->btn_middle.changed = false;
+    event->btn_stylus.changed = false;
   }
 }
 
@@ -587,17 +582,9 @@
   for (size_t slot = 0; slot < events_.size(); slot++) {
     InProgressTouchEvdev* event = &events_[slot];
 
-    if (event->btn_left.down) {
-      event->btn_left.down = false;
-      event->btn_left.changed = true;
-    }
-    if (event->btn_right.down) {
-      event->btn_right.down = false;
-      event->btn_right.changed = true;
-    }
-    if (event->btn_middle.down) {
-      event->btn_middle.down = false;
-      event->btn_middle.changed = true;
+    if (event->btn_stylus.down) {
+      event->btn_stylus.down = false;
+      event->btn_stylus.changed = true;
     }
   }
 
diff --git a/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc b/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
index cc6b3c6b..4ae3d4f0 100644
--- a/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
+++ b/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc
@@ -1115,4 +1115,114 @@
   EXPECT_EQ(0.f / 1024, event.pointer_details.force);
 }
 
+TEST_F(TouchEventConverterEvdevTest, ActiveStylusBarrelButtonWhileHovering) {
+  ui::MockTouchEventConverterEvdev* dev = device();
+  EventDeviceInfo devinfo;
+  EXPECT_TRUE(CapabilitiesToDeviceInfo(kEveStylus, &devinfo));
+  dev->Initialize(devinfo);
+
+  struct input_event mock_kernel_queue[]{
+      // Hover
+      {{0, 0}, EV_KEY, BTN_TOOL_PEN, 1},
+      {{0, 0}, EV_SYN, SYN_REPORT, 0},
+
+      // Button pressed
+      {{0, 0}, EV_KEY, BTN_STYLUS, 1},
+      {{0, 0}, EV_SYN, SYN_REPORT, 0},
+
+      // Touching down
+      {{0, 0}, EV_KEY, BTN_TOUCH, 1},
+      {{0, 0}, EV_SYN, SYN_REPORT, 0},
+
+      // Releasing touch
+      {{0, 0}, EV_KEY, BTN_TOUCH, 0},
+      {{0, 0}, EV_SYN, SYN_REPORT, 0},
+
+      // Releasing button
+      {{0, 0}, EV_KEY, BTN_STYLUS, 0},
+      {{0, 0}, EV_SYN, SYN_REPORT, 0},
+
+      // Leaving hover
+      {{0, 0}, EV_KEY, BTN_TOOL_PEN, 0},
+      {{0, 0}, EV_SYN, SYN_REPORT, 0},
+  };
+
+  dev->ConfigureReadMock(mock_kernel_queue, arraysize(mock_kernel_queue), 0);
+  dev->ReadNow();
+  EXPECT_EQ(2u, size());
+
+  auto down_event = dispatched_touch_event(0);
+  EXPECT_EQ(ET_TOUCH_PRESSED, down_event.type);
+  EXPECT_TRUE(down_event.flags & ui::EventFlags::EF_LEFT_MOUSE_BUTTON);
+  EXPECT_EQ(EventPointerType::POINTER_TYPE_PEN,
+            down_event.pointer_details.pointer_type);
+
+  auto up_event = dispatched_touch_event(1);
+  EXPECT_EQ(ET_TOUCH_RELEASED, up_event.type);
+  EXPECT_TRUE(down_event.flags & ui::EventFlags::EF_LEFT_MOUSE_BUTTON);
+  EXPECT_EQ(EventPointerType::POINTER_TYPE_PEN,
+            up_event.pointer_details.pointer_type);
+}
+
+TEST_F(TouchEventConverterEvdevTest, ActiveStylusBarrelButton) {
+  ui::MockTouchEventConverterEvdev* dev = device();
+  EventDeviceInfo devinfo;
+  EXPECT_TRUE(CapabilitiesToDeviceInfo(kEveStylus, &devinfo));
+  dev->Initialize(devinfo);
+
+  struct input_event mock_kernel_queue[]{
+      // Hover
+      {{0, 0}, EV_KEY, BTN_TOOL_PEN, 1},
+      {{0, 0}, EV_SYN, SYN_REPORT, 0},
+
+      // Touching down
+      {{0, 0}, EV_KEY, BTN_TOUCH, 1},
+      {{0, 0}, EV_SYN, SYN_REPORT, 0},
+
+      // Button pressed
+      {{0, 0}, EV_KEY, BTN_STYLUS, 1},
+      {{0, 0}, EV_SYN, SYN_REPORT, 0},
+
+      // Releasing button
+      {{0, 0}, EV_KEY, BTN_STYLUS, 0},
+      {{0, 0}, EV_SYN, SYN_REPORT, 0},
+
+      // Releasing touch
+      {{0, 0}, EV_KEY, BTN_TOUCH, 0},
+      {{0, 0}, EV_SYN, SYN_REPORT, 0},
+
+      // Leaving hover
+      {{0, 0}, EV_KEY, BTN_TOOL_PEN, 0},
+      {{0, 0}, EV_SYN, SYN_REPORT, 0},
+  };
+
+  dev->ConfigureReadMock(mock_kernel_queue, arraysize(mock_kernel_queue), 0);
+  dev->ReadNow();
+  EXPECT_EQ(4u, size());
+
+  auto down_event = dispatched_touch_event(0);
+  EXPECT_EQ(ET_TOUCH_PRESSED, down_event.type);
+  EXPECT_FALSE(down_event.flags & ui::EventFlags::EF_LEFT_MOUSE_BUTTON);
+  EXPECT_EQ(EventPointerType::POINTER_TYPE_PEN,
+            down_event.pointer_details.pointer_type);
+
+  auto button_down_event = dispatched_touch_event(1);
+  EXPECT_EQ(ET_TOUCH_MOVED, button_down_event.type);
+  EXPECT_TRUE(button_down_event.flags & ui::EventFlags::EF_LEFT_MOUSE_BUTTON);
+  EXPECT_EQ(EventPointerType::POINTER_TYPE_PEN,
+            button_down_event.pointer_details.pointer_type);
+
+  auto button_up_event = dispatched_touch_event(2);
+  EXPECT_EQ(ET_TOUCH_MOVED, button_up_event.type);
+  EXPECT_FALSE(button_up_event.flags & ui::EventFlags::EF_LEFT_MOUSE_BUTTON);
+  EXPECT_EQ(EventPointerType::POINTER_TYPE_PEN,
+            button_up_event.pointer_details.pointer_type);
+
+  auto up_event = dispatched_touch_event(3);
+  EXPECT_EQ(ET_TOUCH_RELEASED, up_event.type);
+  EXPECT_FALSE(down_event.flags & ui::EventFlags::EF_LEFT_MOUSE_BUTTON);
+  EXPECT_EQ(EventPointerType::POINTER_TYPE_PEN,
+            up_event.pointer_details.pointer_type);
+}
+
 }  // namespace ui
diff --git a/ui/gfx/image/mojo/image_skia_struct_traits.cc b/ui/gfx/image/mojo/image_skia_struct_traits.cc
index 98ef823..cb9487f 100644
--- a/ui/gfx/image/mojo/image_skia_struct_traits.cc
+++ b/ui/gfx/image/mojo/image_skia_struct_traits.cc
@@ -107,32 +107,12 @@
 }
 
 // static
-void* StructTraits<gfx::mojom::ImageSkiaDataView, gfx::ImageSkia>::SetUpContext(
+std::vector<gfx::ImageSkiaRep>
+StructTraits<gfx::mojom::ImageSkiaDataView, gfx::ImageSkia>::image_reps(
     const gfx::ImageSkia& input) {
   // Trigger the image to load everything.
   input.EnsureRepsForSupportedScales();
-
-  // Use a context to return a stable list of ImageSkiaRep objects. That is,
-  // multiple calls of image_reps() should return exactly the same list of
-  // ImageSkiaRep objects. So that ImageSkiaRep with the same backing pixel
-  // buffer is properly serialized and only once.
-  return new std::vector<gfx::ImageSkiaRep>(input.image_reps());
-}
-
-// static
-void StructTraits<gfx::mojom::ImageSkiaDataView,
-                  gfx::ImageSkia>::TearDownContext(const gfx::ImageSkia& input,
-                                                   void* context) {
-  delete static_cast<std::vector<gfx::ImageSkiaRep>*>(context);
-}
-
-// static
-const std::vector<gfx::ImageSkiaRep>&
-StructTraits<gfx::mojom::ImageSkiaDataView, gfx::ImageSkia>::image_reps(
-    const gfx::ImageSkia& input,
-    void* context) {
-  // See the comment in SetUpContext regarding context usage.
-  return *(static_cast<std::vector<gfx::ImageSkiaRep>*>(context));
+  return input.image_reps();
 }
 
 // static
diff --git a/ui/gfx/image/mojo/image_skia_struct_traits.h b/ui/gfx/image/mojo/image_skia_struct_traits.h
index 974ec9c..0b9cfecf 100644
--- a/ui/gfx/image/mojo/image_skia_struct_traits.h
+++ b/ui/gfx/image/mojo/image_skia_struct_traits.h
@@ -56,11 +56,7 @@
 
 template <>
 struct StructTraits<gfx::mojom::ImageSkiaDataView, gfx::ImageSkia> {
-  static void* SetUpContext(const gfx::ImageSkia& input);
-  static void TearDownContext(const gfx::ImageSkia& input, void* context);
-  static const std::vector<gfx::ImageSkiaRep>& image_reps(
-      const gfx::ImageSkia& input,
-      void* context);
+  static std::vector<gfx::ImageSkiaRep> image_reps(const gfx::ImageSkia& input);
 
   static bool IsNull(const gfx::ImageSkia& input) {
     return input.image_reps().empty();
diff --git a/ui/gfx/mojo/buffer_types_struct_traits.cc b/ui/gfx/mojo/buffer_types_struct_traits.cc
index 186e2eb5..e9aea16 100644
--- a/ui/gfx/mojo/buffer_types_struct_traits.cc
+++ b/ui/gfx/mojo/buffer_types_struct_traits.cc
@@ -8,30 +8,17 @@
 
 namespace mojo {
 
-void* StructTraits<gfx::mojom::NativePixmapHandleDataView,
-                   gfx::NativePixmapHandle>::
-    SetUpContext(const gfx::NativePixmapHandle& pixmap_handle) {
-  auto* handles = new std::vector<mojo::ScopedHandle>();
+std::vector<mojo::ScopedHandle>
+StructTraits<gfx::mojom::NativePixmapHandleDataView, gfx::NativePixmapHandle>::
+    fds(const gfx::NativePixmapHandle& pixmap_handle) {
+  std::vector<mojo::ScopedHandle> handles;
 #if defined(OS_LINUX)
   for (const base::FileDescriptor& fd : pixmap_handle.fds)
-    handles->emplace_back(mojo::WrapPlatformFile(fd.fd));
+    handles.emplace_back(mojo::WrapPlatformFile(fd.fd));
 #endif  // defined(OS_LINUX)
   return handles;
 }
 
-void StructTraits<gfx::mojom::NativePixmapHandleDataView,
-                  gfx::NativePixmapHandle>::
-    TearDownContext(const gfx::NativePixmapHandle& handle, void* context) {
-  delete static_cast<std::vector<mojo::ScopedHandle>*>(context);
-}
-
-std::vector<mojo::ScopedHandle>& StructTraits<
-    gfx::mojom::NativePixmapHandleDataView,
-    gfx::NativePixmapHandle>::fds(const gfx::NativePixmapHandle& pixmap_handle,
-                                  void* context) {
-  return *static_cast<std::vector<mojo::ScopedHandle>*>(context);
-}
-
 bool StructTraits<
     gfx::mojom::NativePixmapHandleDataView,
     gfx::NativePixmapHandle>::Read(gfx::mojom::NativePixmapHandleDataView data,
diff --git a/ui/gfx/mojo/buffer_types_struct_traits.h b/ui/gfx/mojo/buffer_types_struct_traits.h
index 0fdf909..fadb17a8 100644
--- a/ui/gfx/mojo/buffer_types_struct_traits.h
+++ b/ui/gfx/mojo/buffer_types_struct_traits.h
@@ -239,10 +239,6 @@
 template <>
 struct StructTraits<gfx::mojom::NativePixmapHandleDataView,
                     gfx::NativePixmapHandle> {
-  static void* SetUpContext(const gfx::NativePixmapHandle& handle);
-  static void TearDownContext(const gfx::NativePixmapHandle& handle,
-                              void* context);
-
   static bool IsNull(const gfx::NativePixmapHandle& handle) {
 #if defined(OS_LINUX)
     return false;
@@ -251,9 +247,8 @@
     return true;
 #endif
   }
-  static std::vector<mojo::ScopedHandle>& fds(
-      const gfx::NativePixmapHandle& pixmap_handle,
-      void* context);
+  static std::vector<mojo::ScopedHandle> fds(
+      const gfx::NativePixmapHandle& pixmap_handle);
 
   static const std::vector<gfx::NativePixmapPlane>& planes(
       const gfx::NativePixmapHandle& pixmap_handle) {
diff --git a/ui/ozone/platform/drm/ozone_platform_gbm.cc b/ui/ozone/platform/drm/ozone_platform_gbm.cc
index 109c5a97..2f265424 100644
--- a/ui/ozone/platform/drm/ozone_platform_gbm.cc
+++ b/ui/ozone/platform/drm/ozone_platform_gbm.cc
@@ -151,7 +151,8 @@
     // future CL.
     single_process_ = args.single_process;
     using_mojo_ = args.connector != nullptr;
-    DCHECK(!(using_mojo_ && single_process_));
+    DCHECK(!(using_mojo_ && !single_process_))
+        << "Multiprocess Mojo is not supported yet.";
 
     device_manager_ = CreateDeviceManager();
     window_manager_.reset(new DrmWindowHostManager());
diff --git a/ui/ozone/platform/x11/ozone_platform_x11.cc b/ui/ozone/platform/x11/ozone_platform_x11.cc
index 072c5b02..58176cc 100644
--- a/ui/ozone/platform/x11/ozone_platform_x11.cc
+++ b/ui/ozone/platform/x11/ozone_platform_x11.cc
@@ -9,7 +9,6 @@
 #include <memory>
 #include <utility>
 
-#include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
 #include "base/strings/utf_string_conversions.h"
@@ -32,16 +31,6 @@
 
 namespace {
 
-// Returns true if a flag is present that will cause Ozone UI and GPU to run in
-// the same process.
-// TODO(kylechar): Remove --mojo-platform-channel-handle when mus-ws process
-// split happens.
-bool HasSingleProcessFlag() {
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  return command_line->HasSwitch("mojo-platform-channel-handle") ||
-         command_line->HasSwitch("single-process");
-}
-
 // Singleton OzonePlatform implementation for X11 platform.
 class OzonePlatformX11 : public OzonePlatform {
  public:
@@ -106,7 +95,7 @@
 
     // In single process mode either the UI thread will create an event source
     // or it's a test and an event source isn't desired.
-    if (!params.single_process && !HasSingleProcessFlag())
+    if (!params.single_process)
       CreatePlatformEventSource();
 
     surface_factory_ozone_ = base::MakeUnique<X11SurfaceFactory>();
@@ -127,7 +116,7 @@
       return;
 
     // In single process mode XInitThreads() must be the first Xlib call.
-    if (params.single_process || HasSingleProcessFlag())
+    if (params.single_process)
       XInitThreads();
 
     ui::SetDefaultX11ErrorHandlers();
diff --git a/ui/views/animation/flood_fill_ink_drop_ripple.cc b/ui/views/animation/flood_fill_ink_drop_ripple.cc
index 33b5c69b..7f2c4c1 100644
--- a/ui/views/animation/flood_fill_ink_drop_ripple.cc
+++ b/ui/views/animation/flood_fill_ink_drop_ripple.cc
@@ -213,8 +213,10 @@
       }
       break;
     case InkDropState::ACTION_PENDING: {
-      DCHECK_EQ(InkDropState::HIDDEN, old_ink_drop_state)
-          << " old_ink_drop_state=" << ToString(old_ink_drop_state);
+      DLOG_IF(WARNING, InkDropState::HIDDEN != old_ink_drop_state)
+          << "Invalid InkDropState transition. old_ink_drop_state="
+          << ToString(old_ink_drop_state)
+          << " new_ink_drop_state=" << ToString(new_ink_drop_state);
 
       AnimateToOpacity(visible_opacity_,
                        GetAnimationDuration(ACTION_PENDING_FADE_IN),
@@ -232,9 +234,12 @@
       break;
     }
     case InkDropState::ACTION_TRIGGERED: {
-      DCHECK(old_ink_drop_state == InkDropState::HIDDEN ||
-             old_ink_drop_state == InkDropState::ACTION_PENDING)
-          << " old_ink_drop_state=" << ToString(old_ink_drop_state);
+      DLOG_IF(WARNING, old_ink_drop_state != InkDropState::HIDDEN &&
+                           old_ink_drop_state != InkDropState::ACTION_PENDING)
+          << "Invalid InkDropState transition. old_ink_drop_state="
+          << ToString(old_ink_drop_state)
+          << " new_ink_drop_state=" << ToString(new_ink_drop_state);
+
       if (old_ink_drop_state == InkDropState::HIDDEN) {
         AnimateStateChange(old_ink_drop_state, InkDropState::ACTION_PENDING,
                            animation_observer);
@@ -246,8 +251,11 @@
       break;
     }
     case InkDropState::ALTERNATE_ACTION_PENDING: {
-      DCHECK_EQ(InkDropState::ACTION_PENDING, old_ink_drop_state)
-          << " old_ink_drop_state=" << ToString(old_ink_drop_state);
+      DLOG_IF(WARNING, InkDropState::ACTION_PENDING != old_ink_drop_state)
+          << "Invalid InkDropState transition. old_ink_drop_state="
+          << ToString(old_ink_drop_state)
+          << " new_ink_drop_state=" << ToString(new_ink_drop_state);
+
       AnimateToOpacity(visible_opacity_,
                        GetAnimationDuration(ALTERNATE_ACTION_PENDING),
                        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
@@ -259,8 +267,12 @@
       break;
     }
     case InkDropState::ALTERNATE_ACTION_TRIGGERED:
-      DCHECK_EQ(InkDropState::ALTERNATE_ACTION_PENDING, old_ink_drop_state)
-          << " old_ink_drop_state=" << ToString(old_ink_drop_state);
+      DLOG_IF(WARNING,
+              InkDropState::ALTERNATE_ACTION_PENDING != old_ink_drop_state)
+          << "Invalid InkDropState transition. old_ink_drop_state="
+          << ToString(old_ink_drop_state)
+          << " new_ink_drop_state=" << ToString(new_ink_drop_state);
+
       AnimateToOpacity(kHiddenOpacity, GetAnimationDuration(
                                            ALTERNATE_ACTION_TRIGGERED_FADE_OUT),
                        ui::LayerAnimator::ENQUEUE_NEW_ANIMATION,
diff --git a/ui/views/animation/ink_drop_ripple.cc b/ui/views/animation/ink_drop_ripple.cc
index 22306a93..f6360a8 100644
--- a/ui/views/animation/ink_drop_ripple.cc
+++ b/ui/views/animation/ink_drop_ripple.cc
@@ -38,7 +38,8 @@
   // 1. The attached observers must be notified of all animations started and
   // ended.
   // 2. Not all state transitions is are valid, especially no-op transitions,
-  // and these should be detected by DCHECKs in AnimateStateChange().
+  // and these invalid transitions will be logged as warnings in
+  // AnimateStateChange().
 
   // |animation_observer| will be deleted when AnimationEndedCallback() returns
   // true.
diff --git a/ui/views/animation/square_ink_drop_ripple.cc b/ui/views/animation/square_ink_drop_ripple.cc
index fffc856..276b5d5 100644
--- a/ui/views/animation/square_ink_drop_ripple.cc
+++ b/ui/views/animation/square_ink_drop_ripple.cc
@@ -240,8 +240,11 @@
     case InkDropState::ACTION_PENDING:
       if (old_ink_drop_state == new_ink_drop_state)
         return;
-      DCHECK_EQ(InkDropState::HIDDEN, old_ink_drop_state)
-          << " old_ink_drop_state=" << ToString(old_ink_drop_state);
+      DLOG_IF(WARNING, InkDropState::HIDDEN != old_ink_drop_state)
+          << "Invalid InkDropState transition. old_ink_drop_state="
+          << ToString(old_ink_drop_state)
+          << " new_ink_drop_state=" << ToString(new_ink_drop_state);
+
       AnimateToOpacity(visible_opacity_,
                        GetAnimationDuration(ACTION_PENDING_FADE_IN),
                        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
@@ -257,9 +260,12 @@
                           gfx::Tween::EASE_IN_OUT, animation_observer);
       break;
     case InkDropState::ACTION_TRIGGERED: {
-      DCHECK(old_ink_drop_state == InkDropState::HIDDEN ||
-             old_ink_drop_state == InkDropState::ACTION_PENDING)
-          << " old_ink_drop_state=" << ToString(old_ink_drop_state);
+      DLOG_IF(WARNING, old_ink_drop_state != InkDropState::HIDDEN &&
+                           old_ink_drop_state != InkDropState::ACTION_PENDING)
+          << "Invalid InkDropState transition. old_ink_drop_state="
+          << ToString(old_ink_drop_state)
+          << " new_ink_drop_state=" << ToString(new_ink_drop_state);
+
       if (old_ink_drop_state == InkDropState::HIDDEN) {
         AnimateStateChange(old_ink_drop_state, InkDropState::ACTION_PENDING,
                            animation_observer);
@@ -277,8 +283,11 @@
       break;
     }
     case InkDropState::ALTERNATE_ACTION_PENDING:
-      DCHECK_EQ(InkDropState::ACTION_PENDING, old_ink_drop_state)
-          << " old_ink_drop_state=" << ToString(old_ink_drop_state);
+      DLOG_IF(WARNING, InkDropState::ACTION_PENDING != old_ink_drop_state)
+          << "Invalid InkDropState transition. old_ink_drop_state="
+          << ToString(old_ink_drop_state)
+          << " new_ink_drop_state=" << ToString(new_ink_drop_state);
+
       AnimateToOpacity(visible_opacity_,
                        GetAnimationDuration(ALTERNATE_ACTION_PENDING),
                        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET,
@@ -290,8 +299,12 @@
                           gfx::Tween::EASE_IN_OUT, animation_observer);
       break;
     case InkDropState::ALTERNATE_ACTION_TRIGGERED: {
-      DCHECK_EQ(InkDropState::ALTERNATE_ACTION_PENDING, old_ink_drop_state)
-          << " old_ink_drop_state=" << ToString(old_ink_drop_state);
+      DLOG_IF(WARNING,
+              InkDropState::ALTERNATE_ACTION_PENDING != old_ink_drop_state)
+          << "Invalid InkDropState transition. old_ink_drop_state="
+          << ToString(old_ink_drop_state)
+          << " new_ink_drop_state=" << ToString(new_ink_drop_state);
+
       base::TimeDelta visible_duration =
           GetAnimationDuration(ALTERNATE_ACTION_TRIGGERED_TRANSFORM) -
           GetAnimationDuration(ALTERNATE_ACTION_TRIGGERED_FADE_OUT);
diff --git a/ui/webui/resources/cr_elements/OWNERS b/ui/webui/resources/cr_elements/OWNERS
index 057813143..5fd1662 100644
--- a/ui/webui/resources/cr_elements/OWNERS
+++ b/ui/webui/resources/cr_elements/OWNERS
@@ -1,2 +1,3 @@
 michaelpg@chromium.org
+scottchen@chromium.org
 stevenjb@chromium.org