diff --git a/DEPS b/DEPS
index 4c93b900..429068e9 100644
--- a/DEPS
+++ b/DEPS
@@ -83,7 +83,7 @@
   # 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': '1bc5e494b675aeb5e73cae1d0d3cfad341a05a60',
+  'pdfium_revision': '14fe518ae67eac22cc169517e69092f62a195913',
   # 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.
@@ -605,7 +605,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '34842fa3c36988840c89f5bc6a68503175acf7d9',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '464e89c0ca66c055c0914f8c21cfba386e0c98ec', # commit position 20528
+    Var('webrtc_git') + '/src.git' + '@' + '449d295d1a4e974285f46ab86f3ce099ea586e4f', # commit position 20528
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
diff --git a/base/message_loop/message_loop_unittest.cc b/base/message_loop/message_loop_unittest.cc
index 25718c62..bcc1d30 100644
--- a/base/message_loop/message_loop_unittest.cc
+++ b/base/message_loop/message_loop_unittest.cc
@@ -264,6 +264,10 @@
   static_cast<base::MessageLoopForUI*>(base::MessageLoop::current())->Abort();
 }
 
+void DoNotRun() {
+  ASSERT_TRUE(false);
+}
+
 void RunTest_AbortDontRunMoreTasks(bool delayed, bool init_java_first) {
   WaitableEvent test_done_event(WaitableEvent::ResetPolicy::MANUAL,
                                 WaitableEvent::InitialState::NOT_SIGNALED);
@@ -286,6 +290,8 @@
   } else {
     java_thread->message_loop()->task_runner()->PostTask(
         FROM_HERE, BindOnce(&AbortMessagePump));
+    java_thread->message_loop()->task_runner()->PostTask(FROM_HERE,
+                                                         BindOnce(&DoNotRun));
   }
 
   // Wait to ensure we catch the correct exception (and don't crash)
diff --git a/base/message_loop/message_pump_android.cc b/base/message_loop/message_pump_android.cc
index 5326658..93f40a7 100644
--- a/base/message_loop/message_pump_android.cc
+++ b/base/message_loop/message_pump_android.cc
@@ -35,6 +35,11 @@
   base::MessagePumpForUI* pump =
       reinterpret_cast<base::MessagePumpForUI*>(native_message_pump);
   DCHECK(pump);
+  // If the pump has been aborted, tasks may continue to be queued up, but
+  // shouldn't run.
+  if (pump->ShouldAbort())
+    return;
+
   // This is based on MessagePumpForUI::DoRunLoop() from desktop.
   // Note however that our system queue is handled in the java side.
   // In desktop we inspect and process a single system message and then
diff --git a/chrome/browser/resource_coordinator/tab_manager_web_contents_data.cc b/chrome/browser/resource_coordinator/tab_manager_web_contents_data.cc
index 58d3744..3d18f233e 100644
--- a/chrome/browser/resource_coordinator/tab_manager_web_contents_data.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_web_contents_data.cc
@@ -79,6 +79,7 @@
     return;
   }
 
+  tab_data_.navigation_time = navigation_handle->NavigationStart();
   ukm_source_id_ = ukm::ConvertToSourceId(navigation_handle->GetNavigationId(),
                                           ukm::SourceIdType::NAVIGATION_ID);
 }
@@ -105,6 +106,8 @@
                                base::TimeDelta::FromDays(1), 100);
   }
 
+  ReportUKMWhenTabIsClosed();
+
   if (!web_contents()->IsVisible() &&
       tab_data_.last_inactive_time != base::TimeTicks::UnixEpoch()) {
     ReportUKMWhenBackgroundTabIsClosedOrForegrounded(false);
@@ -228,6 +231,15 @@
   return test_tick_clock_->NowTicks();
 }
 
+void TabManager::WebContentsData::ReportUKMWhenTabIsClosed() {
+  if (!ukm_source_id_)
+    return;
+  auto duration = NowTicks() - tab_data_.navigation_time;
+  ukm::builders::TabManager_TabLifetime(ukm_source_id_)
+      .SetTimeSinceNavigation(duration.InMilliseconds())
+      .Record(ukm::UkmRecorder::Get());
+}
+
 void TabManager::WebContentsData::
     ReportUKMWhenBackgroundTabIsClosedOrForegrounded(bool is_foregrounded) {
   if (!ukm_source_id_)
@@ -243,6 +255,7 @@
     : is_discarded(false),
       discard_count(0),
       is_recently_audible(false),
+      navigation_time(TimeTicks::UnixEpoch()),
       last_audio_change_time(TimeTicks::UnixEpoch()),
       last_discard_time(TimeTicks::UnixEpoch()),
       last_reload_time(TimeTicks::UnixEpoch()),
diff --git a/chrome/browser/resource_coordinator/tab_manager_web_contents_data.h b/chrome/browser/resource_coordinator/tab_manager_web_contents_data.h
index a807171..31dc005 100644
--- a/chrome/browser/resource_coordinator/tab_manager_web_contents_data.h
+++ b/chrome/browser/resource_coordinator/tab_manager_web_contents_data.h
@@ -169,6 +169,9 @@
     int discard_count;
     // Is the tab playing audio?
     bool is_recently_audible;
+    // The navigation time associated with this tab. Useful as a reference time
+    // from which to measure UKM event timings.
+    base::TimeTicks navigation_time;
     // Last time the tab started or stopped playing audio (we record the
     // transition time).
     base::TimeTicks last_audio_change_time;
@@ -195,6 +198,7 @@
   // for more details.
   base::TimeTicks NowTicks() const;
 
+  void ReportUKMWhenTabIsClosed();
   void ReportUKMWhenBackgroundTabIsClosedOrForegrounded(bool is_foregrounded);
 
   // Contains all the needed data for the tab.
diff --git a/chrome/browser/resources/chromeos/arc_support/background.js b/chrome/browser/resources/chromeos/arc_support/background.js
index 60f2def..eda8298 100644
--- a/chrome/browser/resources/chromeos/arc_support/background.js
+++ b/chrome/browser/resources/chromeos/arc_support/background.js
@@ -84,6 +84,8 @@
     if (learnMoreLink) {
       learnMoreLink.addEventListener(
           'click', (event) => this.onLearnMoreLinkClicked(event));
+      learnMoreLink.addEventListener(
+          'keydown', (event) => this.suppressKeyDown(event));
     }
 
     // Create controlled indicator for policy if necessary.
@@ -131,6 +133,15 @@
     showTextOverlay(this.learnMoreContent_);
     event.stopPropagation();
   }
+
+  /**
+   * Called when a key is pressed down on the "Learn More" or "Settings" links.
+   * This prevent propagation of the current event in order to prevent parent
+   * check box toggles its state.
+   */
+  suppressKeyDown(event) {
+    event.stopPropagation();
+  }
 }
 
 /**
@@ -181,9 +192,13 @@
     var learnMoreLink = label.querySelector(this.learnMoreLinkId_);
     learnMoreLink.addEventListener(
         'click', (event) => this.onLearnMoreLinkClicked(event));
+    learnMoreLink.addEventListener(
+        'keydown', (event) => this.suppressKeyDown(event));
     var settingsLink = label.querySelector('#settings-link');
     settingsLink.addEventListener(
         'click', (event) => this.onSettingsLinkClicked(event));
+    settingsLink.addEventListener(
+        'keydown', (event) => this.suppressKeyDown(event));
   }
 
   /** Called when "settings" link is clicked. */
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bubble_controller_unittest.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_bubble_controller_unittest.mm
index 462e5cbb..0be6a70 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_bubble_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bubble_controller_unittest.mm
@@ -33,6 +33,7 @@
 #import "ui/base/cocoa/touch_bar_forward_declarations.h"
 #import "ui/base/cocoa/touch_bar_util.h"
 #include "ui/base/material_design/material_design_controller.h"
+#include "ui/base/ui_base_features.h"
 
 using base::ASCIIToUTF16;
 using bookmarks::BookmarkBubbleObserver;
@@ -84,6 +85,14 @@
     edits_ = 0;
   }
 
+  // CocoaProfileTest:
+  void SetUp() override {
+    CocoaProfileTest::SetUp();
+    // This file only tests Cocoa UI and can be deleted when kSecondaryUiMd is
+    // default.
+    scoped_feature_list_.InitAndDisableFeature(features::kSecondaryUiMd);
+  }
+
   void TearDown() override {
     [controller_ close];
     CocoaProfileTest::TearDown();
@@ -130,6 +139,11 @@
   bool IsWindowClosing() {
     return [static_cast<InfoBubbleWindow*>([controller_ window]) isClosing];
   }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(BookmarkBubbleControllerTest);
 };
 
 // static
diff --git a/chrome/browser/ui/cocoa/extensions/extension_message_bubble_browsertest_mac.mm b/chrome/browser/ui/cocoa/extensions/extension_message_bubble_browsertest_mac.mm
index 723b469..0a2c3c3 100644
--- a/chrome/browser/ui/cocoa/extensions/extension_message_bubble_browsertest_mac.mm
+++ b/chrome/browser/ui/cocoa/extensions/extension_message_bubble_browsertest_mac.mm
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ui/browser_window.h"
 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
 #import "chrome/browser/ui/cocoa/extensions/browser_action_button.h"
@@ -13,6 +14,7 @@
 #include "chrome/browser/ui/extensions/extension_message_bubble_browsertest.h"
 #include "chrome/browser/ui/extensions/settings_api_bubble_helpers.h"
 #include "ui/base/cocoa/cocoa_base_utils.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/events/test/cocoa_test_event_utils.h"
 
 namespace {
@@ -89,9 +91,14 @@
     : public ExtensionMessageBubbleBrowserTest {
  public:
   ExtensionMessageBubbleBrowserTestMac() {}
-  ~ExtensionMessageBubbleBrowserTestMac() override {}
 
   // ExtensionMessageBubbleBrowserTest:
+  void SetUp() override {
+    ExtensionMessageBubbleBrowserTest::SetUp();
+    // This file only tests Cocoa UI and can be deleted when kSecondaryUiMd is
+    // default.
+    scoped_feature_list_.InitAndDisableFeature(features::kSecondaryUiMd);
+  }
   void SetUpCommandLine(base::CommandLine* command_line) override;
 
  private:
@@ -103,6 +110,8 @@
   void ClickActionButton(Browser* browser) override;
   void ClickDismissButton(Browser* browser) override;
 
+  base::test::ScopedFeatureList scoped_feature_list_;
+
   DISALLOW_COPY_AND_ASSIGN(ExtensionMessageBubbleBrowserTestMac);
 };
 
diff --git a/chrome/browser/ui/cocoa/page_info/permission_selector_button_unittest.mm b/chrome/browser/ui/cocoa/page_info/permission_selector_button_unittest.mm
index 89d39ba..039000d 100644
--- a/chrome/browser/ui/cocoa/page_info/permission_selector_button_unittest.mm
+++ b/chrome/browser/ui/cocoa/page_info/permission_selector_button_unittest.mm
@@ -5,10 +5,12 @@
 #import "chrome/browser/ui/cocoa/page_info/permission_selector_button.h"
 
 #include "base/mac/scoped_nsobject.h"
+#include "base/test/scoped_feature_list.h"
 #import "chrome/browser/ui/cocoa/test/cocoa_test_helper.h"
 #include "chrome/browser/ui/page_info/page_info_ui.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "ui/base/ui_base_features.h"
 
 @interface PermissionSelectorButton (Testing)
 - (NSMenu*)permissionMenu;
@@ -39,6 +41,14 @@
     [[test_window() contentView] addSubview:view_];
   }
 
+  // CocoaTest:
+  void SetUp() override {
+    CocoaTest::SetUp();
+    // This file only tests Cocoa UI and can be deleted when kSecondaryUiMd is
+    // default.
+    scoped_feature_list_.InitAndDisableFeature(features::kSecondaryUiMd);
+  }
+
   void Callback(const PageInfoUI::PermissionInfo& permission) {
     EXPECT_TRUE(permission.type == kTestPermissionType);
     got_callback_ = true;
@@ -49,6 +59,11 @@
 
   bool got_callback_;
   base::scoped_nsobject<PermissionSelectorButton> view_;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(PermissionSelectorButtonTest);
 };
 
 TEST_VIEW(PermissionSelectorButtonTest, view_);
diff --git a/chrome/browser/ui/cocoa/passwords/passwords_bubble_browsertest.mm b/chrome/browser/ui/cocoa/passwords/passwords_bubble_browsertest.mm
index ccb44095..cb71280 100644
--- a/chrome/browser/ui/cocoa/passwords/passwords_bubble_browsertest.mm
+++ b/chrome/browser/ui/cocoa/passwords/passwords_bubble_browsertest.mm
@@ -4,6 +4,7 @@
 
 #include "base/mac/foundation_util.h"
 #include "base/mac/scoped_objc_class_swizzler.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
@@ -17,6 +18,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "content/public/test/test_utils.h"
 #include "testing/gtest_mac.h"
+#include "ui/base/ui_base_features.h"
 
 // A helper class to swizzle [NSWindow isKeyWindow] to always return true.
 @interface AlwaysKeyNSWindow : NSWindow
@@ -32,8 +34,13 @@
 // Integration tests for the Mac password bubble.
 class ManagePasswordsBubbleTest : public ManagePasswordsTest {
  public:
+  ManagePasswordsBubbleTest() {}
+
   void SetUpOnMainThread() override {
     ManagePasswordsTest::SetUpOnMainThread();
+    // This file only tests Cocoa UI and can be deleted when kSecondaryUiMd is
+    // default.
+    scoped_feature_list_.InitAndDisableFeature(features::kSecondaryUiMd);
     browser()->window()->Show();
   }
 
@@ -69,6 +76,11 @@
   }
 
   ManagePasswordsIconCocoa* GetView() { return decoration()->icon(); }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(ManagePasswordsBubbleTest);
 };
 
 IN_PROC_BROWSER_TEST_F(ManagePasswordsBubbleTest,
diff --git a/chrome/browser/ui/cocoa/passwords/passwords_bubble_cocoa_unittest.mm b/chrome/browser/ui/cocoa/passwords/passwords_bubble_cocoa_unittest.mm
index 920a68a..c04bb06 100644
--- a/chrome/browser/ui/cocoa/passwords/passwords_bubble_cocoa_unittest.mm
+++ b/chrome/browser/ui/cocoa/passwords/passwords_bubble_cocoa_unittest.mm
@@ -8,6 +8,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/mac/foundation_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_command_controller.h"
@@ -30,15 +31,23 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/gtest_mac.h"
 #include "testing/platform_test.h"
+#include "ui/base/ui_base_features.h"
 
 using testing::Return;
 using testing::ReturnRef;
 
 class ManagePasswordsBubbleCocoaTest : public CocoaProfileTest {
  public:
+  ManagePasswordsBubbleCocoaTest() {}
+
+  // CocoaProfileTest:
   void SetUp() override {
     CocoaProfileTest::SetUp();
 
+    // This file only tests Cocoa UI and can be deleted when kSecondaryUiMd is
+    // default.
+    scoped_feature_list_.InitAndDisableFeature(features::kSecondaryUiMd);
+
     // Create the WebContents.
     siteInstance_ = content::SiteInstance::Create(profile());
     test_web_contents_ = CreateWebContents();
@@ -109,8 +118,11 @@
   }
 
  private:
+  base::test::ScopedFeatureList scoped_feature_list_;
   scoped_refptr<content::SiteInstance> siteInstance_;
   content::WebContents* test_web_contents_;  // weak
+
+  DISALLOW_COPY_AND_ASSIGN(ManagePasswordsBubbleCocoaTest);
 };
 
 TEST_F(ManagePasswordsBubbleCocoaTest, ShowShouldCreateAndShowBubble) {
diff --git a/chrome/browser/ui/cocoa/translate/translate_bubble_controller_unittest.mm b/chrome/browser/ui/cocoa/translate/translate_bubble_controller_unittest.mm
index 5917d46e..08edf87 100644
--- a/chrome/browser/ui/cocoa/translate/translate_bubble_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/translate/translate_bubble_controller_unittest.mm
@@ -6,6 +6,7 @@
 
 #include "base/message_loop/message_loop.h"
 #include "base/test/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #import "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
@@ -15,6 +16,7 @@
 #include "chrome/browser/ui/translate/translate_bubble_model.h"
 #include "chrome/common/url_constants.h"
 #include "content/public/browser/site_instance.h"
+#include "ui/base/ui_base_features.h"
 
 @implementation BrowserWindowController (ForTesting)
 
@@ -38,8 +40,16 @@
 
 class TranslateBubbleControllerTest : public CocoaProfileTest {
  public:
+  TranslateBubbleControllerTest() {}
+
+  // CocoaProfileTest:
   void SetUp() override {
     CocoaProfileTest::SetUp();
+
+    // This file only tests Cocoa UI and can be deleted when kSecondaryUiMd is
+    // default.
+    scoped_feature_list_.InitAndDisableFeature(features::kSecondaryUiMd);
+
     site_instance_ = content::SiteInstance::Create(profile());
 
     NSWindow* nativeWindow = browser()->window()->GetNativeWindow();
@@ -89,9 +99,12 @@
   }
 
  private:
+  base::test::ScopedFeatureList scoped_feature_list_;
   scoped_refptr<content::SiteInstance> site_instance_;
   BrowserWindowController* bwc_;
   content::WebContents* web_contents_;
+
+  DISALLOW_COPY_AND_ASSIGN(TranslateBubbleControllerTest);
 };
 
 TEST_F(TranslateBubbleControllerTest, ShowAndClose) {
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
index caf2c5b..4b602fa 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
 #include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/page_info/chosen_object_row.h"
@@ -22,6 +23,7 @@
 #include "device/usb/mock_usb_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/events/event_utils.h"
 #include "ui/views/controls/button/menu_button.h"
 #include "ui/views/controls/combobox/combobox.h"
@@ -95,6 +97,13 @@
     }
   }
 
+  void SimulateUserSelectingComboboxItemAt(int selector_index, int menu_index) {
+    views::View* view = GetPermissionSelectorAt(selector_index)->button();
+    DCHECK_EQ(views::Combobox::kViewClassName, view->GetClassName());
+    views::Combobox* combobox = static_cast<views::Combobox*>(view);
+    combobox->SetSelectedIndex(menu_index);
+  }
+
   // Simulates updating the number of cookies.
   void SetCookieInfo(const CookieInfoList& list) { view_->SetCookieInfo(list); }
 
@@ -178,26 +187,24 @@
 
 }  // namespace
 
-// TODO(ellyjones): re-enable this test for OSX.
-// This test exercises PermissionSelectorRow in a way that it is not used in
-// practice. In practice, every setting in PermissionSelectorRow starts off
-// "set", so there is always one option checked in the resulting MenuModel. This
-// test creates settings that are left at their defaults, leading to zero
-// checked options, and checks that the text on the MenuButtons is right. Since
-// the Comboboxes the MacViews version of this dialog uses don't have separate
-// text, this test doesn't work.
-#if defined(OS_MACOSX)
-#define MAYBE_SetPermissionInfo DISABLED_SetPermissionInfo
-#else
-#define MAYBE_SetPermissionInfo SetPermissionInfo
-#endif
-
 // Each permission selector row is like this: [icon] [label] [selector]
 constexpr int kViewsPerPermissionRow = 3;
 
 // Test UI construction and reconstruction via
 // PageInfoBubbleView::SetPermissionInfo().
-TEST_F(PageInfoBubbleViewTest, MAYBE_SetPermissionInfo) {
+TEST_F(PageInfoBubbleViewTest, SetPermissionInfo) {
+  // This test exercises PermissionSelectorRow in a way that it is not used in
+  // practice. In practice, every setting in PermissionSelectorRow starts off
+  // "set", so there is always one option checked in the resulting MenuModel.
+  // This test creates settings that are left at their defaults, leading to zero
+  // checked options, and checks that the text on the MenuButtons is right. On
+  // Mac, the behavior matches, but only when SecondaryUiMd is enabled.
+  const bool is_md = base::FeatureList::IsEnabled(features::kSecondaryUiMd);
+#if defined(OS_MACOSX)
+  if (!is_md)
+    return;
+#endif
+
   PermissionInfoList list(1);
   list.back().type = CONTENT_SETTINGS_TYPE_GEOLOCATION;
   list.back().source = content_settings::SETTING_SOURCE_USER;
@@ -227,16 +234,32 @@
 
   // Simulate a user selection via the UI. Note this will also cover logic in
   // PageInfo to update the pref.
-  list.back().setting = CONTENT_SETTING_ALLOW;
-  api_->GetPermissionSelectorAt(0)->PermissionChanged(list.back());
+  if (is_md) {
+    // Under MD, the changed setting is always read from the combobox selected
+    // index when changed in the UI. PermissionSelectorRow::PermissionChanged()
+    // ignores the argument, except to detect whether it is the default.
+    api_->SimulateUserSelectingComboboxItemAt(0, 1);
+  } else {
+    list.back().setting = CONTENT_SETTING_ALLOW;
+    api_->GetPermissionSelectorAt(0)->PermissionChanged(list.back());
+  }
   EXPECT_EQ(num_expected_children, api_->permissions_view()->child_count());
   EXPECT_EQ(base::ASCIIToUTF16("Allow"), api_->GetPermissionButtonTextAt(0));
 
   // Setting to the default via the UI should keep the button around.
-  list.back().setting = CONTENT_SETTING_ASK;
-  api_->GetPermissionSelectorAt(0)->PermissionChanged(list.back());
+  if (is_md) {
+    api_->SimulateUserSelectingComboboxItemAt(0, 0);
+    // Under MD, PermissionMenuModel gets strings using PageInfoUI::
+    // PermissionActionToUIString(..) rather than directly from the
+    // ResourceBundle.
+    EXPECT_EQ(base::ASCIIToUTF16("Ask (default)"),
+              api_->GetPermissionButtonTextAt(0));
+  } else {
+    list.back().setting = CONTENT_SETTING_ASK;
+    api_->GetPermissionSelectorAt(0)->PermissionChanged(list.back());
+    EXPECT_EQ(base::ASCIIToUTF16("Ask"), api_->GetPermissionButtonTextAt(0));
+  }
   EXPECT_EQ(num_expected_children, api_->permissions_view()->child_count());
-  EXPECT_EQ(base::ASCIIToUTF16("Ask"), api_->GetPermissionButtonTextAt(0));
 
   // However, since the setting is now default, recreating the dialog with those
   // settings should omit the permission from the UI.
diff --git a/chrome/browser/ui/views/page_info/permission_selector_row.cc b/chrome/browser/ui/views/page_info/permission_selector_row.cc
index 195d898..2fba9b4 100644
--- a/chrome/browser/ui/views/page_info/permission_selector_row.cc
+++ b/chrome/browser/ui/views/page_info/permission_selector_row.cc
@@ -345,9 +345,7 @@
   //
   // Technically, the MenuButton has the same problem, but MenuButton doesn't
   // use its model in its destructor.
-  if (combobox_) {
-    combobox_->parent()->RemoveChildView(combobox_);
-  }
+  delete combobox_;
 }
 
 // static
diff --git a/chrome/browser/vr/elements/reticle.cc b/chrome/browser/vr/elements/reticle.cc
index f682d6c..1d7f9af 100644
--- a/chrome/browser/vr/elements/reticle.cc
+++ b/chrome/browser/vr/elements/reticle.cc
@@ -20,6 +20,10 @@
 
 Reticle::~Reticle() = default;
 
+UiElement* Reticle::TargetElement() const {
+  return scene_->GetUiElementById(model_->reticle.target_element_id);
+}
+
 void Reticle::Render(UiElementRenderer* renderer,
                      const gfx::Transform& model_view_proj_matrix) const {
   // Scale the reticle to have a fixed FOV size at any distance.
@@ -31,9 +35,7 @@
 
   gfx::Quaternion rotation;
 
-  UiElement* target =
-      scene_->GetUiElementById(model_->reticle.target_element_id);
-
+  UiElement* target = TargetElement();
   if (target) {
     // Make the reticle planar to the element it's hitting.
     rotation = gfx::Quaternion(gfx::Vector3dF(0.0f, 0.0f, -1.0f),
diff --git a/chrome/browser/vr/elements/reticle.h b/chrome/browser/vr/elements/reticle.h
index 144c506..9e3edf13 100644
--- a/chrome/browser/vr/elements/reticle.h
+++ b/chrome/browser/vr/elements/reticle.h
@@ -18,6 +18,8 @@
   Reticle(UiScene* scene, Model* model);
   ~Reticle() override;
 
+  UiElement* TargetElement() const;
+
  private:
   void Render(UiElementRenderer* renderer,
               const gfx::Transform& model_view_proj_matrix) const final;
diff --git a/chrome/browser/vr/ui_renderer_unittest.cc b/chrome/browser/vr/ui_renderer_unittest.cc
index 0841991..c36f2c3 100644
--- a/chrome/browser/vr/ui_renderer_unittest.cc
+++ b/chrome/browser/vr/ui_renderer_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/time/time.h"
 #include "chrome/browser/vr/elements/ui_element.h"
+#include "chrome/browser/vr/model/model.h"
 #include "chrome/browser/vr/test/animation_utils.h"
 #include "chrome/browser/vr/test/constants.h"
 #include "chrome/browser/vr/test/ui_scene_manager_test.h"
@@ -35,7 +36,34 @@
   }
 };
 
+TEST_F(UiRendererTest, ReticleStacking) {
+  UiElement* content = scene_->GetUiElementByName(kContentQuad);
+  EXPECT_TRUE(content);
+  model_->reticle.target_element_id = content->id();
+  auto unsorted = scene_->GetVisible2dBrowsingElements();
+  auto sorted = UiRenderer::GetElementsInDrawOrder(unsorted);
+  bool saw_target = false;
+  for (auto* e : sorted) {
+    if (e == content) {
+      saw_target = true;
+    } else if (saw_target) {
+      EXPECT_EQ(kReticle, e->name());
+      break;
+    }
+  }
+
+  auto controller_elements = scene_->GetVisibleControllerElements();
+  bool saw_reticle = false;
+  for (auto* e : controller_elements) {
+    if (e->name() == kReticle) {
+      saw_reticle = true;
+    }
+  }
+  EXPECT_FALSE(saw_reticle);
+}
+
 TEST_P(UiRendererTest, UiRendererSortingTest) {
+  model_->reticle.target_element_id = 0;
   auto unsorted = ((*scene_).*GetParam().f)();
   auto sorted = UiRenderer::GetElementsInDrawOrder(unsorted);
 
diff --git a/chrome/browser/vr/ui_scene.cc b/chrome/browser/vr/ui_scene.cc
index 7cbc253..1f0393d 100644
--- a/chrome/browser/vr/ui_scene.cc
+++ b/chrome/browser/vr/ui_scene.cc
@@ -14,6 +14,7 @@
 #include "base/values.h"
 #include "chrome/browser/vr/databinding/binding_base.h"
 #include "chrome/browser/vr/elements/draw_phase.h"
+#include "chrome/browser/vr/elements/reticle.h"
 #include "chrome/browser/vr/elements/ui_element.h"
 #include "ui/gfx/transform.h"
 
@@ -22,11 +23,18 @@
 namespace {
 
 template <typename P>
-UiScene::Elements GetVisibleElements(UiElement* root, P predicate) {
+UiScene::Elements GetVisibleElements(UiElement* root,
+                                     UiElement* reticle_element,
+                                     P predicate) {
+  Reticle* reticle = static_cast<Reticle*>(reticle_element);
+  UiElement* target = reticle ? reticle->TargetElement() : nullptr;
   UiScene::Elements elements;
   for (auto& element : *root) {
-    if (element.IsVisible() && predicate(&element))
+    if (element.IsVisible() && predicate(&element)) {
       elements.push_back(&element);
+      if (target && target->id() == element.id())
+        elements.push_back(reticle);
+    }
   }
   return elements;
 }
@@ -182,7 +190,8 @@
 
 UiScene::Elements UiScene::GetVisible2dBrowsingElements() const {
   return GetVisibleElements(
-      GetUiElementByName(k2dBrowsingRoot), [](UiElement* element) {
+      GetUiElementByName(k2dBrowsingRoot), GetUiElementByName(kReticle),
+      [](UiElement* element) {
         return element->draw_phase() == kPhaseForeground ||
                element->draw_phase() == kPhaseFloorCeiling ||
                element->draw_phase() == kPhaseBackground;
@@ -191,7 +200,8 @@
 
 UiScene::Elements UiScene::GetVisible2dBrowsingOverlayElements() const {
   return GetVisibleElements(
-      GetUiElementByName(k2dBrowsingRoot), [](UiElement* element) {
+      GetUiElementByName(k2dBrowsingRoot), GetUiElementByName(kReticle),
+      [](UiElement* element) {
         return element->draw_phase() == kPhaseOverlayBackground ||
                element->draw_phase() == kPhaseOverlayForeground;
       });
@@ -199,7 +209,8 @@
 
 UiScene::Elements UiScene::GetVisibleSplashScreenElements() const {
   return GetVisibleElements(
-      GetUiElementByName(kSplashScreenRoot), [](UiElement* element) {
+      GetUiElementByName(kSplashScreenRoot), GetUiElementByName(kReticle),
+      [](UiElement* element) {
         return element->draw_phase() == kPhaseOverlayBackground ||
                element->draw_phase() == kPhaseOverlayForeground;
       });
@@ -207,16 +218,23 @@
 
 UiScene::Elements UiScene::GetVisibleWebVrOverlayForegroundElements() const {
   return GetVisibleElements(
-      GetUiElementByName(kWebVrRoot), [](UiElement* element) {
+      GetUiElementByName(kWebVrRoot), GetUiElementByName(kReticle),
+      [](UiElement* element) {
         return element->draw_phase() == kPhaseOverlayForeground;
       });
 }
 
 UiScene::Elements UiScene::GetVisibleControllerElements() const {
-  return GetVisibleElements(GetUiElementByName(kControllerGroup),
-                            [](UiElement* element) {
-                              return element->draw_phase() == kPhaseForeground;
-                            });
+  return GetVisibleElements(
+      GetUiElementByName(kControllerGroup), nullptr, [](UiElement* element) {
+        if (element->name() == kReticle) {
+          Reticle* reticle = static_cast<Reticle*>(element);
+          // If the reticle has a non-null target element,
+          // it would have been positioned elsewhere.
+          return !reticle->TargetElement();
+        }
+        return element->draw_phase() == kPhaseForeground;
+      });
 }
 
 UiScene::UiScene() {
diff --git a/components/machine_intelligence/proto/translate_ranker_model.proto b/components/machine_intelligence/proto/translate_ranker_model.proto
index 3551cd009..9954492 100644
--- a/components/machine_intelligence/proto/translate_ranker_model.proto
+++ b/components/machine_intelligence/proto/translate_ranker_model.proto
@@ -17,7 +17,7 @@
   optional uint32 version = 1;
 
   // Defines the weights and bias of a Logistic Regression Model.
-  message LogisticRegressionModel {
+  message TranslateLogisticRegressionModel {
     // Decision threshold. If not defined, use 0.5.
     optional float threshold = 12;
 
@@ -41,6 +41,6 @@
   }
 
   oneof model_revision {
-    LogisticRegressionModel logistic_regression_model = 2;
+    TranslateLogisticRegressionModel translate_logistic_regression_model = 2;
   }
 }
diff --git a/components/machine_intelligence/ranker_model_loader_unittest.cc b/components/machine_intelligence/ranker_model_loader_unittest.cc
index 4081d6e..9b70122e 100644
--- a/components/machine_intelligence/ranker_model_loader_unittest.cc
+++ b/components/machine_intelligence/ranker_model_loader_unittest.cc
@@ -226,7 +226,7 @@
   auto* translate = model->mutable_proto()->mutable_translate();
   translate->set_version(1);
 
-  auto* logit = translate->mutable_logistic_regression_model();
+  auto* logit = translate->mutable_translate_logistic_regression_model();
   logit->set_bias(0.1f);
   logit->set_accept_ratio_weight(0.2f);
   logit->set_decline_ratio_weight(0.3f);
diff --git a/components/machine_intelligence/ranker_model_unittest.cc b/components/machine_intelligence/ranker_model_unittest.cc
index 64e43fb..dd64f5d9 100644
--- a/components/machine_intelligence/ranker_model_unittest.cc
+++ b/components/machine_intelligence/ranker_model_unittest.cc
@@ -36,7 +36,7 @@
   auto* translate = model->mutable_proto()->mutable_translate();
   translate->set_version(1);
 
-  auto* logit = translate->mutable_logistic_regression_model();
+  auto* logit = translate->mutable_translate_logistic_regression_model();
   logit->set_bias(0.1f);
   logit->set_accept_ratio_weight(0.2f);
   logit->set_decline_ratio_weight(0.3f);
diff --git a/components/policy/proto/chrome_device_policy.proto b/components/policy/proto/chrome_device_policy.proto
index 43e0848..47cdb5d 100644
--- a/components/policy/proto/chrome_device_policy.proto
+++ b/components/policy/proto/chrome_device_policy.proto
@@ -900,6 +900,33 @@
   optional bool allow_user_initiated_powerwash = 1;
 }
 
+message ForcedReenrollmentProto {
+  // Keep in sync with the backend enum (chrome_service_settings.proto).
+  enum ForcedReenrollmentMode {
+    // Default value if the mode cannot be parsed.
+    FORCED_REENROLLMENT_MODE_UNSPECIFIED = 0;
+
+    // Force user to re-enroll into the same domain that the device
+    // was enrolled in before the wipe. The device will use attestation-
+    // based enrollment if possible.
+    FORCED = 1;
+
+    // Suggest user to re-enroll into the same domain the device was
+    // enrolled in before the wipe. User can opt to enroll this device
+    // as consumer device instead, or enroll to another domain.
+    SUGGESTED = 2;
+
+    // No forced re-enrollment.
+    NONE = 3;
+  }
+
+  optional ForcedReenrollmentMode forced_reenrollment_mode = 1;
+
+  // Indicate whether the server needs an enrollment ID for attestation-based
+  // enrollment.
+  optional bool enrollment_id_needed = 2;
+}
+
 // Settings to control the minimum version that is allowed to sign in / stay
 // in session.
 message MinimumRequiredVersionProto {
@@ -980,4 +1007,5 @@
       58;
   optional TPMFirmwareUpdateSettingsProto tpm_firmware_update_settings = 59;
   optional MinimumRequiredVersionProto minimum_required_version = 60;
+  optional ForcedReenrollmentProto forced_reenrollment = 61;
 }
diff --git a/components/translate/core/browser/translate_ranker_impl.cc b/components/translate/core/browser/translate_ranker_impl.cc
index 68dd82753..e8e718e9 100644
--- a/components/translate/core/browser/translate_ranker_impl.cc
+++ b/components/translate/core/browser/translate_ranker_impl.cc
@@ -65,7 +65,7 @@
     return RankerModelStatus::VALIDATION_FAILED;
 
   if (model.proto().translate().model_revision_case() !=
-      TranslateRankerModel::kLogisticRegressionModel) {
+      TranslateRankerModel::kTranslateLogisticRegressionModel) {
     return RankerModelStatus::INCOMPATIBLE;
   }
 
@@ -276,8 +276,8 @@
   // logic here.
   const TranslateRankerFeatures features(translate_event);
 
-  const TranslateRankerModel::LogisticRegressionModel& lr_model =
-      model_->proto().translate().logistic_regression_model();
+  const TranslateRankerModel::TranslateLogisticRegressionModel& lr_model =
+      model_->proto().translate().translate_logistic_regression_model();
 
   double dot_product =
       (features.accepted_count * lr_model.accept_count_weight()) +
diff --git a/components/translate/core/browser/translate_ranker_impl_unittest.cc b/components/translate/core/browser/translate_ranker_impl_unittest.cc
index 3218b3d1..466d3fa 100644
--- a/components/translate/core/browser/translate_ranker_impl_unittest.cc
+++ b/components/translate/core/browser/translate_ranker_impl_unittest.cc
@@ -99,7 +99,7 @@
   model->mutable_proto()->mutable_translate()->set_version(kModelVersion);
   auto* details = model->mutable_proto()
                       ->mutable_translate()
-                      ->mutable_logistic_regression_model();
+                      ->mutable_translate_logistic_regression_model();
   if (threshold > 0.0) {
     details->set_threshold(threshold);
   }
diff --git a/docs/use_find_bugs_for_android.md b/docs/use_find_bugs_for_android.md
index 1245d92c..d812118 100644
--- a/docs/use_find_bugs_for_android.md
+++ b/docs/use_find_bugs_for_android.md
@@ -41,5 +41,5 @@
 
 *   [android_clang_dbg_recipe](https://build.chromium.org/p/tryserver.chromium.linux/builders/android_clang_dbg_recipe)
     on the commit queue
-*   [Android Clang Builder (dbg)](https://build.chromium.org/p/chromium.linux/builders/Android%20Clang%20Builder%20\(dbg\))
+*   [Android ASAN (dbg)](https://build.chromium.org/p/chromium.android/builders/Android%20ASAN%20%28dbg%29)
     on the main waterfall
diff --git a/media/formats/mp4/box_reader.h b/media/formats/mp4/box_reader.h
index 7d159ba9..140818a 100644
--- a/media/formats/mp4/box_reader.h
+++ b/media/formats/mp4/box_reader.h
@@ -45,7 +45,8 @@
   bool HasBytes(size_t count) {
     // As the size of a box is implementation limited to 2^31, fail if
     // attempting to check for too many bytes.
-    const size_t impl_limit = 1 << 31;
+    const size_t impl_limit =
+        static_cast<size_t>(std::numeric_limits<int32_t>::max());
     return pos_ <= buf_size_ && count <= impl_limit &&
            count <= buf_size_ - pos_;
   }
diff --git a/net/quic/chromium/quic_chromium_client_session.cc b/net/quic/chromium/quic_chromium_client_session.cc
index 423ba1c4..05d293d 100644
--- a/net/quic/chromium/quic_chromium_client_session.cc
+++ b/net/quic/chromium/quic_chromium_client_session.cc
@@ -95,6 +95,14 @@
                             NUM_LOCATIONS);
 }
 
+std::unique_ptr<base::Value> NetLogQuicConnectionMigrationTriggerCallback(
+    std::string trigger,
+    NetLogCaptureMode capture_mode) {
+  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+  dict->SetString("trigger", trigger);
+  return std::move(dict);
+}
+
 std::unique_ptr<base::Value> NetLogQuicConnectionMigrationFailureCallback(
     QuicConnectionId connection_id,
     std::string reason,
@@ -1467,9 +1475,21 @@
     return;
 
   MigrationResult result = MigrationResult::FAILURE;
-  if (stream_factory_ != nullptr)
-    result = stream_factory_->MaybeMigrateSingleSessionOnWriteError(this,
-                                                                    error_code);
+  if (stream_factory_ != nullptr) {
+    stream_factory_->CollectDataOnWriteError(error_code);
+
+    const NetLogWithSource migration_net_log = NetLogWithSource::Make(
+        net_log_.net_log(), NetLogSourceType::QUIC_CONNECTION_MIGRATION);
+    migration_net_log.BeginEvent(
+        NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED,
+        base::Bind(&NetLogQuicConnectionMigrationTriggerCallback,
+                   "WriteError"));
+
+    result = MigrateToAlternateNetwork(/*close_session_on_error*/ false,
+                                       migration_net_log);
+    migration_net_log.EndEvent(
+        NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED);
+  }
 
   if (result == MigrationResult::SUCCESS)
     return;
@@ -1572,7 +1592,24 @@
 
 void QuicChromiumClientSession::OnPathDegrading() {
   if (stream_factory_) {
-    stream_factory_->MaybeMigrateSingleSessionOnPathDegrading(this);
+    stream_factory_->CollectDataOnPathDegrading();
+
+    const NetLogWithSource migration_net_log = NetLogWithSource::Make(
+        net_log_.net_log(), NetLogSourceType::QUIC_CONNECTION_MIGRATION);
+    migration_net_log.BeginEvent(
+        NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED,
+        base::Bind(&NetLogQuicConnectionMigrationTriggerCallback,
+                   "PathDegrading"));
+    if (migrate_session_early_) {
+      MigrateToAlternateNetwork(/*close_session_on_error*/ true,
+                                migration_net_log);
+    } else {
+      HistogramAndLogMigrationFailure(migration_net_log,
+                                      MIGRATION_STATUS_DISABLED,
+                                      connection_id(), "Migration disabled");
+    }
+    migration_net_log.EndEvent(
+        NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED);
   }
 }
 
diff --git a/net/quic/chromium/quic_stream_factory.cc b/net/quic/chromium/quic_stream_factory.cc
index dcea1ea..f7a4324 100644
--- a/net/quic/chromium/quic_stream_factory.cc
+++ b/net/quic/chromium/quic_stream_factory.cc
@@ -120,16 +120,6 @@
   return std::move(dict);
 }
 
-std::unique_ptr<base::Value> NetLogQuicConnectionMigrationFailureCallback(
-    QuicConnectionId connection_id,
-    std::string reason,
-    NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("connection_id", base::Uint64ToString(connection_id));
-  dict->SetString("reason", reason);
-  return std::move(dict);
-}
-
 // Helper class that is used to log a connection migration event.
 class ScopedConnectionMigrationEventLog {
  public:
@@ -157,17 +147,6 @@
                             CREATION_ERROR_MAX);
 }
 
-void HistogramAndLogMigrationFailure(const NetLogWithSource& net_log,
-                                     enum QuicConnectionMigrationStatus status,
-                                     QuicConnectionId connection_id,
-                                     std::string reason) {
-  UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.ConnectionMigration", status,
-                            MIGRATION_STATUS_MAX);
-  net_log.AddEvent(NetLogEventType::QUIC_CONNECTION_MIGRATION_FAILURE,
-                   base::Bind(&NetLogQuicConnectionMigrationFailureCallback,
-                              connection_id, reason));
-}
-
 void LogPlatformNotificationInHistogram(
     enum QuicPlatformNotification notification) {
   UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.PlatformNotification",
@@ -1294,49 +1273,14 @@
   }
 }
 
-MigrationResult QuicStreamFactory::MaybeMigrateSingleSessionOnWriteError(
-    QuicChromiumClientSession* session,
-    int error_code) {
+void QuicStreamFactory::CollectDataOnWriteError(int error_code) {
   most_recent_write_error_timestamp_ = base::TimeTicks::Now();
   most_recent_write_error_ = error_code;
-
-  const NetLogWithSource migration_net_log = NetLogWithSource::Make(
-      net_log_, NetLogSourceType::QUIC_CONNECTION_MIGRATION);
-  migration_net_log.BeginEvent(
-      NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED,
-      base::Bind(&NetLogQuicConnectionMigrationTriggerCallback, "WriteError"));
-
-  MigrationResult result = session->MigrateToAlternateNetwork(
-      /*close_session_on_error*/ false, migration_net_log);
-  migration_net_log.EndEvent(
-      NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED);
-  return result;
 }
 
-MigrationResult QuicStreamFactory::MaybeMigrateSingleSessionOnPathDegrading(
-    QuicChromiumClientSession* session) {
+void QuicStreamFactory::CollectDataOnPathDegrading() {
   if (most_recent_path_degrading_timestamp_ == base::TimeTicks())
     most_recent_path_degrading_timestamp_ = base::TimeTicks::Now();
-
-  const NetLogWithSource migration_net_log = NetLogWithSource::Make(
-      net_log_, NetLogSourceType::QUIC_CONNECTION_MIGRATION);
-  migration_net_log.BeginEvent(
-      NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED,
-      base::Bind(&NetLogQuicConnectionMigrationTriggerCallback,
-                 "PathDegrading"));
-
-  MigrationResult result = MigrationResult::FAILURE;
-  if (migrate_sessions_early_) {
-    result = session->MigrateToAlternateNetwork(
-        /*close_session_on_error*/ true, migration_net_log);
-  } else {
-    HistogramAndLogMigrationFailure(
-        migration_net_log, MIGRATION_STATUS_DISABLED, session->connection_id(),
-        "Migration disabled");
-  }
-  migration_net_log.EndEvent(
-      NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED);
-  return result;
 }
 
 void QuicStreamFactory::MigrateSessionToNewPeerAddress(
diff --git a/net/quic/chromium/quic_stream_factory.h b/net/quic/chromium/quic_stream_factory.h
index c51c2ff..ac09048 100644
--- a/net/quic/chromium/quic_stream_factory.h
+++ b/net/quic/chromium/quic_stream_factory.h
@@ -303,18 +303,15 @@
       bool close_if_cannot_migrate,
       const NetLogWithSource& net_log);
 
-  // Method that attempts migration of |session| on write error with
-  // |error_code| if |session| is active and if there is an alternate network
-  // than the one to which |session| is currently bound.
-  MigrationResult MaybeMigrateSingleSessionOnWriteError(
-      QuicChromiumClientSession* session,
-      int error_code);
+  // TODO(zhongyi): move metrics collection to session once connection migration
+  // logic is all in QuicChromiumClientSession.
+  // Method that collects error code data on write error.
+  void CollectDataOnWriteError(int error_code);
 
-  // Method that attempts migration of |session| on path degrading if |session|
-  // is active and if there is an alternate network than the one to which
-  // |session| is currently bound.
-  MigrationResult MaybeMigrateSingleSessionOnPathDegrading(
-      QuicChromiumClientSession* session);
+  // TODO(zhongyi): move metrics collection to session once connection migration
+  // logic is all in QuicChromiumClientSession.
+  // Method that collects timestamp when some session is on path degrading.
+  void CollectDataOnPathDegrading();
 
   // Migrates |session| over to using |peer_address|. Causes a PING frame
   // to be sent to the new peer address.
diff --git a/services/resource_coordinator/coordination_unit/coordination_unit_base.cc b/services/resource_coordinator/coordination_unit/coordination_unit_base.cc
index e0b5c799..721bbc9b 100644
--- a/services/resource_coordinator/coordination_unit/coordination_unit_base.cc
+++ b/services/resource_coordinator/coordination_unit/coordination_unit_base.cc
@@ -120,7 +120,6 @@
   // and propagated to the appropriate associated |CoordianationUnitBase|
   // before |OnPropertyChanged| is invoked on all of the registered observers.
   properties_[property_type] = value;
-  PropagateProperty(property_type, value);
   OnPropertyChanged(property_type, value);
 }
 
diff --git a/services/resource_coordinator/coordination_unit/coordination_unit_base.h b/services/resource_coordinator/coordination_unit/coordination_unit_base.h
index 513c7207..7871bea7 100644
--- a/services/resource_coordinator/coordination_unit/coordination_unit_base.h
+++ b/services/resource_coordinator/coordination_unit/coordination_unit_base.h
@@ -32,9 +32,6 @@
   static void AssertNoActiveCoordinationUnits();
   static void ClearAllCoordinationUnits();
 
-  // Recalculate property internally.
-  virtual void RecalculateProperty(const mojom::PropertyType property_type) {}
-
   CoordinationUnitBase(const CoordinationUnitID& id);
   virtual ~CoordinationUnitBase();
 
@@ -64,9 +61,6 @@
   static std::vector<CoordinationUnitBase*> GetCoordinationUnitsOfType(
       CoordinationUnitType cu_type);
 
-  // Propagate property change to relevant |CoordinationUnitBase| instances.
-  virtual void PropagateProperty(mojom::PropertyType property_type,
-                                 int64_t value) {}
   virtual void OnEventReceived(mojom::Event event);
   virtual void OnPropertyChanged(mojom::PropertyType property_type,
                                  int64_t value);
diff --git a/services/resource_coordinator/coordination_unit/page_coordination_unit_impl.cc b/services/resource_coordinator/coordination_unit/page_coordination_unit_impl.cc
index 85124f37..fb1fc48c 100644
--- a/services/resource_coordinator/coordination_unit/page_coordination_unit_impl.cc
+++ b/services/resource_coordinator/coordination_unit/page_coordination_unit_impl.cc
@@ -63,25 +63,6 @@
   SendEvent(mojom::Event::kNavigationCommitted);
 }
 
-void PageCoordinationUnitImpl::RecalculateProperty(
-    const mojom::PropertyType property_type) {
-  switch (property_type) {
-    case mojom::PropertyType::kCPUUsage: {
-      double cpu_usage = CalculateCPUUsage();
-      SetProperty(mojom::PropertyType::kCPUUsage, cpu_usage);
-      break;
-    }
-    case mojom::PropertyType::kExpectedTaskQueueingDuration: {
-      int64_t eqt;
-      if (CalculateExpectedTaskQueueingDuration(&eqt))
-        SetProperty(property_type, eqt);
-      break;
-    }
-    default:
-      NOTREACHED();
-  }
-}
-
 std::set<ProcessCoordinationUnitImpl*>
 PageCoordinationUnitImpl::GetAssociatedProcessCoordinationUnits() const {
   std::set<ProcessCoordinationUnitImpl*> process_cus;
@@ -101,6 +82,38 @@
   return is_visible;
 }
 
+double PageCoordinationUnitImpl::GetCPUUsage() const {
+  double cpu_usage = 0.0;
+
+  for (auto* process_cu : GetAssociatedProcessCoordinationUnits()) {
+    size_t pages_in_process =
+        process_cu->GetAssociatedPageCoordinationUnits().size();
+    DCHECK_LE(1u, pages_in_process);
+
+    int64_t process_cpu_usage;
+    if (process_cu->GetProperty(mojom::PropertyType::kCPUUsage,
+                                &process_cpu_usage)) {
+      cpu_usage += static_cast<double>(process_cpu_usage) / pages_in_process;
+    }
+  }
+
+  return cpu_usage / 1000;
+}
+
+bool PageCoordinationUnitImpl::GetExpectedTaskQueueingDuration(
+    int64_t* output) {
+  // Calculate the EQT for the process of the main frame only because
+  // the smoothness of the main frame may affect the users the most.
+  FrameCoordinationUnitImpl* main_frame_cu = GetMainFrameCoordinationUnit();
+  if (!main_frame_cu)
+    return false;
+  auto* process_cu = main_frame_cu->GetProcessCoordinationUnit();
+  if (!process_cu)
+    return false;
+  return process_cu->GetProperty(
+      mojom::PropertyType::kExpectedTaskQueueingDuration, output);
+}
+
 base::TimeDelta PageCoordinationUnitImpl::TimeSinceLastNavigation() const {
   if (navigation_committed_time_.is_null())
     return base::TimeDelta();
@@ -153,36 +166,4 @@
   return nullptr;
 }
 
-bool PageCoordinationUnitImpl::CalculateExpectedTaskQueueingDuration(
-    int64_t* output) {
-  // Calculate the EQT for the process of the main frame only because
-  // the smoothness of the main frame may affect the users the most.
-  FrameCoordinationUnitImpl* main_frame_cu = GetMainFrameCoordinationUnit();
-  if (!main_frame_cu)
-    return false;
-  auto* associated_process = main_frame_cu->GetProcessCoordinationUnit();
-  if (!associated_process)
-    return false;
-  return associated_process->GetProperty(
-      mojom::PropertyType::kExpectedTaskQueueingDuration, output);
-}
-
-double PageCoordinationUnitImpl::CalculateCPUUsage() {
-  double cpu_usage = 0.0;
-
-  for (auto* process_cu : GetAssociatedProcessCoordinationUnits()) {
-    size_t pages_in_process =
-        process_cu->GetAssociatedPageCoordinationUnits().size();
-    DCHECK_LE(1u, pages_in_process);
-
-    int64_t process_cpu_usage;
-    if (process_cu->GetProperty(mojom::PropertyType::kCPUUsage,
-                                &process_cpu_usage)) {
-      cpu_usage += static_cast<double>(process_cpu_usage) / pages_in_process;
-    }
-  }
-
-  return cpu_usage;
-}
-
 }  // namespace resource_coordinator
diff --git a/services/resource_coordinator/coordination_unit/page_coordination_unit_impl.h b/services/resource_coordinator/coordination_unit/page_coordination_unit_impl.h
index e88b2c5..673da80 100644
--- a/services/resource_coordinator/coordination_unit/page_coordination_unit_impl.h
+++ b/services/resource_coordinator/coordination_unit/page_coordination_unit_impl.h
@@ -36,15 +36,16 @@
   void OnTitleUpdated() override;
   void OnMainFrameNavigationCommitted() override;
 
-  // CoordinationUnitBase implementation.
-  void RecalculateProperty(const mojom::PropertyType property_type) override;
-
   // There is no direct relationship between processes and pages. However,
   // frames are accessible by both processes and frames, so we find all of the
   // processes that are reachable from the pages's accessible frames.
   std::set<ProcessCoordinationUnitImpl*> GetAssociatedProcessCoordinationUnits()
       const;
   bool IsVisible() const;
+  double GetCPUUsage() const;
+
+  // Returns false if can't get an expected task queueing duration successfully.
+  bool GetExpectedTaskQueueingDuration(int64_t* duration);
 
   // Returns 0 if no navigation has happened, otherwise returns the time since
   // the last navigation commit.
@@ -73,10 +74,6 @@
   bool AddFrame(FrameCoordinationUnitImpl* frame_cu);
   bool RemoveFrame(FrameCoordinationUnitImpl* frame_cu);
 
-  // Returns true for a valid value. Returns false otherwise.
-  bool CalculateExpectedTaskQueueingDuration(int64_t* output);
-  double CalculateCPUUsage();
-
   // Returns the main frame CU or nullptr if this page has no main frame.
   FrameCoordinationUnitImpl* GetMainFrameCoordinationUnit();
 
diff --git a/services/resource_coordinator/coordination_unit/page_coordination_unit_impl_unittest.cc b/services/resource_coordinator/coordination_unit/page_coordination_unit_impl_unittest.cc
index f017af16..a90230c 100644
--- a/services/resource_coordinator/coordination_unit/page_coordination_unit_impl_unittest.cc
+++ b/services/resource_coordinator/coordination_unit/page_coordination_unit_impl_unittest.cc
@@ -71,57 +71,33 @@
 TEST_F(PageCoordinationUnitImplTest,
        CalculatePageCPUUsageForSinglePageInSingleProcess) {
   MockSinglePageInSingleProcessCoordinationUnitGraph cu_graph;
-
   cu_graph.process->SetCPUUsage(40);
-
-  int64_t cpu_usage;
-  EXPECT_TRUE(
-      cu_graph.page->GetProperty(mojom::PropertyType::kCPUUsage, &cpu_usage));
-  EXPECT_EQ(40, cpu_usage / 1000);
+  EXPECT_EQ(40, cu_graph.page->GetCPUUsage());
 }
 
 TEST_F(PageCoordinationUnitImplTest,
        CalculatePageCPUUsageForMultiplePagesInSingleProcess) {
   MockMultiplePagesInSingleProcessCoordinationUnitGraph cu_graph;
-
   cu_graph.process->SetCPUUsage(40);
-
-  int64_t cpu_usage;
-  EXPECT_TRUE(
-      cu_graph.page->GetProperty(mojom::PropertyType::kCPUUsage, &cpu_usage));
-  EXPECT_EQ(20, cpu_usage / 1000);
-  EXPECT_TRUE(cu_graph.other_page->GetProperty(mojom::PropertyType::kCPUUsage,
-                                               &cpu_usage));
-  EXPECT_EQ(20, cpu_usage / 1000);
+  EXPECT_EQ(20, cu_graph.page->GetCPUUsage());
+  EXPECT_EQ(20, cu_graph.other_page->GetCPUUsage());
 }
 
 TEST_F(PageCoordinationUnitImplTest,
        CalculatePageCPUUsageForSinglePageWithMultipleProcesses) {
   MockSinglePageWithMultipleProcessesCoordinationUnitGraph cu_graph;
-
   cu_graph.process->SetCPUUsage(40);
   cu_graph.other_process->SetCPUUsage(30);
-
-  int64_t cpu_usage;
-  EXPECT_TRUE(
-      cu_graph.page->GetProperty(mojom::PropertyType::kCPUUsage, &cpu_usage));
-  EXPECT_EQ(70, cpu_usage / 1000);
+  EXPECT_EQ(70, cu_graph.page->GetCPUUsage());
 }
 
 TEST_F(PageCoordinationUnitImplTest,
        CalculatePageCPUUsageForMultiplePagesWithMultipleProcesses) {
   MockMultiplePagesWithMultipleProcessesCoordinationUnitGraph cu_graph;
-
   cu_graph.process->SetCPUUsage(40);
   cu_graph.other_process->SetCPUUsage(30);
-
-  int64_t cpu_usage;
-  EXPECT_TRUE(
-      cu_graph.page->GetProperty(mojom::PropertyType::kCPUUsage, &cpu_usage));
-  EXPECT_EQ(20, cpu_usage / 1000);
-  EXPECT_TRUE(cu_graph.other_page->GetProperty(mojom::PropertyType::kCPUUsage,
-                                               &cpu_usage));
-  EXPECT_EQ(50, cpu_usage / 1000);
+  EXPECT_EQ(20, cu_graph.page->GetCPUUsage());
+  EXPECT_EQ(50, cu_graph.other_page->GetCPUUsage());
 }
 
 TEST_F(PageCoordinationUnitImplTest,
@@ -132,8 +108,7 @@
       base::TimeDelta::FromMilliseconds(1));
 
   int64_t eqt;
-  ASSERT_TRUE(cu_graph.page->GetProperty(
-      mojom::PropertyType::kExpectedTaskQueueingDuration, &eqt));
+  EXPECT_TRUE(cu_graph.page->GetExpectedTaskQueueingDuration(&eqt));
   EXPECT_EQ(1, eqt);
 }
 
@@ -145,11 +120,10 @@
       base::TimeDelta::FromMilliseconds(1));
 
   int64_t eqt;
-  ASSERT_TRUE(cu_graph.page->GetProperty(
-      mojom::PropertyType::kExpectedTaskQueueingDuration, &eqt));
+  EXPECT_TRUE(cu_graph.page->GetExpectedTaskQueueingDuration(&eqt));
   EXPECT_EQ(1, eqt);
-  ASSERT_TRUE(cu_graph.other_page->GetProperty(
-      mojom::PropertyType::kExpectedTaskQueueingDuration, &eqt));
+  eqt = 0;
+  EXPECT_TRUE(cu_graph.other_page->GetExpectedTaskQueueingDuration(&eqt));
   EXPECT_EQ(1, eqt);
 }
 
diff --git a/services/resource_coordinator/coordination_unit/process_coordination_unit_impl.cc b/services/resource_coordinator/coordination_unit/process_coordination_unit_impl.cc
index d350bd90..18d4f30 100644
--- a/services/resource_coordinator/coordination_unit/process_coordination_unit_impl.cc
+++ b/services/resource_coordinator/coordination_unit/process_coordination_unit_impl.cc
@@ -72,6 +72,11 @@
   SetProperty(mojom::PropertyType::kPID, pid);
 }
 
+std::set<FrameCoordinationUnitImpl*>
+ProcessCoordinationUnitImpl::GetFrameCoordinationUnits() const {
+  return frame_coordination_units_;
+}
+
 // There is currently not a direct relationship between processes and
 // pages. However, frames are children of both processes and frames, so we
 // find all of the pages that are reachable from the process's child
@@ -86,33 +91,11 @@
   return page_cus;
 }
 
-void ProcessCoordinationUnitImpl::PropagateProperty(
-    mojom::PropertyType property_type,
+void ProcessCoordinationUnitImpl::OnPropertyChanged(
+    const mojom::PropertyType property_type,
     int64_t value) {
-  switch (property_type) {
-    // Trigger Page CU to recalculate their CPU usage.
-    case mojom::PropertyType::kCPUUsage: {
-      for (auto* page_cu : GetAssociatedPageCoordinationUnits()) {
-        page_cu->RecalculateProperty(mojom::PropertyType::kCPUUsage);
-      }
-      break;
-    }
-    case mojom::PropertyType::kExpectedTaskQueueingDuration: {
-      // Do not propagate if the associated frame is not the main frame.
-      for (auto* frame_cu : frame_coordination_units_) {
-        if (!frame_cu->IsMainFrame())
-          continue;
-        auto* page_cu = frame_cu->GetPageCoordinationUnit();
-        if (page_cu) {
-          page_cu->RecalculateProperty(
-              mojom::PropertyType::kExpectedTaskQueueingDuration);
-        }
-      }
-      break;
-    }
-    default:
-      break;
-  }
+  for (auto& observer : observers())
+    observer.OnProcessPropertyChanged(this, property_type, value);
 }
 
 bool ProcessCoordinationUnitImpl::AddFrame(
diff --git a/services/resource_coordinator/coordination_unit/process_coordination_unit_impl.h b/services/resource_coordinator/coordination_unit/process_coordination_unit_impl.h
index 2955073..2978cc05 100644
--- a/services/resource_coordinator/coordination_unit/process_coordination_unit_impl.h
+++ b/services/resource_coordinator/coordination_unit/process_coordination_unit_impl.h
@@ -35,6 +35,7 @@
   void SetLaunchTime(base::Time launch_time) override;
   void SetPID(int64_t pid) override;
 
+  std::set<FrameCoordinationUnitImpl*> GetFrameCoordinationUnits() const;
   std::set<PageCoordinationUnitImpl*> GetAssociatedPageCoordinationUnits()
       const;
 
@@ -42,7 +43,7 @@
   friend class FrameCoordinationUnitImpl;
 
   // CoordinationUnitInterface implementation.
-  void PropagateProperty(mojom::PropertyType property_type,
+  void OnPropertyChanged(mojom::PropertyType property_type,
                          int64_t value) override;
 
   bool AddFrame(FrameCoordinationUnitImpl* frame_cu);
diff --git a/services/resource_coordinator/observers/coordination_unit_graph_observer.h b/services/resource_coordinator/observers/coordination_unit_graph_observer.h
index 1f0aab4..f2dc4fb4 100644
--- a/services/resource_coordinator/observers/coordination_unit_graph_observer.h
+++ b/services/resource_coordinator/observers/coordination_unit_graph_observer.h
@@ -14,6 +14,7 @@
 class CoordinationUnitManager;
 class FrameCoordinationUnitImpl;
 class PageCoordinationUnitImpl;
+class ProcessCoordinationUnitImpl;
 
 // An observer API for the coordination unit graph maintained by GRC.
 //
@@ -62,6 +63,12 @@
                                      const mojom::PropertyType property_type,
                                      int64_t value) {}
 
+  // Called whenever a property of the ProcessCoordinationUnit is changed.
+  virtual void OnProcessPropertyChanged(
+      const ProcessCoordinationUnitImpl* process_cu,
+      const mojom::PropertyType property_type,
+      int64_t value) {}
+
   // Called whenever an event is received in |coordination_unit| if the
   // |coordination_unit| doesn't implement its own EventReceived handler.
   virtual void OnEventReceived(const CoordinationUnitBase* coordination_unit,
diff --git a/services/resource_coordinator/observers/metrics_collector.cc b/services/resource_coordinator/observers/metrics_collector.cc
index 1561c203..997cd79 100644
--- a/services/resource_coordinator/observers/metrics_collector.cc
+++ b/services/resource_coordinator/observers/metrics_collector.cc
@@ -38,15 +38,12 @@
 
 // Gets the number of tabs that are co-resident in all of the render processes
 // associated with a |CoordinationUnitType::kPage| coordination unit.
-size_t GetNumCoresidentTabs(const CoordinationUnitBase* coordination_unit) {
-  DCHECK_EQ(CoordinationUnitType::kPage, coordination_unit->id().type);
-  auto* page_cu =
-      PageCoordinationUnitImpl::FromCoordinationUnitBase(coordination_unit);
+size_t GetNumCoresidentTabs(const PageCoordinationUnitImpl* page_cu) {
   std::set<CoordinationUnitBase*> coresident_tabs;
   for (auto* process_cu : page_cu->GetAssociatedProcessCoordinationUnits()) {
-    for (auto* tab_coordination_unit :
+    for (auto* associated_page_cu :
          process_cu->GetAssociatedPageCoordinationUnits()) {
-      coresident_tabs.insert(tab_coordination_unit);
+      coresident_tabs.insert(associated_page_cu);
     }
   }
   // A tab cannot be co-resident with itself.
@@ -64,7 +61,8 @@
 bool MetricsCollector::ShouldObserve(
     const CoordinationUnitBase* coordination_unit) {
   return coordination_unit->id().type == CoordinationUnitType::kFrame ||
-         coordination_unit->id().type == CoordinationUnitType::kPage;
+         coordination_unit->id().type == CoordinationUnitType::kPage ||
+         coordination_unit->id().type == CoordinationUnitType::kProcess;
 }
 
 void MetricsCollector::OnCoordinationUnitCreated(
@@ -126,11 +124,6 @@
       ResetMetricsReportRecord(page_cu_id);
       return;
     }
-  } else if (property_type == mojom::PropertyType::kCPUUsage) {
-    if (IsCollectingCPUUsageForUkm(page_cu_id)) {
-      RecordCPUUsageForUkm(page_cu_id, static_cast<double>(value) / 1000,
-                           GetNumCoresidentTabs(page_cu));
-    }
   } else if (property_type == mojom::PropertyType::kUKMSourceId) {
     ukm::SourceId ukm_source_id = value;
     UpdateUkmSourceIdForPage(page_cu_id, ukm_source_id);
@@ -140,6 +133,20 @@
   }
 }
 
+void MetricsCollector::OnProcessPropertyChanged(
+    const ProcessCoordinationUnitImpl* process_cu,
+    const mojom::PropertyType property_type,
+    int64_t value) {
+  if (property_type == mojom::PropertyType::kCPUUsage) {
+    for (auto* page_cu : process_cu->GetAssociatedPageCoordinationUnits()) {
+      if (IsCollectingCPUUsageForUkm(page_cu->id())) {
+        RecordCPUUsageForUkm(page_cu->id(), page_cu->GetCPUUsage(),
+                             GetNumCoresidentTabs(page_cu));
+      }
+    }
+  }
+}
+
 void MetricsCollector::OnFrameEventReceived(
     const FrameCoordinationUnitImpl* frame_cu,
     const mojom::Event event) {
diff --git a/services/resource_coordinator/observers/metrics_collector.h b/services/resource_coordinator/observers/metrics_collector.h
index e2bf919..5056c715 100644
--- a/services/resource_coordinator/observers/metrics_collector.h
+++ b/services/resource_coordinator/observers/metrics_collector.h
@@ -48,6 +48,9 @@
   void OnPagePropertyChanged(const PageCoordinationUnitImpl* page_cu,
                              const mojom::PropertyType property_type,
                              int64_t value) override;
+  void OnProcessPropertyChanged(const ProcessCoordinationUnitImpl* process_cu,
+                                const mojom::PropertyType property_type,
+                                int64_t value) override;
   void OnFrameEventReceived(const FrameCoordinationUnitImpl* frame_cu,
                             const mojom::Event event) override;
   void OnPageEventReceived(const PageCoordinationUnitImpl* page_cu,
diff --git a/services/resource_coordinator/observers/tab_signal_generator_impl.cc b/services/resource_coordinator/observers/tab_signal_generator_impl.cc
index 2c4faf6..a4be19c 100644
--- a/services/resource_coordinator/observers/tab_signal_generator_impl.cc
+++ b/services/resource_coordinator/observers/tab_signal_generator_impl.cc
@@ -8,6 +8,7 @@
 
 #include "services/resource_coordinator/coordination_unit/frame_coordination_unit_impl.h"
 #include "services/resource_coordinator/coordination_unit/page_coordination_unit_impl.h"
+#include "services/resource_coordinator/coordination_unit/process_coordination_unit_impl.h"
 #include "services/service_manager/public/cpp/bind_source_info.h"
 
 namespace resource_coordinator {
@@ -27,9 +28,9 @@
 
 bool TabSignalGeneratorImpl::ShouldObserve(
     const CoordinationUnitBase* coordination_unit) {
-  auto coordination_unit_type = coordination_unit->id().type;
-  return coordination_unit_type == CoordinationUnitType::kPage ||
-         coordination_unit_type == CoordinationUnitType::kFrame;
+  auto cu_type = coordination_unit->id().type;
+  return cu_type == CoordinationUnitType::kFrame ||
+         cu_type == CoordinationUnitType::kProcess;
 }
 
 void TabSignalGeneratorImpl::OnFramePropertyChanged(
@@ -47,14 +48,22 @@
   }
 }
 
-void TabSignalGeneratorImpl::OnPagePropertyChanged(
-    const PageCoordinationUnitImpl* page_cu,
+void TabSignalGeneratorImpl::OnProcessPropertyChanged(
+    const ProcessCoordinationUnitImpl* process_cu,
     const mojom::PropertyType property_type,
     int64_t value) {
   if (property_type == mojom::PropertyType::kExpectedTaskQueueingDuration) {
-    DISPATCH_TAB_SIGNAL(observers_, SetExpectedTaskQueueingDuration,
-                        page_cu->id(),
-                        base::TimeDelta::FromMilliseconds(value));
+    for (auto* frame_cu : process_cu->GetFrameCoordinationUnits()) {
+      if (!frame_cu->IsMainFrame())
+        continue;
+      auto* page_cu = frame_cu->GetPageCoordinationUnit();
+      int64_t duration;
+      if (!page_cu->GetExpectedTaskQueueingDuration(&duration))
+        continue;
+      DISPATCH_TAB_SIGNAL(observers_, SetExpectedTaskQueueingDuration,
+                          page_cu->id(),
+                          base::TimeDelta::FromMilliseconds(duration));
+    }
   }
 }
 
diff --git a/services/resource_coordinator/observers/tab_signal_generator_impl.h b/services/resource_coordinator/observers/tab_signal_generator_impl.h
index 971499a..b967fa0 100644
--- a/services/resource_coordinator/observers/tab_signal_generator_impl.h
+++ b/services/resource_coordinator/observers/tab_signal_generator_impl.h
@@ -17,10 +17,6 @@
 
 namespace resource_coordinator {
 
-class CoordinationUnitBase;
-class FrameCoordinationUnitImpl;
-class PageCoordinationUnitImpl;
-
 // The TabSignalGenerator is a dedicated |CoordinationUnitGraphObserver| for
 // calculating and emitting tab-scoped signals. This observer observes Tab
 // CoordinationUnits and Frame CoordinationUnits, utilize information from the
@@ -39,9 +35,9 @@
   void OnFramePropertyChanged(const FrameCoordinationUnitImpl* frame_cu,
                               const mojom::PropertyType property_type,
                               int64_t value) override;
-  void OnPagePropertyChanged(const PageCoordinationUnitImpl* page_cu,
-                             const mojom::PropertyType property_type,
-                             int64_t value) override;
+  void OnProcessPropertyChanged(const ProcessCoordinationUnitImpl* process_cu,
+                                const mojom::PropertyType property_type,
+                                int64_t value) override;
 
   void BindToInterface(
       resource_coordinator::mojom::TabSignalGeneratorRequest request,
diff --git a/services/resource_coordinator/observers/tab_signal_generator_impl_unittest.cc b/services/resource_coordinator/observers/tab_signal_generator_impl_unittest.cc
index 344b4e9e..0d0a35dd 100644
--- a/services/resource_coordinator/observers/tab_signal_generator_impl_unittest.cc
+++ b/services/resource_coordinator/observers/tab_signal_generator_impl_unittest.cc
@@ -15,9 +15,9 @@
 class MockTabSignalGeneratorImpl : public TabSignalGeneratorImpl {
  public:
   // Overridden from TabSignalGeneratorImpl.
-  void OnPagePropertyChanged(const PageCoordinationUnitImpl* coordination_unit,
-                             const mojom::PropertyType property_type,
-                             int64_t value) override {
+  void OnProcessPropertyChanged(const ProcessCoordinationUnitImpl* process_cu,
+                                const mojom::PropertyType property_type,
+                                int64_t value) override {
     if (property_type == mojom::PropertyType::kExpectedTaskQueueingDuration)
       ++eqt_change_count_;
   }
@@ -41,7 +41,7 @@
 TEST_F(TabSignalGeneratorImplTest,
        CalculateTabEQTForSingleTabWithMultipleProcesses) {
   MockSinglePageWithMultipleProcessesCoordinationUnitGraph cu_graph;
-  cu_graph.page->AddObserver(tab_signal_generator());
+  cu_graph.process->AddObserver(tab_signal_generator());
 
   cu_graph.process->SetExpectedTaskQueueingDuration(
       base::TimeDelta::FromMilliseconds(1));
@@ -52,8 +52,7 @@
   // propagate to the page.
   EXPECT_EQ(1u, tab_signal_generator()->eqt_change_count());
   int64_t eqt;
-  ASSERT_TRUE(cu_graph.page->GetProperty(
-      mojom::PropertyType::kExpectedTaskQueueingDuration, &eqt));
+  EXPECT_TRUE(cu_graph.page->GetExpectedTaskQueueingDuration(&eqt));
   EXPECT_EQ(1, eqt);
 }
 
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index ba740c67..02a9a727 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -7542,8 +7542,6 @@
 crbug.com/591099 paint/invalidation/overflow-changed-on-child-of-composited-layer.html [ Failure ]
 crbug.com/591099 paint/invalidation/overflow-clip-subtree-layout.html [ Failure Pass ]
 crbug.com/591099 paint/invalidation/overflow-delete-line.html [ Failure ]
-crbug.com/591099 paint/invalidation/overflow-flipped-writing-mode-block.html [ Failure Pass ]
-crbug.com/591099 paint/invalidation/overflow-flipped-writing-mode-table.html [ Failure Pass ]
 crbug.com/591099 paint/invalidation/overflow-hidden-in-overflow-hidden-scrolled.html [ Failure ]
 crbug.com/591099 paint/invalidation/overflow-hidden-to-visible.html [ Failure ]
 crbug.com/591099 paint/invalidation/overflow-hidden-yet-scrolled-with-custom-scrollbar.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-flipped-writing-mode-block-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-flipped-writing-mode-block-expected.txt
deleted file mode 100644
index c780d93..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-flipped-writing-mode-block-expected.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutBlockFlow DIV id='target'",
-          "rect": [-92, 108, 50, 100],
-          "reason": "disappeared"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutBlockFlow DIV id='target'",
-      "reason": "style change"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-flipped-writing-mode-table-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-flipped-writing-mode-table-expected.txt
deleted file mode 100644
index c780d93..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/paint/invalidation/overflow-flipped-writing-mode-table-expected.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutBlockFlow DIV id='target'",
-          "rect": [-92, 108, 50, 100],
-          "reason": "disappeared"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutBlockFlow DIV id='target'",
-      "reason": "style change"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/paint/invalidation/overflow-flipped-writing-mode-block-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/paint/invalidation/overflow-flipped-writing-mode-block-expected.txt
deleted file mode 100644
index ba3918d..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/paint/invalidation/overflow-flipped-writing-mode-block-expected.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [800, 600],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutBlockFlow DIV id='target'",
-          "rect": [-92, 108, 50, 100],
-          "reason": "style change"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutBlockFlow DIV id='target'",
-      "reason": "style change"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/paint/invalidation/overflow-flipped-writing-mode-table-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/paint/invalidation/overflow-flipped-writing-mode-table-expected.txt
deleted file mode 100644
index ba3918d..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/root-layer-scrolls/paint/invalidation/overflow-flipped-writing-mode-table-expected.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [800, 600],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutBlockFlow DIV id='target'",
-          "rect": [-92, 108, 50, 100],
-          "reason": "style change"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutBlockFlow DIV id='target'",
-      "reason": "style change"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/overflow-flipped-writing-mode-block-expected.html b/third_party/WebKit/LayoutTests/paint/invalidation/overflow-flipped-writing-mode-block-expected.html
deleted file mode 100644
index fcaa85f..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/overflow-flipped-writing-mode-block-expected.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<div style="height: 100px; width: 100px; background-color: red; writing-mode: vertical-rl;">
-    <div style="width: 25px;">
-        <div style="width: 100px; background-color: green;"></div>
-    </div>
-</div>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/overflow-flipped-writing-mode-block-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/overflow-flipped-writing-mode-block-expected.txt
deleted file mode 100644
index ee602cc..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/overflow-flipped-writing-mode-block-expected.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutBlockFlow DIV id='target'",
-          "rect": [-92, 108, 50, 100],
-          "reason": "style change"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutBlockFlow DIV id='target'",
-      "reason": "style change"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/overflow-flipped-writing-mode-block.html b/third_party/WebKit/LayoutTests/paint/invalidation/overflow-flipped-writing-mode-block.html
deleted file mode 100644
index 2f6befe81..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/overflow-flipped-writing-mode-block.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<script src="resources/text-based-repaint.js"></script>
-<script>
-    onload = runRepaintAndPixelTest;
-
-    function repaintTest()
-    {
-        document.getElementById("target").style.visibility = "hidden";
-    }
-</script>
-<div style="height: 100px; width: 100px; background-color: red; writing-mode: vertical-rl;">
-    <div style="width: 25px;">
-        <div style="width: 100px; background-color: green;"></div>
-    </div>
-</div>
-<div id="target" style="width: 50px; height: 100px; background-color: red; margin-left: -100px;"></div>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/overflow-flipped-writing-mode-table-expected.html b/third_party/WebKit/LayoutTests/paint/invalidation/overflow-flipped-writing-mode-table-expected.html
deleted file mode 100644
index 95ec910..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/overflow-flipped-writing-mode-table-expected.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<div style="height: 100px; width: 100px; background-color: red; writing-mode: vertical-rl;">
-    <div style="display: table; height: 100px;">
-        <div style="width: 25px;">
-            <div style="width: 100px; background-color: green;"></div>
-        </div>
-    </div>
-</div>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/overflow-flipped-writing-mode-table-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/overflow-flipped-writing-mode-table-expected.txt
deleted file mode 100644
index ee602cc..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/overflow-flipped-writing-mode-table-expected.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutBlockFlow DIV id='target'",
-          "rect": [-92, 108, 50, 100],
-          "reason": "style change"
-        }
-      ]
-    }
-  ],
-  "objectPaintInvalidations": [
-    {
-      "object": "LayoutBlockFlow DIV id='target'",
-      "reason": "style change"
-    }
-  ]
-}
-
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/overflow-flipped-writing-mode-table.html b/third_party/WebKit/LayoutTests/paint/invalidation/overflow-flipped-writing-mode-table.html
deleted file mode 100644
index b464c31..0000000
--- a/third_party/WebKit/LayoutTests/paint/invalidation/overflow-flipped-writing-mode-table.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<script src="resources/text-based-repaint.js"></script>
-<script>
-    onload = runRepaintAndPixelTest;
-
-    function repaintTest()
-    {
-        document.getElementById("target").style.visibility = "hidden";
-    }
-</script>
-<div style="height: 100px; width: 100px; background-color: red; writing-mode: vertical-rl;">
-    <div style="display: table; height: 100px;">
-        <div style="width: 25px;">
-            <div style="width: 100px; background-color: green;"></div>
-        </div>
-    </div>
-</div>
-<div id="target" style="width: 50px; height: 100px; background-color: red; margin-left: -100px;"></div>
diff --git a/third_party/WebKit/Source/core/frame/SmartClip.cpp b/third_party/WebKit/Source/core/frame/SmartClip.cpp
index 4a0970c..c0c6083 100644
--- a/third_party/WebKit/Source/core/frame/SmartClip.cpp
+++ b/third_party/WebKit/Source/core/frame/SmartClip.cpp
@@ -100,7 +100,6 @@
   }
 
   return SmartClipData(
-      best_node,
       frame_->GetDocument()->View()->ContentsToViewport(united_rects),
       collected_text.ToString());
 }
diff --git a/third_party/WebKit/Source/core/frame/SmartClip.h b/third_party/WebKit/Source/core/frame/SmartClip.h
index 495c9cb..9a6d6e48 100644
--- a/third_party/WebKit/Source/core/frame/SmartClip.h
+++ b/third_party/WebKit/Source/core/frame/SmartClip.h
@@ -42,16 +42,15 @@
   STACK_ALLOCATED();
 
  public:
-  SmartClipData() : is_empty_(true) {}
+  SmartClipData() {}
 
-  SmartClipData(Node* node, IntRect rect, String string)
-      : is_empty_(!node), rect_in_viewport_(rect), string_(string) {}
+  SmartClipData(IntRect rect, String string)
+      : rect_in_viewport_(rect), string_(string) {}
 
   IntRect RectInViewport() const;
   const String& ClipData() const;
 
  private:
-  bool is_empty_;
   IntRect rect_in_viewport_;
   String string_;
 };
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index d0f94bcb..e84a0d8 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -1587,4 +1587,18 @@
   </metric>
 </event>
 
+<event name="TabManager.TabLifetime" singular="True">
+  <owner>chrisha@chromium.org</owner>
+  <summary>
+    Collected when a tab is closed, at most once per source.
+  </summary>
+  <metric name="TimeSinceNavigation">
+    <summary>
+      The time when the tab was closed, expressed as the amount of time in MS
+      that has elapsed since the main frame navigation providing the content of
+      the tab.
+    </summary>
+  </metric>
+</event>
+
 </ukm-configuration>