diff --git a/DEPS b/DEPS
index 50bf2be..a7dc181 100644
--- a/DEPS
+++ b/DEPS
@@ -167,11 +167,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '3375a8848278320b2d166521b43ca23d09df0e16',
+  'skia_revision': 'f7e9d17791e406285df83449b95cd498d1a9cd1f',
   # 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': '4c6500079244d1fe5cd0276dd245924128849761',
+  'v8_revision': '377dc2acc20702d6b96dc7a9e32a8b7164448670',
   # 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.
@@ -179,11 +179,11 @@
   # 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': '70d79b76994e4cfafbdae2f8b9210392f2e37724',
+  'angle_revision': '7f506bde16f165f923e9fd7e80c4f8b6314618fd',
   # 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': '072466ad1faba98054504b7158313570a5e085b6',
+  'swiftshader_revision': '6b4b8141e11d7e1a397aa04f39c0c7eb20428104',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -877,7 +877,7 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '191a60616c6ba9510957b26eae500b3c2099487c',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '9488a16bbe3da08122d8b42712178aaa84e46d16',
       'condition': 'checkout_linux',
   },
 
@@ -887,7 +887,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'e656e9883179f680292735a6f1fcea9177b2e81f',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '2b829368295598cb663f9e7b931c330d5697cfc3',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1459,7 +1459,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '7c4e67ff117d6c640e6dd17989afe2fb7da7eecb',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '7da4e563b79101d1e69a1111fdab20875ea60c6f',
+    Var('webrtc_git') + '/src.git' + '@' + '1903a35e00e89555180b8442c42d3bdbd91565ee',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1521,7 +1521,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@da7bc7fa532d35a0bf6b4e2a2f02fd3f59f8bf5c',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e1fd70911515747eeeb1e32f37ccf2f16a0b4a0a',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 11a64a0..0c690c5 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -1083,7 +1083,7 @@
 
       if (shelf_widget_->hotseat_widget()->IsDraggedToExtended())
         return HotseatState::kExtended;
-      if (std::abs(last_drag_velocity_) >= 5) {
+      if (std::abs(last_drag_velocity_) >= 120) {
         if (last_drag_velocity_ > 0)
           return HotseatState::kHidden;
         return HotseatState::kExtended;
@@ -1094,8 +1094,10 @@
               .bounds()
               .bottom() -
           shelf_widget_->hotseat_widget()->GetWindowBoundsInScreen().y();
-      if (top_of_hotseat_to_screen_bottom < ShelfConfig::Get()->shelf_size())
+      if (top_of_hotseat_to_screen_bottom <
+          ShelfConfig::Get()->hotseat_size() / 2) {
         return HotseatState::kHidden;
+      }
       return HotseatState::kExtended;
     }
     case kDragAppListInProgress:
@@ -1387,10 +1389,17 @@
     int hotseat_y;
     const int hotseat_size = Shell::Get()->shelf_config()->hotseat_size();
     switch (state_.hotseat_state) {
-      case HotseatState::kShown:
-        // Show the hotseat co-altitude with ShelfView.
-        hotseat_y = 0;
-        break;
+      case HotseatState::kShown: {
+        // When the hotseat state is HotseatState::kShown in tablet mode, the
+        // home launcher is showing. Elevate the hotseat a few px to match the
+        // navigation and status area.
+        const bool use_padding = chromeos::switches::ShouldShowShelfHotseat() &&
+                                 IsTabletModeEnabled();
+        hotseat_y =
+            use_padding
+                ? -Shell::Get()->shelf_config()->hotseat_bottom_padding()
+                : 0;
+      } break;
       case HotseatState::kHidden:
         // Show the hotseat offscreen.
         hotseat_y = hotseat_size;
@@ -1406,11 +1415,19 @@
         shelf_width - target_bounds->nav_bounds_in_shelf.size().width() -
         home_button_edge_spacing - ShelfConfig::Get()->app_icon_group_margin() -
         status_size.width();
-    const int hotseat_x = base::i18n::IsRTL()
-                              ? target_bounds->nav_bounds_in_shelf.x() -
-                                    home_button_edge_spacing - hotseat_width
-                              : target_bounds->nav_bounds_in_shelf.right() +
-                                    home_button_edge_spacing;
+
+    int hotseat_x = base::i18n::IsRTL()
+                        ? target_bounds->nav_bounds_in_shelf.x() -
+                              home_button_edge_spacing - hotseat_width
+                        : target_bounds->nav_bounds_in_shelf.right() +
+                              home_button_edge_spacing;
+
+    if (state_.hotseat_state != HotseatState::kShown) {
+      // Give the hotseat more space if it is shown outside of the shelf.
+      hotseat_width = available_bounds.width();
+      hotseat_x = 0;
+    }
+
     hotseat_origin = gfx::Point(hotseat_x, hotseat_y);
 
     hotseat_height = hotseat_size;
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 00525854..bbfa191 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -3416,9 +3416,9 @@
 }
 
 // Tests that releasing the hotseat gesture below the threshold results in a
-// kHidden hotseat.
-TEST_P(HotseatShelfLayoutManagerTest, ReleasingSlowDragBelowThreshold) {
-  GetPrimaryShelf()->SetAutoHideBehavior(GetParam());
+// kHidden hotseat when the shelf is shown.
+TEST_F(HotseatShelfLayoutManagerTest, ReleasingSlowDragBelowThreshold) {
+  GetPrimaryShelf()->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
   TabletModeControllerTestApi().EnterTabletMode();
   std::unique_ptr<aura::Window> window =
       AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400));
@@ -3427,20 +3427,18 @@
   gfx::Rect display_bounds =
       display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
   const gfx::Point start(display_bounds.bottom_center());
-  const int shelf_size = GetPrimaryShelf()
-                             ->shelf_widget()
-                             ->shelf_view_for_testing()
-                             ->GetBoundsInScreen()
-                             .height();
-  const gfx::Point end(start + gfx::Vector2d(0, -shelf_size / 4));
+  const int hotseat_size = GetPrimaryShelf()
+                               ->shelf_widget()
+                               ->hotseat_widget()
+                               ->GetWindowBoundsInScreen()
+                               .height();
+  const gfx::Point end(start + gfx::Vector2d(0, -hotseat_size / 2 + 1));
   const base::TimeDelta kTimeDelta = base::TimeDelta::FromMilliseconds(1000);
   const int kNumScrollSteps = 4;
   GetEventGenerator()->GestureScrollSequence(start, end, kTimeDelta,
                                              kNumScrollSteps);
 
   EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state());
-  if (GetParam() == SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS)
-    EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, GetPrimaryShelf()->GetAutoHideState());
 }
 
 // Tests that releasing the hotseat gesture above the threshold results in a
@@ -3455,12 +3453,12 @@
   gfx::Rect display_bounds =
       display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
   const gfx::Point start(display_bounds.bottom_center());
-  const int shelf_size = GetPrimaryShelf()
-                             ->shelf_widget()
-                             ->shelf_view_for_testing()
-                             ->GetBoundsInScreen()
-                             .height();
-  const gfx::Point end(start + gfx::Vector2d(0, -2 * shelf_size));
+  const int hotseat_size = GetPrimaryShelf()
+                               ->shelf_widget()
+                               ->hotseat_widget()
+                               ->GetWindowBoundsInScreen()
+                               .height();
+  const gfx::Point end(start + gfx::Vector2d(0, -hotseat_size * 3.0f / 2.0f));
   const base::TimeDelta kTimeDelta = base::TimeDelta::FromMilliseconds(1000);
   const int kNumScrollSteps = 4;
   GetEventGenerator()->GestureScrollSequence(start, end, kTimeDelta,
@@ -3496,6 +3494,35 @@
   state_watcher_->CheckEqual({HotseatState::kExtended, HotseatState::kShown});
 }
 
+// Tests that the hotseat is not flush with the bottom of the screen when home
+// launcher is showing.
+TEST_P(HotseatShelfLayoutManagerTest, HotseatNotFlushWhenHomeLauncherShowing) {
+  GetPrimaryShelf()->SetAutoHideBehavior(GetParam());
+  TabletModeControllerTestApi().EnterTabletMode();
+  const int display_height =
+      display::Screen::GetScreen()->GetPrimaryDisplay().bounds().height();
+  const int hotseat_bottom = GetPrimaryShelf()
+                                 ->shelf_widget()
+                                 ->hotseat_widget()
+                                 ->GetWindowBoundsInScreen()
+                                 .bottom();
+  EXPECT_LT(hotseat_bottom, display_height);
+}
+
+// Tests that the hotseat is flush with the bottom of the screen when in
+// clamshell mode and the shelf is oriented on the bottom.
+TEST_P(HotseatShelfLayoutManagerTest, HotseatFlushWithScreenBottomInClamshell) {
+  GetPrimaryShelf()->SetAutoHideBehavior(GetParam());
+  const int display_height =
+      display::Screen::GetScreen()->GetPrimaryDisplay().bounds().height();
+  const int hotseat_bottom = GetPrimaryShelf()
+                                 ->shelf_widget()
+                                 ->hotseat_widget()
+                                 ->GetWindowBoundsInScreen()
+                                 .bottom();
+  EXPECT_EQ(hotseat_bottom, display_height);
+}
+
 class ShelfLayoutManagerKeyboardTest : public AshTestBase {
  public:
   ShelfLayoutManagerKeyboardTest() = default;
diff --git a/ash/system/network/active_network_icon.cc b/ash/system/network/active_network_icon.cc
index dd9a918..422ded417 100644
--- a/ash/system/network/active_network_icon.cc
+++ b/ash/system/network/active_network_icon.cc
@@ -78,7 +78,8 @@
   }
   // Check for Activating first since activating networks may be connected.
   if (network && network->type == NetworkType::kCellular &&
-      network->cellular->activation_state == ActivationStateType::kActivating) {
+      network->type_state->get_cellular()->activation_state ==
+          ActivationStateType::kActivating) {
     base::string16 activating_string = l10n_util::GetStringFUTF16(
         IDS_ASH_STATUS_TRAY_NETWORK_ACTIVATING, network_name);
     if (a11y_name)
diff --git a/ash/system/network/network_feature_pod_button.cc b/ash/system/network/network_feature_pod_button.cc
index 0dbeb00..1858027 100644
--- a/ash/system/network/network_feature_pod_button.cc
+++ b/ash/system/network/network_feature_pod_button.cc
@@ -43,7 +43,8 @@
   }
 
   if (network->type == NetworkType::kCellular) {
-    CellularStateProperties* cellular = network->cellular.get();
+    CellularStateProperties* cellular =
+        network->type_state->get_cellular().get();
     if (cellular->network_technology == onc::cellular::kTechnologyCdma1Xrtt) {
       return l10n_util::GetStringUTF16(
           IDS_ASH_STATUS_TRAY_NETWORK_CELLULAR_TYPE_ONE_X);
@@ -167,7 +168,8 @@
   }
   // Check for Activating first since activating networks may be connected.
   if (network && network->type == NetworkType::kCellular &&
-      network->cellular->activation_state == ActivationStateType::kActivating) {
+      network->type_state->get_cellular()->activation_state ==
+          ActivationStateType::kActivating) {
     SetLabel(network_name);
     SetSubLabel(l10n_util::GetStringUTF16(
         IDS_ASH_STATUS_TRAY_NETWORK_ACTIVATING_SUBLABEL));
diff --git a/ash/system/network/network_icon.cc b/ash/system/network/network_icon.cc
index 15c3fb2..1473e42 100644
--- a/ash/system/network/network_icon.cc
+++ b/ash/system/network/network_icon.cc
@@ -253,7 +253,8 @@
                                 IconType icon_type) {
   DCHECK(network->type == NetworkType::kCellular);
   Badge badge = {nullptr, GetDefaultColorForIconType(icon_type)};
-  const std::string& technology = network->cellular->network_technology;
+  const std::string& technology =
+      network->type_state->get_cellular()->network_technology;
   if (technology == onc::cellular::kTechnologyEvdo) {
     badge.icon = &kNetworkBadgeTechnologyEvdoIcon;
   } else if (technology == onc::cellular::kTechnologyCdma1Xrtt) {
@@ -375,7 +376,7 @@
     }
   }
 
-  bool roaming = network->cellular->roaming;
+  bool roaming = network->type_state->get_cellular()->roaming;
   if (roaming != is_roaming_) {
     VLOG(2) << "New is_roaming: " << roaming;
     is_roaming_ = roaming;
@@ -391,13 +392,13 @@
   bool is_connected =
       chromeos::network_config::StateIsConnected(network->connection_state);
   if (type == NetworkType::kWiFi) {
-    if (network->wifi->security != SecurityType::kNone &&
+    if (network->type_state->get_wifi()->security != SecurityType::kNone &&
         !IsTrayIcon(icon_type_)) {
       badges->bottom_right = {&kUnifiedNetworkBadgeSecureIcon, icon_color};
     }
   } else if (type == NetworkType::kCellular) {
     // technology_badge_ is set in UpdateCellularState.
-    if (is_connected && network->cellular->roaming) {
+    if (is_connected && network->type_state->get_cellular()->roaming) {
       badges->bottom_right = {&kNetworkBadgeRoamingIcon, icon_color};
     }
   }
@@ -545,7 +546,8 @@
 
 base::string16 GetLabelForNetworkList(const NetworkStateProperties* network) {
   if (network->type == NetworkType::kCellular) {
-    ActivationStateType activation_state = network->cellular->activation_state;
+    ActivationStateType activation_state =
+        network->type_state->get_cellular()->activation_state;
     if (activation_state == ActivationStateType::kActivating) {
       return l10n_util::GetStringFUTF16(
           IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATING,
diff --git a/ash/system/network/network_list_view.cc b/ash/system/network/network_list_view.cc
index 0d14b86..3c97357 100644
--- a/ash/system/network/network_list_view.cc
+++ b/ash/system/network/network_list_view.cc
@@ -132,27 +132,36 @@
         vpn_connected_ = true;
       continue;
     }
-    // If cellular is not enabled, skip cellular networks with no service.
-    ActivationStateType activation_state =
-        network->cellular ? network->cellular->activation_state
-                          : ActivationStateType::kUnknown;
-    if (network->type == NetworkType::kCellular &&
-        model()->GetDeviceState(NetworkType::kCellular) !=
-            DeviceStateType::kEnabled &&
-        activation_state == ActivationStateType::kNoService) {
-      continue;
-    }
-    if (network->type == NetworkType::kWiFi)
-      wifi_has_networks_ = true;
-    // Real (non 'default') Cellular networks are always connectable.
-    if (network->type == NetworkType::kCellular && network->connectable)
-      mobile_has_networks_ = true;
-    if (network->type == NetworkType::kTether) {
-      mobile_has_networks_ = true;
-      tether_has_networks_ = true;
-    }
 
     auto info = std::make_unique<NetworkInfo>(network->guid);
+    ActivationStateType activation_state = ActivationStateType::kUnknown;
+    switch (network->type) {
+      case NetworkType::kCellular:
+        activation_state =
+            network->type_state->get_cellular()->activation_state;
+        // If cellular is not enabled, skip cellular networks with no service.
+        if (model()->GetDeviceState(NetworkType::kCellular) !=
+                DeviceStateType::kEnabled &&
+            activation_state == ActivationStateType::kNoService) {
+          continue;
+        }
+        // Real (non 'default') Cellular networks are always connectable.
+        if (network->connectable)
+          mobile_has_networks_ = true;
+        break;
+      case NetworkType::kWiFi:
+        wifi_has_networks_ = true;
+        break;
+      case NetworkType::kTether:
+        mobile_has_networks_ = true;
+        tether_has_networks_ = true;
+        info->battery_percentage =
+            network->type_state->get_tether()->battery_percentage;
+        break;
+      default:
+        break;
+    }
+
     info->label = network_icon::GetLabelForNetworkList(network.get());
     // |network_list_| only contains non virtual networks.
     info->image = network_icon::GetImageForNonVirtualNetwork(
@@ -166,8 +175,6 @@
 
     info->connection_state = connection_state;
 
-    if (network->tether)
-      info->battery_percentage = network->tether->battery_percentage;
     if (network->captive_portal_provider) {
       info->captive_portal_provider_name =
           network->captive_portal_provider->name;
diff --git a/ash/system/network/tray_network_state_model.cc b/ash/system/network/tray_network_state_model.cc
index 6eeb76e..41265c9 100644
--- a/ash/system/network/tray_network_state_model.cc
+++ b/ash/system/network/tray_network_state_model.cc
@@ -55,9 +55,8 @@
 
   GetNetworkConfigService(
       remote_cros_network_config_.BindNewPipeAndPassReceiver());
-  chromeos::network_config::mojom::CrosNetworkConfigObserverPtr observer_ptr;
-  cros_network_config_observer_receiver_.Bind(mojo::MakeRequest(&observer_ptr));
-  remote_cros_network_config_->AddObserver(std::move(observer_ptr));
+  remote_cros_network_config_->AddObserver(
+      cros_network_config_observer_receiver_.BindNewPipeAndPassRemote());
 
   GetActiveNetworks();
   GetVirtualNetworks();
diff --git a/ash/system/network/vpn_list.cc b/ash/system/network/vpn_list.cc
index 5c0b80f..0a6f53f 100644
--- a/ash/system/network/vpn_list.cc
+++ b/ash/system/network/vpn_list.cc
@@ -18,9 +18,8 @@
 
   ash::GetNetworkConfigService(
       cros_network_config_.BindNewPipeAndPassReceiver());
-  chromeos::network_config::mojom::CrosNetworkConfigObserverPtr observer_ptr;
-  cros_network_config_observer_.Bind(mojo::MakeRequest(&observer_ptr));
-  cros_network_config_->AddObserver(std::move(observer_ptr));
+  cros_network_config_->AddObserver(
+      cros_network_config_observer_.BindNewPipeAndPassRemote());
   OnVpnProvidersChanged();
 }
 
diff --git a/ash/system/network/vpn_list_view.cc b/ash/system/network/vpn_list_view.cc
index 2f38035..b44d928 100644
--- a/ash/system/network/vpn_list_view.cc
+++ b/ash/system/network/vpn_list_view.cc
@@ -56,6 +56,7 @@
 using chromeos::network_config::mojom::NetworkType;
 using chromeos::network_config::mojom::VpnProvider;
 using chromeos::network_config::mojom::VpnProviderPtr;
+using chromeos::network_config::mojom::VPNStatePropertiesPtr;
 using chromeos::network_config::mojom::VpnType;
 
 namespace ash {
@@ -77,10 +78,10 @@
   if (network->type != NetworkType::kVPN)
     return false;
 
-  if (network->vpn->type == VpnType::kArc ||
-      network->vpn->type == VpnType::kExtension) {
-    return network->vpn->type == provider->type &&
-           network->vpn->provider_id == provider->provider_id;
+  const VPNStatePropertiesPtr& vpn = network->type_state->get_vpn();
+  if (vpn->type == VpnType::kArc || vpn->type == VpnType::kExtension) {
+    return vpn->type == provider->type &&
+           vpn->provider_id == provider->provider_id;
   }
 
   // Internal provider types all match the default internal provider.
@@ -451,7 +452,7 @@
   for (const auto& network : networks) {
     if (network->connection_state == ConnectionStateType::kNotConnected)
       break;
-    if (network->vpn->type != VpnType::kArc)
+    if (network->type_state->get_vpn()->type != VpnType::kArc)
       continue;
 
     // If no matched provider found for this network. Show it unnested.
diff --git a/ash/system/power/power_button_menu_view.cc b/ash/system/power/power_button_menu_view.cc
index f4d7890..51cb537 100644
--- a/ash/system/power/power_button_menu_view.cc
+++ b/ash/system/power/power_button_menu_view.cc
@@ -183,38 +183,31 @@
 }
 
 void PowerButtonMenuView::Layout() {
-  gfx::Rect rect(GetContentsBounds());
-  const gfx::Size item_size(power_off_item_->GetPreferredSize());
-  rect.set_size(item_size);
-  gfx::Rect power_off_rect(rect);
+  gfx::Rect rect(GetContentsBounds().origin(),
+                 power_off_item_->GetPreferredSize());
   const int y_offset =
       kMenuItemVerticalPadding - PowerButtonMenuItemView::kItemBorderThickness;
   int x_offset = kMenuItemHorizontalPadding -
                  PowerButtonMenuItemView::kItemBorderThickness;
-  power_off_rect.Offset(x_offset, y_offset);
-  power_off_item_->SetBoundsRect(power_off_rect);
+  rect.Offset(x_offset, y_offset);
+  power_off_item_->SetBoundsRect(rect);
 
   if (sign_out_item_) {
-    gfx::Rect sign_out_rect(rect);
     const int padding_between_items_with_border =
         kPaddingBetweenMenuItems -
         2 * PowerButtonMenuItemView::kItemBorderThickness;
-    x_offset += item_size.width() + padding_between_items_with_border;
-    sign_out_rect.Offset(x_offset, y_offset);
-    sign_out_item_->SetBoundsRect(sign_out_rect);
+    x_offset = rect.width() + padding_between_items_with_border;
+    rect.Offset(x_offset, 0);
+    sign_out_item_->SetBoundsRect(rect);
 
     if (lock_screen_item_) {
-      gfx::Rect lock_screen_rect(rect);
-      x_offset += item_size.width() + padding_between_items_with_border;
-      lock_screen_rect.Offset(x_offset, y_offset);
-      lock_screen_item_->SetBoundsRect(lock_screen_rect);
+      rect.Offset(x_offset, 0);
+      lock_screen_item_->SetBoundsRect(rect);
     }
 
     if (feedback_item_) {
-      gfx::Rect feedback_rect(rect);
-      x_offset += item_size.width() + padding_between_items_with_border;
-      feedback_rect.Offset(x_offset, y_offset);
-      feedback_item_->SetBoundsRect(feedback_rect);
+      rect.Offset(x_offset, 0);
+      feedback_item_->SetBoundsRect(rect);
     }
   }
 }
diff --git a/base/native_library_win.cc b/base/native_library_win.cc
index 08d520d0..74413b6 100644
--- a/base/native_library_win.cc
+++ b/base/native_library_win.cc
@@ -8,7 +8,6 @@
 
 #include "base/files/file_util.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/optional.h"
 #include "base/path_service.h"
 #include "base/scoped_native_library.h"
 #include "base/strings/string_util.h"
@@ -170,12 +169,12 @@
   return module;
 }
 
-Optional<FilePath> GetSystemLibraryName(FilePath::StringPieceType name) {
+FilePath GetSystemLibraryName(FilePath::StringPieceType name) {
   FilePath library_path;
   // Use an absolute path to load the DLL to avoid DLL preloading attacks.
-  if (!base::PathService::Get(base::DIR_SYSTEM, &library_path))
-    return base::nullopt;
-  return make_optional(library_path.Append(name));
+  if (PathService::Get(DIR_SYSTEM, &library_path))
+    library_path = library_path.Append(name);
+  return library_path;
 }
 
 }  // namespace
@@ -210,18 +209,19 @@
 
 NativeLibrary LoadSystemLibrary(FilePath::StringPieceType name,
                                 NativeLibraryLoadError* error) {
-  Optional<FilePath> library_path = GetSystemLibraryName(name);
-  if (library_path)
-    return LoadSystemLibraryHelper(library_path.value(), error);
-  if (error)
-    error->code = ERROR_NOT_FOUND;
-  return nullptr;
+  FilePath library_path = GetSystemLibraryName(name);
+  if (library_path.empty()) {
+    if (error)
+      error->code = ERROR_NOT_FOUND;
+    return nullptr;
+  }
+  return LoadSystemLibraryHelper(library_path, error);
 }
 
 NativeLibrary PinSystemLibrary(FilePath::StringPieceType name,
                                NativeLibraryLoadError* error) {
-  Optional<FilePath> library_path = GetSystemLibraryName(name);
-  if (!library_path) {
+  FilePath library_path = GetSystemLibraryName(name);
+  if (library_path.empty()) {
     if (error)
       error->code = ERROR_NOT_FOUND;
     return nullptr;
@@ -231,25 +231,28 @@
   // Dllmain.
   ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
   ScopedNativeLibrary module;
-  if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
-                            as_wcstr(library_path.value().value()),
-                            ScopedNativeLibrary::Receiver(module).get())) {
-    // Load and pin the library since it wasn't already loaded.
-    module = ScopedNativeLibrary(
-        LoadSystemLibraryHelper(library_path.value(), error));
-    if (module.is_valid()) {
-      ScopedNativeLibrary temp;
-      if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
-                                as_wcstr(library_path.value().value()),
-                                ScopedNativeLibrary::Receiver(temp).get())) {
-        if (error)
-          error->code = ::GetLastError();
-        // Return nullptr since we failed to pin the module.
-        return nullptr;
-      }
-    }
+  if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
+                           as_wcstr(library_path.value()),
+                           ScopedNativeLibrary::Receiver(module).get())) {
+    return module.release();
   }
-  return module.release();
+
+  // Load and pin the library since it wasn't already loaded.
+  module = ScopedNativeLibrary(LoadSystemLibraryHelper(library_path, error));
+  if (!module.is_valid())
+    return nullptr;
+
+  ScopedNativeLibrary temp;
+  if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
+                           as_wcstr(library_path.value()),
+                           ScopedNativeLibrary::Receiver(temp).get())) {
+    return module.release();
+  }
+
+  if (error)
+    error->code = ::GetLastError();
+  // Return nullptr since we failed to pin the module.
+  return nullptr;
 }
 
 }  // namespace base
diff --git a/base/trace_event/trace_log.cc b/base/trace_event/trace_log.cc
index 39c19c8..1e0cd14 100644
--- a/base/trace_event/trace_log.cc
+++ b/base/trace_event/trace_log.cc
@@ -378,7 +378,7 @@
       num_traces_recorded_(0),
       process_sort_index_(0),
       process_id_hash_(0),
-      process_id_(0),
+      process_id_(base::kNullProcessId),
       trace_options_(kInternalRecordUntilFull),
       trace_config_(TraceConfig()),
       thread_shared_chunk_index_(0),
diff --git a/base/trace_event/trace_log.h b/base/trace_event/trace_log.h
index 63be5aa..e0c850ca 100644
--- a/base/trace_event/trace_log.h
+++ b/base/trace_event/trace_log.h
@@ -301,6 +301,7 @@
                         TraceEventHandle handle);
 
   int process_id() const { return process_id_; }
+  const std::string& process_name() const { return process_name_; }
 
   uint64_t MangleEventId(uint64_t id);
 
diff --git a/base/win/win_util.cc b/base/win/win_util.cc
index 97301a1..edfbcf1 100644
--- a/base/win/win_util.cc
+++ b/base/win/win_util.cc
@@ -234,7 +234,7 @@
 // if the keyboard count is 1 or more.. While this will work in most cases
 // it won't work if there are devices which expose keyboard interfaces which
 // are attached to the machine.
-bool IsKeyboardPresentOnSlate(std::string* reason, HWND hwnd) {
+bool IsKeyboardPresentOnSlate(HWND hwnd, std::string* reason) {
   bool result = false;
 
   if (GetVersion() < Version::WIN8) {
@@ -267,15 +267,14 @@
     if (reason)
       *reason += "Tablet device.\n";
     return false;
-  } else {
-    if (reason) {
-      *reason += "Not a tablet device";
-      result = true;
-    } else {
-      return true;
-    }
   }
 
+  if (!reason)
+    return true;
+
+  *reason += "Not a tablet device";
+  result = true;
+
   // To determine whether a keyboard is present on the device, we do the
   // following:-
   // 1. Check whether the device supports auto rotation. If it does then
@@ -302,13 +301,12 @@
       // If there is no auto rotation sensor or rotation is not supported in
       // the current configuration, then we can assume that this is a desktop
       // or a traditional laptop.
-      if (reason) {
-        *reason += (auto_rotation_state & AR_NOSENSOR) ? "AR_NOSENSOR\n"
-                                                       : "AR_NOT_SUPPORTED\n";
-        result = true;
-      } else {
+      if (!reason)
         return true;
-      }
+
+      *reason += (auto_rotation_state & AR_NOSENSOR) ? "AR_NOSENSOR\n"
+                                                     : "AR_NOT_SUPPORTED\n";
+      result = true;
     }
   }
 
@@ -341,10 +339,9 @@
     if (status == CR_SUCCESS) {
       // To reduce the scope of the hack we only look for ACPI and HID\\VID
       // prefixes in the keyboard device ids.
-      if (StartsWith(base::AsStringPiece16(device_id), STRING16_LITERAL("ACPI"),
+      if (StartsWith(AsStringPiece16(device_id), STRING16_LITERAL("ACPI"),
                      CompareCase::INSENSITIVE_ASCII) ||
-          StartsWith(base::AsStringPiece16(device_id),
-                     STRING16_LITERAL("HID\\VID"),
+          StartsWith(AsStringPiece16(device_id), STRING16_LITERAL("HID\\VID"),
                      CompareCase::INSENSITIVE_ASCII)) {
         if (reason) {
           *reason += "device: ";
diff --git a/base/win/win_util.h b/base/win/win_util.h
index 8c938957..c0848b6 100644
--- a/base/win/win_util.h
+++ b/base/win/win_util.h
@@ -149,7 +149,7 @@
 // A slate is a touch device that may have a keyboard attached. This function
 // returns true if a keyboard is attached and optionally will set the |reason|
 // parameter to the detection method that was used to detect the keyboard.
-BASE_EXPORT bool IsKeyboardPresentOnSlate(std::string* reason, HWND hwnd);
+BASE_EXPORT bool IsKeyboardPresentOnSlate(HWND hwnd, std::string* reason);
 
 // Get the size of a struct up to and including the specified member.
 // This is necessary to set compatible struct sizes for different versions
diff --git a/chrome/VERSION b/chrome/VERSION
index b5ed79c..09054b9a 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=79
 MINOR=0
-BUILD=3925
+BUILD=3926
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 1294192..09546bb 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1536,6 +1536,7 @@
     "javatests/src/org/chromium/chrome/browser/ServicificationBackgroundService.java",
     "javatests/src/org/chromium/chrome/browser/offlinepages/OfflineTestUtil.java",
     "javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchTestBridge.java",
+    "javatests/src/org/chromium/chrome/browser/prerender/PrerenderTestHelper.java",
     "javatests/src/org/chromium/chrome/browser/test/MockCertVerifierRuleAndroid.java",
   ]
 }
@@ -1546,6 +1547,7 @@
   java_files = [
     "javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchTestBridge.java",
     "javatests/src/org/chromium/chrome/browser/offlinepages/OfflineTestUtil.java",
+    "javatests/src/org/chromium/chrome/browser/prerender/PrerenderTestHelper.java",
     "javatests/src/org/chromium/chrome/browser/sync/FakeServerHelper.java",
     "javatests/src/org/chromium/chrome/browser/test/MockCertVerifierRuleAndroid.java",
     "javatests/src/org/chromium/chrome/browser/ServicificationBackgroundService.java",
@@ -1573,6 +1575,7 @@
     "../browser/android/ssl/mock_cert_verifier_rule_android.h",
     "../browser/offline_pages/android/offline_test_util_jni.cc",
     "../browser/offline_pages/android/prefetch_test_bridge.cc",
+    "../browser/prerender/prerender_test_helper.cc",
   ]
   deps = [
     ":test_support_jni_headers",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index d51ae5f..7331642 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -298,7 +298,6 @@
   "javatests/src/org/chromium/chrome/browser/password_manager/OnboardingDialogIntegrationTest.java",
   "javatests/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogTest.java",
   "javatests/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogTest.java",
-  "javatests/src/org/chromium/chrome/browser/payments/micro/MicrotransactionRenderTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/CurrencyFormatterTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/MockPackageManagerDelegate.java",
diff --git a/chrome/android/java/res/drawable-hdpi/permission_push_notification_off.png b/chrome/android/java/res/drawable-hdpi/permission_push_notification_off.png
new file mode 100644
index 0000000..2aa6c90
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/permission_push_notification_off.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/permission_push_notification_off.png b/chrome/android/java/res/drawable-mdpi/permission_push_notification_off.png
new file mode 100644
index 0000000..79fcb5c1
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/permission_push_notification_off.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/permission_push_notification_off.png b/chrome/android/java/res/drawable-xhdpi/permission_push_notification_off.png
new file mode 100644
index 0000000..a96ccf4
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/permission_push_notification_off.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/permission_push_notification_off.png b/chrome/android/java/res/drawable-xxhdpi/permission_push_notification_off.png
new file mode 100644
index 0000000..b658ee0e
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/permission_push_notification_off.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/permission_push_notification_off.png b/chrome/android/java/res/drawable-xxxhdpi/permission_push_notification_off.png
new file mode 100644
index 0000000..6c3756c
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxxhdpi/permission_push_notification_off.png
Binary files differ
diff --git a/chrome/android/java/res/layout/microtransaction_toolbar.xml b/chrome/android/java/res/layout/microtransaction_toolbar.xml
index cc5b2fc..1f22722 100644
--- a/chrome/android/java/res/layout/microtransaction_toolbar.xml
+++ b/chrome/android/java/res/layout/microtransaction_toolbar.xml
@@ -4,6 +4,7 @@
      found in the LICENSE file. -->
 <RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:background="@color/sheet_bg_color"
     android:layout_height="wrap_content"
     android:layout_width="match_parent">
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index ccc7e0a..64449fd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -2191,7 +2191,8 @@
         }
         // Detecting a long press of the back button via onLongPress is broken in Android N.
         // To work around this, use a postDelayed, which is supported in all versions.
-        if (keyCode == KeyEvent.KEYCODE_BACK && !isTablet()) {
+        if (keyCode == KeyEvent.KEYCODE_BACK && !isTablet()
+                && !getFullscreenManager().getPersistentFullscreenMode()) {
             if (mShowHistoryRunnable == null) mShowHistoryRunnable = this ::showFullHistoryForTab;
             mHandler.postDelayed(mShowHistoryRunnable, ViewConfiguration.getLongPressTimeout());
             return super.onKeyDown(keyCode, event);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index 69b4d2b..95ca1de 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -1836,11 +1836,6 @@
         if (view != null) view.requestFocus();
     }
 
-    @VisibleForTesting
-    public boolean hasPrerenderedUrl(String url) {
-        return TabJni.get().hasPrerenderedUrl(mNativeTabAndroid, Tab.this, url);
-    }
-
     /**
      * Update whether or not the current native tab and/or web contents are
      * currently visible (from an accessibility perspective), or whether
@@ -1917,7 +1912,6 @@
                 long nativeTabAndroid, Tab caller, String url, String title);
         void createHistoricalTab(long nativeTabAndroid, Tab caller);
         void loadOriginalImage(long nativeTabAndroid, Tab caller);
-        boolean hasPrerenderedUrl(long nativeTabAndroid, Tab caller, String url);
         void attachDetachedTab(long nativeTabAndroid, Tab caller);
         boolean areRendererInputEventsIgnored(long nativeTabAndroid, Tab caller);
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/PrerenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/PrerenderTest.java
index 925dabc2..911434b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/PrerenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/PrerenderTest.java
@@ -22,10 +22,10 @@
 import org.chromium.base.test.util.FlakyTest;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.prerender.ExternalPrerenderHandler;
+import org.chromium.chrome.browser.prerender.PrerenderTestHelper;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
-import org.chromium.chrome.test.util.PrerenderTestHelper;
 import org.chromium.chrome.test.util.browser.TabTitleObserver;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.net.test.EmbeddedTestServer;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java
index 5965667..ea6e69e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/fullscreen/FullscreenManagerTest.java
@@ -38,6 +38,7 @@
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.omnibox.UrlBar;
+import org.chromium.chrome.browser.prerender.PrerenderTestHelper;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabStateBrowserControlsVisibilityDelegate;
 import org.chromium.chrome.browser.tab.TabWebContentsDelegateAndroid;
@@ -45,7 +46,6 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.FullscreenTestUtils;
 import org.chromium.chrome.test.util.OmniboxTestUtils;
-import org.chromium.chrome.test.util.PrerenderTestHelper;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.content_public.browser.GestureListenerManager;
 import org.chromium.content_public.browser.GestureStateListener;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/micro/MicrotransactionRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/micro/MicrotransactionRenderTest.java
deleted file mode 100644
index e2490c4..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/micro/MicrotransactionRenderTest.java
+++ /dev/null
@@ -1,348 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.payments.micro;
-
-import android.support.annotation.Nullable;
-import android.support.graphics.drawable.VectorDrawableCompat;
-import android.support.test.filters.SmallTest;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ProgressBar;
-import android.widget.RelativeLayout;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.test.util.Feature;
-import org.chromium.chrome.R;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.ui.DummyUiActivityTestCase;
-import org.chromium.chrome.test.util.RenderTestRule;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.ui.modelutil.PropertyModel;
-import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
-
-/** Tests for the microtransaction view binder. */
-@RunWith(ChromeJUnit4ClassRunner.class)
-public class MicrotransactionRenderTest extends DummyUiActivityTestCase {
-    @Rule
-    public RenderTestRule mRenderTestRule =
-            new RenderTestRule("components/test/data/payments/render_tests");
-
-    private PropertyModel mModel;
-    private MicrotransactionView mView;
-    private RelativeLayout mLayout;
-    private PropertyModelChangeProcessor mProcessor;
-
-    @Override
-    public void setUpTest() throws Exception {
-        super.setUpTest();
-        // Initial button state:
-        mModel =
-                new PropertyModel.Builder(MicrotransactionProperties.ALL_KEYS)
-                        .with(MicrotransactionProperties.ACCOUNT_BALANCE, "$18.00")
-                        .with(MicrotransactionProperties.AMOUNT, "$1.00")
-                        .with(MicrotransactionProperties.CURRENCY, "USD")
-                        .with(MicrotransactionProperties.IS_SHOWING_LINE_ITEMS, true)
-                        .with(MicrotransactionProperties.IS_SHOWING_PAY_BUTTON, true)
-                        .with(MicrotransactionProperties.IS_SHOWING_PROCESSING_SPINNER, false)
-                        .with(MicrotransactionProperties.PAYMENT_APP_ICON,
-                                VectorDrawableCompat.create(getActivity().getResources(),
-                                        R.drawable.ic_done_googblue_36dp, getActivity().getTheme()))
-                        .with(MicrotransactionProperties.PAYMENT_APP_NAME, "App Name")
-                        .with(MicrotransactionProperties.STATUS_TEXT_RESOURCE,
-                                R.string.payment_request_payment_method_section_name)
-                        .build();
-
-        mView = new MicrotransactionView(getActivity());
-        mProcessor = PropertyModelChangeProcessor.create(
-                mModel, mView, MicrotransactionViewBinder::bind);
-
-        mLayout = new RelativeLayout(getActivity());
-        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-        mLayout.addView(mView.getContentView(), params);
-        mLayout.addView(mView.getToolbarView(), params);
-
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            getActivity().setContentView(mLayout,
-                    new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                            ViewGroup.LayoutParams.WRAP_CONTENT));
-        });
-    }
-
-    @Override
-    public void tearDownTest() throws Exception {
-        mProcessor.destroy();
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testButtonInitial() throws Throwable {
-        mRenderTestRule.render(mLayout, "button_initial");
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testButtonInitialExpanded() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> mModel.set(MicrotransactionProperties.PAYMENT_APP_NAME_ALPHA, 1f));
-        mRenderTestRule.render(mLayout, "button_initial_expanded");
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testButtonProcessing() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(() -> showButtonProcessing());
-        mRenderTestRule.render(mLayout, "button_processing");
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testButtonProcessingExpanded() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mModel.set(MicrotransactionProperties.PAYMENT_APP_NAME_ALPHA, 1f);
-            showButtonProcessing();
-        });
-        mRenderTestRule.render(mLayout, "button_processing_expanded");
-    }
-
-    private void showButtonProcessing() {
-        mModel.set(MicrotransactionProperties.IS_SHOWING_LINE_ITEMS, false);
-        mModel.set(MicrotransactionProperties.IS_SHOWING_PAY_BUTTON, false);
-        mModel.set(MicrotransactionProperties.IS_SHOWING_PROCESSING_SPINNER, true);
-        mModel.set(MicrotransactionProperties.STATUS_TEXT, null);
-        mModel.set(MicrotransactionProperties.STATUS_TEXT_RESOURCE,
-                R.string.payments_processing_message);
-
-        stopSpinner(mView.mToolbarProcessingSpinner);
-        stopSpinner(mView.mContentProcessingSpinner);
-    }
-
-    private void stopSpinner(View view) {
-        ProgressBar spinner = (ProgressBar) view;
-        spinner.setIndeterminate(false);
-        spinner.setMax(2);
-        spinner.setProgress(1);
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testButtonErrorResource() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            showEmphasizedStatus(R.string.payment_fingerprint_not_recognized, null,
-                    R.drawable.ic_error_googred_36dp, R.color.microtransaction_error_tint);
-            mModel.set(MicrotransactionProperties.IS_SHOWING_LINE_ITEMS, false);
-        });
-        mRenderTestRule.render(mLayout, "button_error_resource");
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testButtonErrorResourceExpanded() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mModel.set(MicrotransactionProperties.PAYMENT_APP_NAME_ALPHA, 1f);
-            showEmphasizedStatus(R.string.payment_fingerprint_not_recognized, null,
-                    R.drawable.ic_error_googred_36dp, R.color.microtransaction_error_tint);
-            mModel.set(MicrotransactionProperties.IS_SHOWING_LINE_ITEMS, false);
-        });
-        mRenderTestRule.render(mLayout, "button_error_resource_expanded");
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testButtonErrorString() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            showEmphasizedStatus(null, "Finger moved too fast. Try again.",
-                    R.drawable.ic_error_googred_36dp, R.color.microtransaction_error_tint);
-            mModel.set(MicrotransactionProperties.IS_SHOWING_LINE_ITEMS, false);
-        });
-        mRenderTestRule.render(mLayout, "button_error_string");
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testButtonErrorStringExpanded() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mModel.set(MicrotransactionProperties.PAYMENT_APP_NAME_ALPHA, 1f);
-            showEmphasizedStatus(null, "Finger moved too fast. Try again.",
-                    R.drawable.ic_error_googred_36dp, R.color.microtransaction_error_tint);
-            mModel.set(MicrotransactionProperties.IS_SHOWING_LINE_ITEMS, false);
-        });
-        mRenderTestRule.render(mLayout, "button_error_string_expanded");
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testButtonSuccess() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            showEmphasizedStatus(R.string.payment_complete_message, null,
-                    R.drawable.ic_done_googblue_36dp, R.color.microtransaction_emphasis_tint);
-            mModel.set(MicrotransactionProperties.IS_SHOWING_LINE_ITEMS, false);
-        });
-        mRenderTestRule.render(mLayout, "button_success");
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testButtonSuccessExpanded() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mModel.set(MicrotransactionProperties.PAYMENT_APP_NAME_ALPHA, 1f);
-            showEmphasizedStatus(R.string.payment_complete_message, null,
-                    R.drawable.ic_done_googblue_36dp, R.color.microtransaction_emphasis_tint);
-            mModel.set(MicrotransactionProperties.IS_SHOWING_LINE_ITEMS, false);
-        });
-        mRenderTestRule.render(mLayout, "button_success_expanded");
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testFingerprintInitial() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(() -> showFingerprintInitial());
-        mRenderTestRule.render(mLayout, "fingerprint_initial");
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testFingerprintInitialExpanded() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mModel.set(MicrotransactionProperties.PAYMENT_APP_NAME_ALPHA, 1f);
-            showFingerprintInitial();
-        });
-        mRenderTestRule.render(mLayout, "fingerprint_initial_expanded");
-    }
-
-    private void showFingerprintInitial() {
-        mModel.set(MicrotransactionProperties.IS_SHOWING_PAY_BUTTON, false);
-        mModel.set(MicrotransactionProperties.STATUS_ICON, R.drawable.ic_fingerprint_grey500_36dp);
-        mModel.set(
-                MicrotransactionProperties.STATUS_ICON_TINT, R.color.microtransaction_default_tint);
-        mModel.set(MicrotransactionProperties.STATUS_TEXT_RESOURCE,
-                R.string.payment_touch_sensor_to_pay);
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testFingerprintProcessing() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(() -> showFingerprintProcessing());
-        mRenderTestRule.render(mLayout, "fingerprint_processing");
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testFingerprintProcessingExpanded() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mModel.set(MicrotransactionProperties.PAYMENT_APP_NAME_ALPHA, 1f);
-            showFingerprintProcessing();
-        });
-        mRenderTestRule.render(mLayout, "fingerprint_processing_expanded");
-    }
-
-    private void showFingerprintProcessing() {
-        mModel.set(MicrotransactionProperties.IS_SHOWING_PAY_BUTTON, false);
-        mModel.set(MicrotransactionProperties.STATUS_TEXT, null);
-        mModel.set(MicrotransactionProperties.STATUS_TEXT_RESOURCE,
-                R.string.payments_processing_message);
-        mModel.set(MicrotransactionProperties.STATUS_ICON, R.drawable.ic_fingerprint_grey500_36dp);
-        mModel.set(MicrotransactionProperties.STATUS_ICON_TINT,
-                R.color.microtransaction_emphasis_tint);
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testFingerprintErrorResource() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            showEmphasizedStatus(R.string.payment_fingerprint_not_recognized, null,
-                    R.drawable.ic_error_googred_36dp, R.color.microtransaction_error_tint);
-        });
-        mRenderTestRule.render(mLayout, "fingerprint_error_resource");
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testFingerprintErrorResourceExpanded() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mModel.set(MicrotransactionProperties.PAYMENT_APP_NAME_ALPHA, 1f);
-            showEmphasizedStatus(R.string.payment_fingerprint_not_recognized, null,
-                    R.drawable.ic_error_googred_36dp, R.color.microtransaction_error_tint);
-        });
-        mRenderTestRule.render(mLayout, "fingerprint_error_resource_expanded");
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testFingerprintErrorString() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            showEmphasizedStatus(null, "Finger moved too fast. Try again.",
-                    R.drawable.ic_error_googred_36dp, R.color.microtransaction_error_tint);
-        });
-        mRenderTestRule.render(mLayout, "fingerprint_error_string");
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testFingerprintErrorStringExpanded() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mModel.set(MicrotransactionProperties.PAYMENT_APP_NAME_ALPHA, 1f);
-            showEmphasizedStatus(null, "Finger moved too fast. Try again.",
-                    R.drawable.ic_error_googred_36dp, R.color.microtransaction_error_tint);
-        });
-        mRenderTestRule.render(mLayout, "fingerprint_error_string_expanded");
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testFingerprintSuccess() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            showEmphasizedStatus(R.string.payment_complete_message, null,
-                    R.drawable.ic_done_googblue_36dp, R.color.microtransaction_emphasis_tint);
-        });
-        mRenderTestRule.render(mLayout, "fingerprint_success");
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Microtransaction", "RenderTest"})
-    public void testFingerprintSuccessExpanded() throws Throwable {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mModel.set(MicrotransactionProperties.PAYMENT_APP_NAME_ALPHA, 1f);
-            showEmphasizedStatus(R.string.payment_complete_message, null,
-                    R.drawable.ic_done_googblue_36dp, R.color.microtransaction_emphasis_tint);
-        });
-        mRenderTestRule.render(mLayout, "fingerprint_success_expanded");
-    }
-
-    private void showEmphasizedStatus(@Nullable Integer messageResourceId,
-            @Nullable CharSequence message, @Nullable Integer iconResourceId,
-            @Nullable Integer iconTint) {
-        mModel.set(MicrotransactionProperties.IS_SHOWING_PAY_BUTTON, false);
-        mModel.set(MicrotransactionProperties.STATUS_TEXT, message);
-        mModel.set(MicrotransactionProperties.STATUS_TEXT_RESOURCE, messageResourceId);
-        mModel.set(MicrotransactionProperties.IS_STATUS_EMPHASIZED, true);
-        mModel.set(MicrotransactionProperties.STATUS_ICON, iconResourceId);
-        mModel.set(MicrotransactionProperties.STATUS_ICON_TINT, iconTint);
-        mModel.set(MicrotransactionProperties.IS_SHOWING_PROCESSING_SPINNER, false);
-    }
-}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/PrerenderTestHelper.java b/chrome/android/javatests/src/org/chromium/chrome/browser/prerender/PrerenderTestHelper.java
similarity index 61%
rename from chrome/test/android/javatests/src/org/chromium/chrome/test/util/PrerenderTestHelper.java
rename to chrome/android/javatests/src/org/chromium/chrome/browser/prerender/PrerenderTestHelper.java
index a029154..6e8a0b8 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/PrerenderTestHelper.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/prerender/PrerenderTestHelper.java
@@ -2,16 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.test.util;
+package org.chromium.chrome.browser.prerender;
 
 import android.graphics.Rect;
 
 import org.junit.Assert;
 
 import org.chromium.chrome.browser.TabLoadStatus;
-import org.chromium.chrome.browser.prerender.ExternalPrerenderHandler;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.test.ChromeActivityTestRule;
+import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.test.util.Coordinates;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
@@ -31,7 +30,7 @@
         return TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Boolean>() {
             @Override
             public Boolean call() {
-                return tab.hasPrerenderedUrl(url);
+                return nativeHasPrerenderedUrl(tab.getWebContents(), url);
             }
         });
     }
@@ -42,8 +41,8 @@
      * shortTimeout should be set to true when expecting this function to return false, as to
      * make the tests run faster.
      */
-    public static boolean waitForPrerenderUrl(final Tab tab, final String url,
-            boolean shortTimeout) {
+    public static boolean waitForPrerenderUrl(
+            final Tab tab, final String url, boolean shortTimeout) {
         try {
             CriteriaHelper.pollInstrumentationThread(new Criteria() {
                 @Override
@@ -59,28 +58,6 @@
     }
 
     /**
-     * Clears the omnibox.
-     *
-     * @param testRule ChromeActivityTestRule instance.
-     */
-    public static void clearOmnibox(ChromeActivityTestRule<?> testRule)
-            throws InterruptedException {
-        testRule.typeInOmnibox("", false);
-    }
-
-    /**
-     * Clears the omnibox and types in the url character-by-character.
-     *
-     * @param url url to type into the omnibox.
-     * @param testRule ChromeActivityTestRule<?> instance.
-     */
-    public static void clearOmniboxAndTypeUrl(String url, ChromeActivityTestRule<?> testRule)
-            throws InterruptedException {
-        clearOmnibox(testRule);
-        testRule.typeInOmnibox(url, true);
-    }
-
-    /**
      * Prerenders a url.
      *
      * @param testUrl Url to prerender
@@ -90,21 +67,24 @@
         final Tab currentTab = tab;
         final Coordinates coord = Coordinates.createFor(currentTab.getWebContents());
         ExternalPrerenderHandler prerenderHandler =
-                TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<
-                        ExternalPrerenderHandler>() {
-                    @Override
-                    public ExternalPrerenderHandler call() throws Exception {
-                        ExternalPrerenderHandler prerenderHandler = new ExternalPrerenderHandler();
-                        Rect bounds = new Rect(0, 0, coord.getContentWidthPixInt(),
-                                coord.getContentHeightPixInt());
-                        boolean didPrerender =
-                                prerenderHandler.addPrerender(currentTab.getProfile(),
-                                        currentTab.getWebContents(), testUrl, null, bounds, true)
-                                != null;
-                        Assert.assertTrue("Failed to prerender test url: " + testUrl, didPrerender);
-                        return prerenderHandler;
-                    }
-                });
+                TestThreadUtils.runOnUiThreadBlockingNoException(
+                        new Callable<ExternalPrerenderHandler>() {
+                            @Override
+                            public ExternalPrerenderHandler call() throws Exception {
+                                ExternalPrerenderHandler prerenderHandler =
+                                        new ExternalPrerenderHandler();
+                                Rect bounds = new Rect(0, 0, coord.getContentWidthPixInt(),
+                                        coord.getContentHeightPixInt());
+                                boolean didPrerender =
+                                        prerenderHandler.addPrerender(currentTab.getProfile(),
+                                                currentTab.getWebContents(), testUrl, null, bounds,
+                                                true)
+                                        != null;
+                                Assert.assertTrue(
+                                        "Failed to prerender test url: " + testUrl, didPrerender);
+                                return prerenderHandler;
+                            }
+                        });
 
         Assert.assertTrue("URL was not prerendered.",
                 PrerenderTestHelper.waitForPrerenderUrl(currentTab, testUrl, false));
@@ -122,4 +102,6 @@
         return result == TabLoadStatus.FULL_PRERENDERED_PAGE_LOAD
                 || result == TabLoadStatus.PARTIAL_PRERENDERED_PAGE_LOAD;
     }
+
+    private static native boolean nativeHasPrerenderedUrl(WebContents webContents, String url);
 }
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index ca50175c..f73c85fa 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -4785,9 +4785,6 @@
     <message name="IDS_SETTINGS_STORAGE_ITEM_DOWNLOADS" desc="In Device Settings > Storage, label for the size of Downloads directory.">
       Downloads
     </message>
-    <message name="IDS_SETTINGS_STORAGE_ITEM_DRIVE_CACHE" desc="In Device Settings > Storage, label for the size of Google Drive's offline files.">
-      Offline files
-    </message>
     <message name="IDS_SETTINGS_STORAGE_ITEM_BROWSING_DATA" desc="In Device Settings > Storage, label for the size of browsing data.">
       Browsing data
     </message>
@@ -4824,15 +4821,6 @@
     <message name="IDS_SETTINGS_STORAGE_SPACE_CRITICALLY_LOW_MESSAGE_LINE_2" desc="The second paragraph of a warning message indicating device's available disk space is critically low.">
       If you don’t free up space, users and data may be automatically removed.
     </message>
-    <message name="IDS_SETTINGS_STORAGE_CLEAR_DRIVE_CACHE_DIALOG_TITLE" desc="Title for the storage manager confirmation dialog tab to remove Google Drive offline files.">
-      Delete offline files?
-    </message>
-    <message name="IDS_SETTINGS_STORAGE_CLEAR_DRIVE_CACHE_DESCRIPTION" desc="Description in the storage manager confirmation dialog to remove Google Drive offline files.">
-      Temporary Google Drive offline files will be deleted. Files that you’ve set as available offline won’t be deleted from this device.
-    </message>
-    <message name="IDS_SETTINGS_STORAGE_DELETE_ALL_BUTTON_TITLE" desc="In storage manager confirmation dialog, label for the button to delete offline files.">
-      Delete files
-    </message>
     <message name="IDS_SETTINGS_STORAGE_EXTERNAL" desc="In Device Settings > Storage, label for the subpage for setting preferences for external storage.">
       External storage preferences
     </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 8995b4d..8950a0ce8 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -5386,6 +5386,7 @@
 
   if (is_chromeos) {
     deps += [
+      "//chrome/browser/resources/chromeos/crostini_installer:polymer3_elements",
       "//chrome/browser/ui/webui/chromeos/add_supervision:mojo_bindings_js",
       "//chrome/browser/ui/webui/chromeos/crostini_installer:mojo_bindings_js",
       "//chrome/browser/ui/webui/chromeos/machine_learning:mojo_bindings_js",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 39f62458..90a9256 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -6,6 +6,7 @@
   "+chrome/android/features/dev_ui",
   "+chrome/android/modules",
   "+chrome/android/public/profiles/jni_headers",
+  "+chrome/android/test_support_jni_headers",
   "+chrome/app",
   "+chrome/chrome_watcher",
   "+chrome/credential_provider/common",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index ad400a16..d840695 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -90,7 +90,6 @@
 #include "components/ntp_tiles/features.h"
 #include "components/offline_pages/core/offline_page_feature.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
-#include "components/omnibox/browser/zero_suggest_provider.h"
 #include "components/omnibox/common/omnibox_features.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/payments/core/features.h"
@@ -859,21 +858,38 @@
 #endif  // defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
 
 const FeatureEntry::FeatureParam kOmniboxOnFocusSuggestionsParamSERP[] = {
-    {"ZeroSuggestVariant:6:*", ZeroSuggestProvider::kRemoteSendUrlVariant}};
-const FeatureEntry::FeatureParam kOmniboxOnFocusSuggestionsParamNTPOmnibox[] = {
-    {"ZeroSuggestVariant:7:*", ZeroSuggestProvider::kRemoteNoUrlVariant}};
-const FeatureEntry::FeatureParam kOmniboxOnFocusSuggestionsParamNTPRealbox[] = {
-    {"ZeroSuggestVariant:15:*", ZeroSuggestProvider::kRemoteNoUrlVariant}};
+    {"ZeroSuggestVariant:6:*", "RemoteSendUrl"}};
+const FeatureEntry::FeatureParam
+    kOmniboxOnFocusSuggestionsParamNTPOmniboxRemote[] = {
+        {"ZeroSuggestVariant:7:*", "RemoteNoUrl"}};
+const FeatureEntry::FeatureParam
+    kOmniboxOnFocusSuggestionsParamNTPOmniboxRemoteLocal[] = {
+        {"ZeroSuggestVariant:7:*", "RemoteNoUrl,Local"}};
+const FeatureEntry::FeatureParam
+    kOmniboxOnFocusSuggestionsParamNTPRealboxRemote[] = {
+        {"ZeroSuggestVariant:15:*", "RemoteNoUrl"}};
+const FeatureEntry::FeatureParam
+    kOmniboxOnFocusSuggestionsParamNTPRealboxRemoteLocal[] = {
+        {"ZeroSuggestVariant:15:*", "RemoteNoUrl,Local"}};
 const FeatureEntry::FeatureVariation kOmniboxOnFocusSuggestionsVariations[] = {
     {"SERP - RemoteSendURL", kOmniboxOnFocusSuggestionsParamSERP,
-     base::size(kOmniboxOnFocusSuggestionsParamNTPOmnibox),
+     base::size(kOmniboxOnFocusSuggestionsParamSERP),
      "t3315869" /* variation_id */},
-    {"NTP Omnibox - Remote", kOmniboxOnFocusSuggestionsParamNTPOmnibox,
-     base::size(kOmniboxOnFocusSuggestionsParamNTPOmnibox),
+    {"NTP Omnibox - Remote", kOmniboxOnFocusSuggestionsParamNTPOmniboxRemote,
+     base::size(kOmniboxOnFocusSuggestionsParamNTPOmniboxRemote),
      "t3316133" /* variation_id */},
-    {"NTP Realbox - Remote", kOmniboxOnFocusSuggestionsParamNTPRealbox,
-     base::size(kOmniboxOnFocusSuggestionsParamNTPRealbox),
+    {"NTP Omnibox - Remote,Local",
+     kOmniboxOnFocusSuggestionsParamNTPOmniboxRemoteLocal,
+     base::size(kOmniboxOnFocusSuggestionsParamNTPOmniboxRemoteLocal),
      "t3316133" /* variation_id */},
+    {"NTP Realbox - Remote", kOmniboxOnFocusSuggestionsParamNTPRealboxRemote,
+     base::size(kOmniboxOnFocusSuggestionsParamNTPRealboxRemote),
+     "t3316133" /* variation_id */},
+    {"NTP Realbox - Remote,Local",
+     kOmniboxOnFocusSuggestionsParamNTPRealboxRemoteLocal,
+     base::size(kOmniboxOnFocusSuggestionsParamNTPRealboxRemoteLocal),
+     "t3316133" /* variation_id */},
+
 };
 
 const FeatureEntry::FeatureParam kOmniboxUIMaxAutocompleteMatches3[] = {
diff --git a/chrome/browser/android/resource_id.h b/chrome/browser/android/resource_id.h
index 7299154..6a3b0fb6 100644
--- a/chrome/browser/android/resource_id.h
+++ b/chrome/browser/android/resource_id.h
@@ -51,6 +51,8 @@
                     R.drawable.infobar_downloading)
 DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_NOTIFICATIONS,
                     R.drawable.infobar_desktop_notifications)
+DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_NOTIFICATIONS_OFF,
+                    R.drawable.permission_push_notification_off)
 DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_PREVIEWS,
                     R.drawable.infobar_chrome)
 DECLARE_RESOURCE_ID(IDR_ANDROID_INFOBAR_PROTECTED_MEDIA_IDENTIFIER,
diff --git a/chrome/browser/android/tab_android.cc b/chrome/browser/android/tab_android.cc
index dbe1e678..b00e343 100644
--- a/chrome/browser/android/tab_android.cc
+++ b/chrome/browser/android/tab_android.cc
@@ -22,9 +22,7 @@
 #include "chrome/browser/browser_about_handler.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/infobars/infobar_service.h"
-#include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/prerender/prerender_manager.h"
-#include "chrome/browser/prerender/prerender_manager_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -200,23 +198,6 @@
   session_tab_helper->SetWindowID(session_window_id_);
 }
 
-bool TabAndroid::HasPrerenderedUrl(GURL gurl) {
-  prerender::PrerenderManager* prerender_manager = GetPrerenderManager();
-  if (!prerender_manager)
-    return false;
-
-  std::vector<content::WebContents*> contents =
-      prerender_manager->GetAllPrerenderingContents();
-  prerender::PrerenderContents* prerender_contents;
-  for (content::WebContents* content : contents) {
-    prerender_contents = prerender_manager->GetPrerenderContents(content);
-    if (prerender_contents->prerender_url() == gurl &&
-        prerender_contents->has_finished_loading()) {
-      return true;
-    }
-  }
-  return false;
-}
 
 bool TabAndroid::IsCurrentlyACustomTab() {
   JNIEnv* env = base::android::AttachCurrentThread();
@@ -383,19 +364,10 @@
 
   // If the page was prerendered, use it.
   // Note in incognito mode, we don't have a PrerenderManager.
-
-  prerender::PrerenderManager* prerender_manager =
-      prerender::PrerenderManagerFactory::GetForBrowserContext(GetProfile());
-  if (prerender_manager) {
-    bool prefetched_page_loaded = HasPrerenderedUrl(gurl);
-    // Getting the load status before MaybeUsePrerenderedPage() b/c it resets.
-    prerender::PrerenderManager::Params params(
-        /*uses_post=*/false, /*extra_headers=*/std::string(),
-        /*should_replace_current_entry=*/false, web_contents());
-    if (prerender_manager->MaybeUsePrerenderedPage(gurl, &params)) {
-      return prefetched_page_loaded ?
-          FULL_PRERENDERED_PAGE_LOAD : PARTIAL_PRERENDERED_PAGE_LOAD;
-    }
+  bool loaded;
+  if (prerender::PrerenderManager::MaybeUsePrerenderedPage(
+          GetProfile(), web_contents(), gurl, &loaded)) {
+    return loaded ? FULL_PRERENDERED_PAGE_LOAD : PARTIAL_PRERENDERED_PAGE_LOAD;
   }
 
   GURL fixed_url(
@@ -470,13 +442,6 @@
     entry->SetTitle(title);
 }
 
-prerender::PrerenderManager* TabAndroid::GetPrerenderManager() const {
-  Profile* profile = GetProfile();
-  if (!profile)
-    return nullptr;
-  return prerender::PrerenderManagerFactory::GetForBrowserContext(profile);
-}
-
 // static
 void TabAndroid::CreateHistoricalTabFromContents(WebContents* web_contents) {
   DCHECK(web_contents);
@@ -514,13 +479,6 @@
   renderer->RequestReloadImageForContextNode();
 }
 
-bool TabAndroid::HasPrerenderedUrl(JNIEnv* env,
-                                   const JavaParamRef<jobject>& obj,
-                                   const JavaParamRef<jstring>& url) {
-  GURL gurl(base::android::ConvertJavaStringToUTF8(env, url));
-  return HasPrerenderedUrl(gurl);
-}
-
 void TabAndroid::AttachDetachedTab(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& obj) {
diff --git a/chrome/browser/android/tab_android.h b/chrome/browser/android/tab_android.h
index adae903..a12f8d0 100644
--- a/chrome/browser/android/tab_android.h
+++ b/chrome/browser/android/tab_android.h
@@ -38,10 +38,6 @@
 class WebContents;
 }
 
-namespace prerender {
-class PrerenderManager;
-}
-
 class TabAndroid {
  public:
   // A Java counterpart will be generated for this enum.
@@ -105,8 +101,6 @@
   void SetWindowSessionID(SessionID window_id);
   void SetSyncId(int sync_id);
 
-  bool HasPrerenderedUrl(GURL gurl);
-
   // Returns true if this tab is currently presented in the context of custom
   // tabs. Tabs can be moved between different activities so the returned value
   // might change over the lifetime of the tab.
@@ -184,10 +178,6 @@
       const base::android::JavaParamRef<jobject>& obj,
       const base::android::JavaParamRef<jobject>& delegate);
 
-  bool HasPrerenderedUrl(JNIEnv* env,
-                         const base::android::JavaParamRef<jobject>& obj,
-                         const base::android::JavaParamRef<jstring>& url);
-
   scoped_refptr<content::DevToolsAgentHost> GetDevToolsAgentHost();
 
   void SetDevToolsAgentHost(scoped_refptr<content::DevToolsAgentHost> host);
@@ -200,8 +190,6 @@
       const base::android::JavaParamRef<jobject>& obj);
 
  private:
-  prerender::PrerenderManager* GetPrerenderManager() const;
-
   JavaObjectWeakGlobalRef weak_java_tab_;
 
   // Identifier of the window the tab is in.
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc
index e249a40..b20c080 100644
--- a/chrome/browser/apps/app_service/arc_apps.cc
+++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -20,8 +20,10 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_icon.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/grit/component_extension_resources.h"
+#include "chrome/services/app_service/public/cpp/intent_filter_util.h"
 #include "components/arc/app_permissions/arc_app_permissions_bridge.h"
 #include "components/arc/arc_service_manager.h"
+#include "components/arc/intent_helper/arc_intent_helper_bridge.h"
 #include "components/arc/mojom/app_permissions.mojom.h"
 #include "components/arc/session/arc_bridge_service.h"
 #include "content/public/browser/system_connector.h"
@@ -383,7 +385,8 @@
 
 void ArcApps::OnPackageModified(
     const arc::mojom::ArcPackageInfo& package_info) {
-  ConvertAndPublishPackageApps(package_info);
+  static constexpr bool update_icon = false;
+  ConvertAndPublishPackageApps(package_info, update_icon);
 }
 
 void ArcApps::OnPackageListInitialRefreshed() {
@@ -433,7 +436,8 @@
 
 apps::mojom::AppPtr ArcApps::Convert(ArcAppListPrefs* prefs,
                                      const std::string& app_id,
-                                     const ArcAppListPrefs::AppInfo& app_info) {
+                                     const ArcAppListPrefs::AppInfo& app_info,
+                                     bool update_icon) {
   apps::mojom::AppPtr app = apps::mojom::App::New();
 
   app->app_type = apps::mojom::AppType::kArc;
@@ -444,11 +448,14 @@
   app->name = app_info.name;
   app->short_name = app->name;
 
-  IconEffects icon_effects = IconEffects::kNone;
-  if (app_info.suspended) {
-    icon_effects = static_cast<IconEffects>(icon_effects | IconEffects::kGray);
+  if (update_icon) {
+    IconEffects icon_effects = IconEffects::kNone;
+    if (app_info.suspended) {
+      icon_effects =
+          static_cast<IconEffects>(icon_effects | IconEffects::kGray);
+    }
+    app->icon_key = icon_key_factory_.MakeIconKey(icon_effects);
   }
-  app->icon_key = icon_key_factory_.MakeIconKey(icon_effects);
 
   app->last_launch_time = app_info.last_launch_time;
   app->install_time = app_info.install_time;
@@ -471,6 +478,13 @@
     UpdateAppPermissions(package->permissions, &app->permissions);
   }
 
+  auto* intent_helper_bridge =
+      arc::ArcIntentHelperBridge::GetForBrowserContext(profile_);
+  if (intent_helper_bridge) {
+    UpdateAppIntentFilters(app_info.package_name, intent_helper_bridge,
+                           &app->intent_filters);
+  }
+
   return app;
 }
 
@@ -483,7 +497,8 @@
 }
 
 void ArcApps::ConvertAndPublishPackageApps(
-    const arc::mojom::ArcPackageInfo& package_info) {
+    const arc::mojom::ArcPackageInfo& package_info,
+    bool update_icon) {
   if (!package_info.permissions.has_value()) {
     return;
   }
@@ -494,10 +509,75 @@
       std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
           prefs->GetApp(app_id);
       if (app_info) {
-        Publish(Convert(prefs, app_id, *app_info));
+        Publish(Convert(prefs, app_id, *app_info, update_icon));
       }
     }
   }
 }
 
+void ArcApps::UpdateAppIntentFilters(
+    std::string package_name,
+    arc::ArcIntentHelperBridge* intent_helper_bridge,
+    std::vector<apps::mojom::IntentFilterPtr>* intent_filters) {
+  // TODO(crbug.com/853604): Add per-package observer to update the intent
+  // filter when package changes. Currently there is only observer for
+  // everything changes, want to try to split up it for each package.
+  const std::vector<arc::IntentFilter>& arc_intent_filters =
+      intent_helper_bridge->GetIntentFilterForPackage(package_name);
+  for (auto& arc_intent_filter : arc_intent_filters) {
+    auto intent_filter = apps::mojom::IntentFilter::New();
+
+    std::vector<apps::mojom::ConditionValuePtr> scheme_condition_values;
+    for (auto& scheme : arc_intent_filter.schemes()) {
+      scheme_condition_values.push_back(apps_util::MakeConditionValue(
+          scheme, apps::mojom::PatternMatchType::kNone));
+    }
+    if (!scheme_condition_values.empty()) {
+      auto scheme_condition =
+          apps_util::MakeCondition(apps::mojom::ConditionType::kScheme,
+                                   std::move(scheme_condition_values));
+      intent_filter->conditions.push_back(std::move(scheme_condition));
+    }
+
+    std::vector<apps::mojom::ConditionValuePtr> host_condition_values;
+    for (auto& authority : arc_intent_filter.authorities()) {
+      host_condition_values.push_back(apps_util::MakeConditionValue(
+          authority.host(), apps::mojom::PatternMatchType::kNone));
+    }
+    if (!host_condition_values.empty()) {
+      auto host_condition = apps_util::MakeCondition(
+          apps::mojom::ConditionType::kHost, std::move(host_condition_values));
+      intent_filter->conditions.push_back(std::move(host_condition));
+    }
+
+    std::vector<apps::mojom::ConditionValuePtr> path_condition_values;
+    for (auto& path : arc_intent_filter.paths()) {
+      apps::mojom::PatternMatchType match_type;
+      switch (path.match_type()) {
+        case arc::mojom::PatternType::PATTERN_LITERAL:
+          match_type = apps::mojom::PatternMatchType::kLiteral;
+          break;
+        case arc::mojom::PatternType::PATTERN_PREFIX:
+          match_type = apps::mojom::PatternMatchType::kPrefix;
+          break;
+        case arc::mojom::PatternType::PATTERN_SIMPLE_GLOB:
+          match_type = apps::mojom::PatternMatchType::kGlob;
+          break;
+        default:
+          NOTREACHED();
+      }
+      path_condition_values.push_back(
+          apps_util::MakeConditionValue(path.pattern(), match_type));
+    }
+    if (!path_condition_values.empty()) {
+      auto path_condition =
+          apps_util::MakeCondition(apps::mojom::ConditionType::kPattern,
+                                   std::move(path_condition_values));
+      intent_filter->conditions.push_back(std::move(path_condition));
+    }
+
+    intent_filters->push_back(std::move(intent_filter));
+  }
+}
+
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/arc_apps.h b/chrome/browser/apps/app_service/arc_apps.h
index 51208e66..da9c70b 100644
--- a/chrome/browser/apps/app_service/arc_apps.h
+++ b/chrome/browser/apps/app_service/arc_apps.h
@@ -23,6 +23,10 @@
 
 class Profile;
 
+namespace arc {
+class ArcIntentHelperBridge;
+}
+
 namespace apps {
 
 class AppServiceProxy;
@@ -91,10 +95,16 @@
 
   apps::mojom::AppPtr Convert(ArcAppListPrefs* prefs,
                               const std::string& app_id,
-                              const ArcAppListPrefs::AppInfo& app_info);
+                              const ArcAppListPrefs::AppInfo& app_info,
+                              bool update_icon = true);
   void Publish(apps::mojom::AppPtr app);
   void ConvertAndPublishPackageApps(
-      const arc::mojom::ArcPackageInfo& package_info);
+      const arc::mojom::ArcPackageInfo& package_info,
+      bool update_icon = true);
+  void UpdateAppIntentFilters(
+      std::string package_name,
+      arc::ArcIntentHelperBridge* intent_helper_bridge,
+      std::vector<apps::mojom::IntentFilterPtr>* intent_filters);
 
   mojo::Receiver<apps::mojom::Publisher> receiver_{this};
   mojo::RemoteSet<apps::mojom::Subscriber> subscribers_;
diff --git a/chrome/browser/background/background_contents.cc b/chrome/browser/background/background_contents.cc
index d57e7c1..bc5bd98 100644
--- a/chrome/browser/background/background_contents.cc
+++ b/chrome/browser/background/background_contents.cc
@@ -55,6 +55,7 @@
   create_params.main_frame_routing_id = main_frame_routing_id;
   create_params.main_frame_widget_routing_id = main_frame_widget_routing_id;
   create_params.renderer_initiated_creation = routing_id != MSG_ROUTING_NONE;
+  create_params.is_never_visible = true;
   if (session_storage_namespace) {
     content::SessionStorageNamespaceMap session_storage_namespace_map;
     session_storage_namespace_map.insert(
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 716d14c0..56d982c 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -393,11 +393,8 @@
 
         <include name="IDR_CROSH_BUILTIN_MANIFEST" file="resources\chromeos\crosh_builtin\manifest.json" type="BINDATA" />
         <include name="IDR_CROSTINI_INSTALLER_INDEX_HTML" file="resources\chromeos\crostini_installer\index.html" type="BINDATA" />
-        <include name="IDR_CROSTINI_INSTALLER_APP_HTML" file="resources\chromeos\crostini_installer\app.html" type="BINDATA" />
-        <include name="IDR_CROSTINI_INSTALLER_APP_JS" file="resources\chromeos\crostini_installer\app.js" type="BINDATA" />
-        <include name="IDR_CROSTINI_INSTALLER_BROWSER_PROXY_HTML" file="resources\chromeos\crostini_installer\browser_proxy.html" type="BINDATA" />
+        <include name="IDR_CROSTINI_INSTALLER_APP_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\crostini_installer\app.js" type="BINDATA" use_base_dir="false" />
         <include name="IDR_CROSTINI_INSTALLER_BROWSER_PROXY_JS" file="resources\chromeos\crostini_installer\browser_proxy.js" type="BINDATA" />
-        <include name="IDR_CROSTINI_INSTALLER_MOJO_HTML" file="${root_gen_dir}\chrome\browser\ui\webui\chromeos\crostini_installer\crostini_installer.mojom.html" use_base_dir="false" type="BINDATA" />
         <include name="IDR_CROSTINI_INSTALLER_MOJO_LITE_JS" file="${root_gen_dir}\chrome\browser\ui\webui\chromeos\crostini_installer\crostini_installer.mojom-lite.js" use_base_dir="false" type="BINDATA" />
         <include name="IDR_CRYPTOHOME_HTML" file="resources\chromeos\cryptohome.html" flattenhtml="true" type="BINDATA" />
         <include name="IDR_CRYPTOHOME_JS" file="resources\chromeos\cryptohome.js" type="BINDATA" />
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 6e6a66c..07757ee 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/chrome_browser_interface_binders.h"
 
 #include "build/build_config.h"
+#include "chrome/browser/navigation_predictor/navigation_predictor.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
@@ -12,6 +13,7 @@
 #include "services/image_annotation/public/mojom/constants.mojom-forward.h"
 #include "services/image_annotation/public/mojom/image_annotation.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
+#include "third_party/blink/public/mojom/loader/navigation_predictor.mojom.h"
 
 #if defined(OS_ANDROID)
 #include "content/public/browser/web_contents.h"
@@ -64,6 +66,8 @@
   map->Add<image_annotation::mojom::Annotator>(
       base::BindRepeating(&BindImageAnnotator));
 
+  map->Add<blink::mojom::AnchorElementMetricsHost>(
+      base::BindRepeating(&NavigationPredictor::Create));
 #if defined(OS_ANDROID)
   map->Add<blink::mojom::InstalledAppProvider>(base::BindRepeating(
       &ForwardToJavaFrameRegistry<blink::mojom::InstalledAppProvider>));
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 50684ed..0bff08b 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -75,7 +75,6 @@
 #include "chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.h"
 #include "chrome/browser/metrics/chrome_feature_list_creator.h"
 #include "chrome/browser/nacl_host/nacl_browser_delegate_impl.h"
-#include "chrome/browser/navigation_predictor/navigation_predictor.h"
 #include "chrome/browser/net/chrome_network_delegate.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/net_benchmarking.h"
@@ -4413,9 +4412,6 @@
   }
 #endif
 
-  frame_interfaces_parameterized_->AddInterface(
-      base::BindRepeating(&NavigationPredictor::Create));
-
 #if defined(OS_ANDROID)
   frame_interfaces_parameterized_->AddInterface(
       base::BindRepeating(&offline_pages::OfflinePageAutoFetcher::Create));
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index db84c79..5aafe5b 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -2649,6 +2649,7 @@
     "login/screens/multidevice_setup_screen_unittest.cc",
     "login/screens/network_screen_unittest.cc",
     "login/screens/recommend_apps/recommend_apps_fetcher_impl_unittest.cc",
+    "login/screens/update_required_screen_unittest.cc",
     "login/screens/update_screen_unittest.cc",
     "login/session/user_session_manager_test.cc",
     "login/signin_partition_manager_unittest.cc",
diff --git a/chrome/browser/chromeos/crostini/crostini_features.cc b/chrome/browser/chromeos/crostini/crostini_features.cc
index c977285..c5fd66f 100644
--- a/chrome/browser/chromeos/crostini/crostini_features.cc
+++ b/chrome/browser/chromeos/crostini/crostini_features.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_features.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "components/prefs/pref_service.h"
 
@@ -44,9 +45,11 @@
 }
 
 bool CrostiniFeatures::IsRootAccessAllowed(Profile* profile) {
-  // TODO(crbug.com/1004708): Move implementation from crostini_util to here and
-  // redirect all callers.
-  return crostini::IsCrostiniRootAccessAllowed(profile);
+  if (base::FeatureList::IsEnabled(features::kCrostiniAdvancedAccessControls)) {
+    return profile->GetPrefs()->GetBoolean(
+        crostini::prefs::kUserCrostiniRootAccessAllowedByPolicy);
+  }
+  return true;
 }
 
 }  // namespace crostini
diff --git a/chrome/browser/chromeos/crostini/crostini_features_unittest.cc b/chrome/browser/chromeos/crostini/crostini_features_unittest.cc
index ef57286..eecd032e 100644
--- a/chrome/browser/chromeos/crostini/crostini_features_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_features_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
 #include "chrome/browser/chromeos/crostini/fake_crostini_features.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "components/prefs/pref_service.h"
@@ -60,4 +61,34 @@
   EXPECT_FALSE(crostini_features.IsExportImportUIAllowed(&profile));
 }
 
+TEST(CrostiniFeaturesTest, TestRootAccessAllowed) {
+  content::BrowserTaskEnvironment task_environment;
+  TestingProfile profile;
+  FakeCrostiniFeatures crostini_features;
+  base::test::ScopedFeatureList scoped_feature_list;
+
+  // Set up for success.
+  crostini_features.set_ui_allowed(true);
+  scoped_feature_list.InitWithFeatures(
+      {features::kCrostiniAdvancedAccessControls}, {});
+  profile.GetPrefs()->SetBoolean(
+      crostini::prefs::kUserCrostiniRootAccessAllowedByPolicy, true);
+
+  // Success.
+  EXPECT_TRUE(crostini_features.IsRootAccessAllowed(&profile));
+
+  // Pref off.
+  profile.GetPrefs()->SetBoolean(
+      crostini::prefs::kUserCrostiniRootAccessAllowedByPolicy, false);
+  EXPECT_FALSE(crostini_features.IsRootAccessAllowed(&profile));
+
+  // Feature disabled.
+  {
+    base::test::ScopedFeatureList feature_list_disabled;
+    feature_list_disabled.InitWithFeatures(
+        {}, {features::kCrostiniAdvancedAccessControls});
+    EXPECT_TRUE(crostini_features.IsRootAccessAllowed(&profile));
+  }
+}
+
 }  // namespace crostini
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index fa66ea9..0ea761b4 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -22,6 +22,7 @@
 #include "base/time/default_clock.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/crostini/ansible/ansible_management_service.h"
+#include "chrome/browser/chromeos/crostini/crostini_features.h"
 #include "chrome/browser/chromeos/crostini/crostini_manager_factory.h"
 #include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
 #include "chrome/browser/chromeos/crostini/crostini_remover.h"
@@ -1372,7 +1373,7 @@
     std::string container_name,
     std::string package_path,
     InstallLinuxPackageCallback callback) {
-  if (!IsCrostiniRootAccessAllowed(profile_)) {
+  if (!CrostiniFeatures::Get()->IsRootAccessAllowed(profile_)) {
     LOG(ERROR) << "Attempted to install package when root access to Crostini "
                   "VM not allowed.";
     std::move(callback).Run(CrostiniResult::INSTALL_LINUX_PACKAGE_FAILED);
diff --git a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
index 57760bf..cff125a 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
+#include "chrome/browser/chromeos/crostini/fake_crostini_features.h"
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/testing_profile.h"
@@ -170,8 +171,7 @@
   ~CrostiniManagerTest() override { chromeos::DBusThreadManager::Shutdown(); }
 
   void SetUp() override {
-    scoped_feature_list_.InitWithFeatures(
-        {features::kCrostini, features::kCrostiniAdvancedAccessControls}, {});
+    scoped_feature_list_.InitWithFeatures({features::kCrostini}, {});
     run_loop_ = std::make_unique<base::RunLoop>();
     profile_ = std::make_unique<TestingProfile>();
     crostini_manager_ = CrostiniManager::GetForProfile(profile_.get());
@@ -348,8 +348,8 @@
 }
 
 TEST_F(CrostiniManagerTest, InstallLinuxPackageRootAccessError) {
-  profile()->GetPrefs()->SetBoolean(
-      crostini::prefs::kUserCrostiniRootAccessAllowedByPolicy, false);
+  FakeCrostiniFeatures crostini_features;
+  crostini_features.set_root_access_allowed(false);
   crostini_manager()->InstallLinuxPackage(
       kVmName, kContainerName, "/tmp/package.deb",
       base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
diff --git a/chrome/browser/chromeos/crostini/crostini_util.cc b/chrome/browser/chromeos/crostini/crostini_util.cc
index a22a3a0..15c3688 100644
--- a/chrome/browser/chromeos/crostini/crostini_util.cc
+++ b/chrome/browser/chromeos/crostini/crostini_util.cc
@@ -358,14 +358,6 @@
   return base::FeatureList::IsEnabled(features::kCrostiniAnsibleInfrastructure);
 }
 
-bool IsCrostiniRootAccessAllowed(Profile* profile) {
-  if (base::FeatureList::IsEnabled(features::kCrostiniAdvancedAccessControls)) {
-    return profile->GetPrefs()->GetBoolean(
-        crostini::prefs::kUserCrostiniRootAccessAllowedByPolicy);
-  }
-  return true;
-}
-
 void LaunchCrostiniApp(Profile* profile,
                        const std::string& app_id,
                        int64_t display_id) {
diff --git a/chrome/browser/chromeos/crostini/crostini_util.h b/chrome/browser/chromeos/crostini/crostini_util.h
index df91648..10b1b86 100644
--- a/chrome/browser/chromeos/crostini/crostini_util.h
+++ b/chrome/browser/chromeos/crostini/crostini_util.h
@@ -67,10 +67,6 @@
 // Crostini container is enabled.
 bool IsCrostiniAnsibleInfrastructureEnabled();
 
-// Returns whether user is allowed root access to Crostini. Always returns true
-// when advanced access controls feature flag is disabled.
-bool IsCrostiniRootAccessAllowed(Profile* profile);
-
 // Launches the Crostini app with ID of |app_id| on the display with ID of
 // |display_id|. |app_id| should be a valid Crostini app list id.
 void LaunchCrostiniApp(Profile* profile,
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index eadcf18..c8eccc0 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -78,6 +78,7 @@
 #include "chromeos/dbus/session_manager/session_manager_client.h"
 #include "chromeos/printing/printer_configuration.h"
 #include "chromeos/services/assistant/public/cpp/assistant_prefs.h"
+#include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
 #include "chromeos/services/machine_learning/public/cpp/service_connection.h"
 #include "components/arc/arc_prefs.h"
 #include "components/arc/metrics/arc_metrics_constants.h"
@@ -1702,13 +1703,128 @@
   Respond(Error("Assistant service timed out"));
 }
 
+// AssistantInteractionHelper is a helper class used to interact with Assistant
+// server and store interaction states for tests. It is shared by
+// |AutotestPrivateSendAssistantTextQueryFunction| and
+// |AutotestPrivateWaitForAssistantQueryStatusFunction|.
+class AssistantInteractionHelper
+    : public chromeos::assistant::mojom::AssistantInteractionSubscriber {
+ public:
+  using OnInteractionFinishedCallback = base::OnceCallback<void(bool)>;
+
+  AssistantInteractionHelper()
+      : query_status_(std::make_unique<base::DictionaryValue>()) {}
+
+  ~AssistantInteractionHelper() override = default;
+
+  void Init(OnInteractionFinishedCallback on_interaction_finished_callback) {
+    // Bind to Assistant service interface.
+    AssistantClient::Get()->BindAssistant(mojo::MakeRequest(&assistant_));
+
+    // Subscribe to Assistant interaction events.
+    assistant_->AddAssistantInteractionSubscriber(
+        assistant_interaction_subscriber_receiver_.BindNewPipeAndPassRemote());
+
+    on_interaction_finished_callback_ =
+        std::move(on_interaction_finished_callback);
+  }
+
+  void SendTextQuery(const std::string& query, bool allow_tts) {
+    // Start text interaction with Assistant server.
+    assistant_->StartTextInteraction(query, allow_tts);
+
+    query_status_->SetKey("queryText", base::Value(query));
+  }
+
+  std::unique_ptr<base::DictionaryValue> GetQueryStatus() {
+    return std::move(query_status_);
+  }
+
+ private:
+  // chromeos::assistant::mojom::AssistantInteractionSubscriber:
+  using AssistantSuggestionPtr =
+      chromeos::assistant::mojom::AssistantSuggestionPtr;
+  using AssistantInteractionMetadataPtr =
+      chromeos::assistant::mojom::AssistantInteractionMetadataPtr;
+  using AssistantInteractionResolution =
+      chromeos::assistant::mojom::AssistantInteractionResolution;
+
+  void OnInteractionStarted(AssistantInteractionMetadataPtr metadata) override {
+    const bool is_voice_interaction =
+        chromeos::assistant::mojom::AssistantInteractionType::kVoice ==
+        metadata->type;
+    query_status_->SetKey("isMicOpen", base::Value(is_voice_interaction));
+  }
+
+  void OnInteractionFinished(
+      AssistantInteractionResolution resolution) override {
+    // Only invoke the callback when |result_| is not empty to avoid an early
+    // return before the entire session is completed. This happens when
+    // sending queries to modify device settings, e.g. "turn on bluetooth",
+    // which results in a round trip due to the need to fetch device state
+    // on the client and return that to the server as part of a follow-up
+    // interaction.
+    if (result_.empty())
+      return;
+
+    query_status_->SetKey("queryResponse", std::move(result_));
+
+    if (on_interaction_finished_callback_) {
+      const bool success =
+          (resolution == AssistantInteractionResolution::kNormal) ? true
+                                                                  : false;
+      std::move(on_interaction_finished_callback_).Run(success);
+    }
+  }
+
+  void OnHtmlResponse(const std::string& response,
+                      const std::string& fallback) override {
+    result_.SetKey("htmlResponse", base::Value(response));
+    result_.SetKey("htmlFallback", base::Value(fallback));
+  }
+
+  void OnTextResponse(const std::string& response) override {
+    result_.SetKey("text", base::Value(response));
+  }
+
+  void OnSpeechRecognitionFinalResult(
+      const std::string& final_result) override {
+    query_status_->SetKey("queryText", base::Value(final_result));
+  }
+
+  void OnSuggestionsResponse(
+      std::vector<AssistantSuggestionPtr> response) override {}
+  void OnOpenUrlResponse(const GURL& url, bool in_background) override {}
+  void OnOpenAppResponse(chromeos::assistant::mojom::AndroidAppInfoPtr app_info,
+                         OnOpenAppResponseCallback callback) override {}
+  void OnSpeechRecognitionStarted() override {}
+  void OnSpeechRecognitionIntermediateResult(
+      const std::string& high_confidence_text,
+      const std::string& low_confidence_text) override {}
+  void OnSpeechRecognitionEndOfUtterance() override {}
+  void OnSpeechLevelUpdated(float speech_level) override {}
+  void OnTtsStarted(bool due_to_error) override {}
+  void OnWaitStarted() override {}
+
+  chromeos::assistant::mojom::AssistantPtr assistant_;
+  mojo::Receiver<chromeos::assistant::mojom::AssistantInteractionSubscriber>
+      assistant_interaction_subscriber_receiver_{this};
+  std::unique_ptr<base::DictionaryValue> query_status_;
+  base::DictionaryValue result_;
+
+  // Callback triggered when interaction finished with non-empty response.
+  OnInteractionFinishedCallback on_interaction_finished_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(AssistantInteractionHelper);
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 // AutotestPrivateSendAssistantTextQueryFunction
 ///////////////////////////////////////////////////////////////////////////////
 
 AutotestPrivateSendAssistantTextQueryFunction::
     AutotestPrivateSendAssistantTextQueryFunction()
-    : result_(std::make_unique<base::DictionaryValue>()) {}
+    : interaction_helper_(std::make_unique<AssistantInteractionHelper>()) {}
 
 AutotestPrivateSendAssistantTextQueryFunction::
     ~AutotestPrivateSendAssistantTextQueryFunction() = default;
@@ -1729,15 +1845,13 @@
         "Assistant not allowed - state: %d", allowed_state)));
   }
 
-  // Bind to Assistant service interface.
-  AssistantClient::Get()->BindAssistant(mojo::MakeRequest(&assistant_));
-
-  // Subscribe to Assistant interaction events.
-  assistant_->AddAssistantInteractionSubscriber(
-      assistant_interaction_subscriber_receiver_.BindNewPipeAndPassRemote());
+  interaction_helper_->Init(
+      base::BindOnce(&AutotestPrivateSendAssistantTextQueryFunction::
+                         OnInteractionFinishedCallback,
+                     this));
 
   // Start text interaction with Assistant server.
-  assistant_->StartTextInteraction(params->query, /*allow_tts*/ false);
+  interaction_helper_->SendTextQuery(params->query, /*allow_tts=*/false);
 
   // Set up a delayed timer to wait for the query response and hold a reference
   // to |this| to avoid being destructed. Also make sure we stop and respond
@@ -1750,34 +1864,15 @@
   return RespondLater();
 }
 
-void AutotestPrivateSendAssistantTextQueryFunction::OnTextResponse(
-    const std::string& response) {
-  result_->SetKey("text", base::Value(response));
-}
-
-void AutotestPrivateSendAssistantTextQueryFunction::OnHtmlResponse(
-    const std::string& response,
-    const std::string& fallback) {
-  result_->SetKey("htmlResponse", base::Value(response));
-  result_->SetKey("htmlFallback", base::Value(fallback));
-}
-
-void AutotestPrivateSendAssistantTextQueryFunction::OnInteractionFinished(
-    AssistantInteractionResolution resolution) {
-  // Only return a result to the caller and stop the timer when |result_|
-  // is not empty to avoid an early return before the entire interaction is
-  // completed. This happens when sending queries to modify device settings,
-  // e.g. "turn on bluetooth", which results in two rounds of interaction.
-  if (result_->empty())
-    return;
-
-  if (resolution != AssistantInteractionResolution::kNormal) {
+void AutotestPrivateSendAssistantTextQueryFunction::
+    OnInteractionFinishedCallback(bool success) {
+  if (!success) {
     Respond(Error("Interaction ends abnormally."));
     timeout_timer_.AbandonAndStop();
     return;
   }
 
-  Respond(OneArgument(std::move(result_)));
+  Respond(OneArgument(interaction_helper_->GetQueryStatus()));
   timeout_timer_.AbandonAndStop();
 }
 
@@ -1786,6 +1881,62 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateWaitForAssistantQueryStatusFunction
+///////////////////////////////////////////////////////////////////////////////
+AutotestPrivateWaitForAssistantQueryStatusFunction::
+    AutotestPrivateWaitForAssistantQueryStatusFunction()
+    : interaction_helper_(std::make_unique<AssistantInteractionHelper>()) {}
+
+AutotestPrivateWaitForAssistantQueryStatusFunction::
+    ~AutotestPrivateWaitForAssistantQueryStatusFunction() = default;
+
+ExtensionFunction::ResponseAction
+AutotestPrivateWaitForAssistantQueryStatusFunction::Run() {
+  DVLOG(1) << "AutotestPrivateWaitForAssistantQueryStatusFunction";
+
+  std::unique_ptr<api::autotest_private::WaitForAssistantQueryStatus::Params>
+      params(api::autotest_private::WaitForAssistantQueryStatus::Params::Create(
+          *args_));
+  EXTENSION_FUNCTION_VALIDATE(params);
+
+  Profile* profile = Profile::FromBrowserContext(browser_context());
+  ash::mojom::AssistantAllowedState allowed_state =
+      assistant::IsAssistantAllowedForProfile(profile);
+  if (allowed_state != ash::mojom::AssistantAllowedState::ALLOWED) {
+    return RespondNow(Error(base::StringPrintf(
+        "Assistant not allowed - state: %d", allowed_state)));
+  }
+
+  interaction_helper_->Init(
+      base::BindOnce(&AutotestPrivateWaitForAssistantQueryStatusFunction::
+                         OnInteractionFinishedCallback,
+                     this));
+
+  // Start waiting for the response before time out.
+  timeout_timer_.Start(
+      FROM_HERE, base::TimeDelta::FromSeconds(params->timeout_s),
+      base::BindOnce(
+          &AutotestPrivateWaitForAssistantQueryStatusFunction::Timeout, this));
+  return RespondLater();
+}
+
+void AutotestPrivateWaitForAssistantQueryStatusFunction::
+    OnInteractionFinishedCallback(bool success) {
+  if (!success) {
+    Respond(Error("Interaction ends abnormally."));
+    timeout_timer_.AbandonAndStop();
+    return;
+  }
+
+  Respond(OneArgument(interaction_helper_->GetQueryStatus()));
+  timeout_timer_.AbandonAndStop();
+}
+
+void AutotestPrivateWaitForAssistantQueryStatusFunction::Timeout() {
+  Respond(Error("No query response received before time out."));
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // AutotestPrivateSetWhitelistedPrefFunction
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
index 3607188..429094c 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
@@ -15,7 +15,6 @@
 #include "base/timer/timer.h"
 #include "chrome/browser/chromeos/printing/cups_printers_manager.h"
 #include "chrome/browser/extensions/chrome_extension_function.h"
-#include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
 #include "chromeos/services/machine_learning/public/mojom/machine_learning_service.mojom.h"
 #include "chromeos/services/machine_learning/public/mojom/model.mojom.h"
 #include "extensions/browser/browser_context_keyed_api_factory.h"
@@ -28,6 +27,8 @@
 
 namespace extensions {
 
+class AssistantInteractionHelper;
+
 class AutotestPrivateLogoutFunction : public ExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("autotestPrivate.logout", AUTOTESTPRIVATE_LOGOUT)
@@ -509,9 +510,7 @@
 };
 
 // Send text query to Assistant and return response.
-class AutotestPrivateSendAssistantTextQueryFunction
-    : public ExtensionFunction,
-      public chromeos::assistant::mojom::AssistantInteractionSubscriber {
+class AutotestPrivateSendAssistantTextQueryFunction : public ExtensionFunction {
  public:
   AutotestPrivateSendAssistantTextQueryFunction();
   DECLARE_EXTENSION_FUNCTION("autotestPrivate.sendAssistantTextQuery",
@@ -521,46 +520,39 @@
   ~AutotestPrivateSendAssistantTextQueryFunction() override;
   ResponseAction Run() override;
 
-  using AssistantInteractionMetadataPtr =
-      chromeos::assistant::mojom::AssistantInteractionMetadataPtr;
-  using AssistantInteractionResolution =
-      chromeos::assistant::mojom::AssistantInteractionResolution;
-  using AssistantSuggestionPtr =
-      chromeos::assistant::mojom::AssistantSuggestionPtr;
-
-  // chromeos::assistant::mojom::AssistantInteractionSubscriber:
-  void OnHtmlResponse(const std::string& response,
-                      const std::string& fallback) override;
-  void OnTextResponse(const std::string& response) override;
-  void OnInteractionFinished(
-      AssistantInteractionResolution resolution) override;
-  void OnInteractionStarted(AssistantInteractionMetadataPtr metadata) override {
-  }
-  void OnSuggestionsResponse(
-      std::vector<AssistantSuggestionPtr> response) override {}
-  void OnOpenUrlResponse(const GURL& url, bool in_background) override {}
-  void OnOpenAppResponse(chromeos::assistant::mojom::AndroidAppInfoPtr app_info,
-                         OnOpenAppResponseCallback callback) override {}
-  void OnSpeechRecognitionStarted() override {}
-  void OnSpeechRecognitionIntermediateResult(
-      const std::string& high_confidence_text,
-      const std::string& low_confidence_text) override {}
-  void OnSpeechRecognitionEndOfUtterance() override {}
-  void OnSpeechRecognitionFinalResult(
-      const std::string& final_result) override {}
-  void OnSpeechLevelUpdated(float speech_level) override {}
-  void OnTtsStarted(bool due_to_error) override {}
-  void OnWaitStarted() override {}
+  // Called when the interaction finished with non-empty response.
+  void OnInteractionFinishedCallback(bool success);
 
   // Called when Assistant service fails to respond in a certain amount of
   // time. We will respond with an error.
   void Timeout();
 
-  chromeos::assistant::mojom::AssistantPtr assistant_;
-  mojo::Receiver<chromeos::assistant::mojom::AssistantInteractionSubscriber>
-      assistant_interaction_subscriber_receiver_{this};
+  std::unique_ptr<AssistantInteractionHelper> interaction_helper_;
   base::OneShotTimer timeout_timer_;
-  std::unique_ptr<base::DictionaryValue> result_;
+};
+
+// Wait for the next text/voice query interaction completed and respond with
+// the query status if any valid response was caught before time out.
+class AutotestPrivateWaitForAssistantQueryStatusFunction
+    : public ExtensionFunction {
+ public:
+  AutotestPrivateWaitForAssistantQueryStatusFunction();
+  DECLARE_EXTENSION_FUNCTION("autotestPrivate.waitForAssistantQueryStatus",
+                             AUTOTESTPRIVATE_WAITFORASSISTANTQUERYSTATUS)
+
+ private:
+  ~AutotestPrivateWaitForAssistantQueryStatusFunction() override;
+  ResponseAction Run() override;
+
+  // Called when the current interaction finished with non-empty response.
+  void OnInteractionFinishedCallback(bool success);
+
+  // Called when Assistant service fails to respond in a certain amount of
+  // time. We will respond with an error.
+  void Timeout();
+
+  std::unique_ptr<AssistantInteractionHelper> interaction_helper_;
+  base::OneShotTimer timeout_timer_;
 };
 
 // Set user pref value in the pref tree.
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
index d9d4b85..4201d56 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
@@ -606,22 +606,6 @@
   return RespondLater();
 }
 
-void FileManagerPrivateGetSizeStatsFunction::OnGetDriveAvailableSpace(
-    drive::FileError error,
-    int64_t bytes_total,
-    int64_t bytes_used) {
-  if (error == drive::FILE_ERROR_OK) {
-    const uint64_t bytes_total_unsigned = bytes_total;
-    // bytes_used can be larger than bytes_total (over quota).
-    const uint64_t bytes_remaining_unsigned =
-        std::max(bytes_total - bytes_used, int64_t(0));
-    OnGetSizeStats(&bytes_total_unsigned, &bytes_remaining_unsigned);
-  } else {
-    // If stats couldn't be gotten for drive, result should be left undefined.
-    Respond(NoArguments());
-  }
-}
-
 void FileManagerPrivateGetSizeStatsFunction::OnGetMtpAvailableSpace(
     device::mojom::MtpStorageInfoPtr mtp_storage_info,
     const bool error) {
@@ -883,18 +867,7 @@
     RunAfterFreeDiskSpace(true);
     return;
   }
-  // Also we can try to secure needed space by freeing Drive caches.
-  drive::FileSystemInterface* const drive_file_system =
-      drive::util::GetFileSystemByProfile(chrome_details_.GetProfile());
-  if (!drive_file_system) {
-    RunAfterFreeDiskSpace(false);
-    return;
-  }
-  drive_file_system->FreeDiskSpaceIfNeededFor(
-      space_needed,
-      base::Bind(
-          &FileManagerPrivateInternalStartCopyFunction::RunAfterFreeDiskSpace,
-          this));
+  RunAfterFreeDiskSpace(false);
 }
 
 void FileManagerPrivateInternalStartCopyFunction::RunAfterFreeDiskSpace(
@@ -1099,31 +1072,20 @@
   std::set<std::string> hashes(params->hash_list.begin(),
                                params->hash_list.end());
 
-  drive::FileSystemInterface* const file_system =
-      drive::util::GetFileSystemByProfile(chrome_details_.GetProfile());
-  if (file_system) {
-    file_system->SearchByHashes(
-        hashes,
-        base::BindOnce(
-            &FileManagerPrivateSearchFilesByHashesFunction::OnSearchByHashes,
-            this, hashes));
-  } else {
-    // |file_system| is NULL if the backend is DriveFs. It doesn't provide
-    // dedicated backup solution yet, so for now just walk the files and check
-    // MD5 extended attribute.
-    base::PostTaskAndReplyWithResult(
-        FROM_HERE,
-        {base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT},
-        base::BindOnce(
-            &FileManagerPrivateSearchFilesByHashesFunction::SearchByAttribute,
-            this, hashes,
-            integration_service->GetMountPointPath().Append(
-                drive::util::kDriveMyDriveRootDirName),
-            base::FilePath("/").Append(drive::util::kDriveMyDriveRootDirName)),
-        base::BindOnce(
-            &FileManagerPrivateSearchFilesByHashesFunction::OnSearchByAttribute,
-            this, hashes));
-  }
+  // DriveFs doesn't provide dedicated backup solution yet, so for now just walk
+  // the files and check MD5 extended attribute.
+  base::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      {base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+      base::BindOnce(
+          &FileManagerPrivateSearchFilesByHashesFunction::SearchByAttribute,
+          this, hashes,
+          integration_service->GetMountPointPath().Append(
+              drive::util::kDriveMyDriveRootDirName),
+          base::FilePath("/").Append(drive::util::kDriveMyDriveRootDirName)),
+      base::BindOnce(
+          &FileManagerPrivateSearchFilesByHashesFunction::OnSearchByAttribute,
+          this, hashes));
 
   return RespondLater();
 }
@@ -1253,64 +1215,6 @@
   Respond(OneArgument(std::move(result)));
 }
 
-FileManagerPrivateInternalSetEntryTagFunction::
-    FileManagerPrivateInternalSetEntryTagFunction()
-    : chrome_details_(this) {}
-
-ExtensionFunction::ResponseAction
-FileManagerPrivateInternalSetEntryTagFunction::Run() {
-  using extensions::api::file_manager_private_internal::SetEntryTag::Params;
-  const std::unique_ptr<Params> params(Params::Create(*args_));
-  EXTENSION_FUNCTION_VALIDATE(params);
-
-  scoped_refptr<storage::FileSystemContext> file_system_context =
-      file_manager::util::GetFileSystemContextForRenderFrameHost(
-          Profile::FromBrowserContext(browser_context()), render_frame_host());
-  const storage::FileSystemURL file_system_url(
-      file_system_context->CrackURL(GURL(params->url)));
-  if (file_system_url.type() == storage::kFileSystemTypeDriveFs) {
-    return RespondNow(NoArguments());
-  }
-
-  const base::FilePath drive_path =
-      drive::util::ExtractDrivePath(file_system_url.path());
-  if (drive_path.empty())
-    return RespondNow(Error("Only Drive files and directories are supported."));
-
-  drive::FileSystemInterface* const file_system =
-      drive::util::GetFileSystemByProfile(chrome_details_.GetProfile());
-  // |file_system| is NULL if Drive is disabled.
-  if (!file_system)
-    return RespondNow(Error("Drive is disabled."));
-
-  google_apis::drive::Property::Visibility visibility;
-  switch (params->visibility) {
-    case extensions::api::file_manager_private::ENTRY_TAG_VISIBILITY_PRIVATE:
-      visibility = google_apis::drive::Property::VISIBILITY_PRIVATE;
-      break;
-    case extensions::api::file_manager_private::ENTRY_TAG_VISIBILITY_PUBLIC:
-      visibility = google_apis::drive::Property::VISIBILITY_PUBLIC;
-      break;
-    default:
-      NOTREACHED();
-      return RespondNow(Error("Invalid visibility."));
-      break;
-  }
-
-  file_system->SetProperty(
-      drive_path, visibility, params->key, params->value,
-      base::Bind(&FileManagerPrivateInternalSetEntryTagFunction::
-                     OnSetEntryPropertyCompleted,
-                 this));
-  return RespondLater();
-}
-
-void FileManagerPrivateInternalSetEntryTagFunction::OnSetEntryPropertyCompleted(
-    drive::FileError result) {
-  Respond(result == drive::FILE_ERROR_OK ? NoArguments()
-                                         : Error("Failed to set a tag."));
-}
-
 ExtensionFunction::ResponseAction
 FileManagerPrivateInternalGetDirectorySizeFunction::Run() {
   using extensions::api::file_manager_private_internal::GetDirectorySize::
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h
index 69f82140..2d709fc 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h
@@ -328,7 +328,6 @@
 };
 
 // Implements the chrome.fileManagerPrivate.searchFilesByHashes method.
-// TODO(b/883628): Write some tests maybe?
 class FileManagerPrivateSearchFilesByHashesFunction
     : public LoggedExtensionFunction {
  public:
@@ -380,26 +379,6 @@
   const ChromeExtensionFunctionDetails chrome_details_;
 };
 
-// Implements the chrome.fileManagerPrivate.setEntryTag method.
-class FileManagerPrivateInternalSetEntryTagFunction
-    : public LoggedExtensionFunction {
- public:
-  FileManagerPrivateInternalSetEntryTagFunction();
-  DECLARE_EXTENSION_FUNCTION("fileManagerPrivateInternal.setEntryTag",
-                             FILEMANAGERPRIVATEINTERNAL_SETENTRYTAG)
- protected:
-  ~FileManagerPrivateInternalSetEntryTagFunction() override = default;
-
- private:
-  const ChromeExtensionFunctionDetails chrome_details_;
-
-  // Called when setting a tag is completed with either a success or an error.
-  void OnSetEntryPropertyCompleted(drive::FileError result);
-
-  ExtensionFunction::ResponseAction Run() override;
-  DISALLOW_COPY_AND_ASSIGN(FileManagerPrivateInternalSetEntryTagFunction);
-};
-
 // Implements the chrome.fileManagerPrivate.getDirectorySize method.
 class FileManagerPrivateInternalGetDirectorySizeFunction
     : public LoggedExtensionFunction {
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index 414caae..533c0df 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -1521,9 +1521,6 @@
   std::vector<base::Feature> enabled_features;
   std::vector<base::Feature> disabled_features;
 
-  enabled_features.emplace_back(features::kCrostiniAdvancedAccessControls);
-  enabled_features.emplace_back(chromeos::features::kCrostiniBackup);
-
   if (!IsGuestModeTest()) {
     enabled_features.emplace_back(features::kCrostini);
   }
@@ -1615,10 +1612,8 @@
     // for testing without such tight coupling.
     crostini_volume_ = std::make_unique<CrostiniTestVolume>();
     profile()->GetPrefs()->SetBoolean(crostini::prefs::kCrostiniEnabled, true);
-    profile()->GetPrefs()->SetBoolean(
-        crostini::prefs::kUserCrostiniRootAccessAllowedByPolicy, true);
-    profile()->GetPrefs()->SetBoolean(
-        crostini::prefs::kUserCrostiniExportImportUIAllowedByPolicy, true);
+    crostini_features_.set_root_access_allowed(true);
+    crostini_features_.set_export_import_ui_allowed(true);
     crostini::CrostiniManager* crostini_manager =
         crostini::CrostiniManager::GetForProfile(
             profile()->GetOriginalProfile());
@@ -2102,16 +2097,14 @@
   if (name == "setCrostiniRootAccessAllowed") {
     bool enabled;
     ASSERT_TRUE(value.GetBoolean("enabled", &enabled));
-    profile()->GetPrefs()->SetBoolean(
-        crostini::prefs::kUserCrostiniRootAccessAllowedByPolicy, enabled);
+    crostini_features_.set_root_access_allowed(enabled);
     return;
   }
 
   if (name == "setCrostiniExportImportAllowed") {
     bool enabled;
     ASSERT_TRUE(value.GetBoolean("enabled", &enabled));
-    profile()->GetPrefs()->SetBoolean(
-        crostini::prefs::kUserCrostiniExportImportUIAllowedByPolicy, enabled);
+    crostini_features_.set_export_import_ui_allowed(enabled);
     return;
   }
 
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
index 36632a0..5b33288 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
@@ -14,6 +14,7 @@
 #include "base/test/metrics/user_action_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/values.h"
+#include "chrome/browser/chromeos/crostini/fake_crostini_features.h"
 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/profiles/profile.h"
@@ -146,6 +147,7 @@
   bool IsSmbEnabled() const;
 
   base::test::ScopedFeatureList feature_list_;
+  crostini::FakeCrostiniFeatures crostini_features_;
 
   std::unique_ptr<DownloadsTestVolume> local_volume_;
   std::unique_ptr<CrostiniTestVolume> crostini_volume_;
diff --git a/chrome/browser/chromeos/login/screens/update_required_screen.cc b/chrome/browser/chromeos/login/screens/update_required_screen.cc
index 945e85e..47e6a0a 100644
--- a/chrome/browser/chromeos/login/screens/update_required_screen.cc
+++ b/chrome/browser/chromeos/login/screens/update_required_screen.cc
@@ -5,19 +5,50 @@
 #include "chrome/browser/chromeos/login/screens/update_required_screen.h"
 
 #include <algorithm>
+#include <utility>
 
+#include "ash/public/cpp/login_screen.h"
+#include "ash/public/cpp/system_tray.h"
 #include "base/bind.h"
+#include "chrome/browser/chromeos/login/error_screens_histogram_helper.h"
+#include "chrome/browser/chromeos/login/helper.h"
+#include "chrome/browser/chromeos/login/ui/login_display.h"
+#include "chrome/browser/chromeos/login/ui/login_display_host.h"
+#include "chrome/browser/chromeos/login/wizard_controller.h"
 #include "chrome/browser/ui/webui/chromeos/login/update_required_screen_handler.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/network_state_handler.h"
+
+namespace {
+constexpr char kUserActionSelectNetworkButtonClicked[] = "select-network";
+constexpr char kUserActionUpdateButtonClicked[] = "update";
+constexpr char kUserActionAcceptUpdateOverCellular[] = "update-accept-cellular";
+constexpr char kUserActionRejectUpdateOverCellular[] = "update-reject-cellular";
+
+// Delay before showing error message if captive portal is detected.
+// We wait for this delay to let captive portal to perform redirect and show
+// its login page before error message appears.
+constexpr const base::TimeDelta kDelayErrorMessage =
+    base::TimeDelta::FromSeconds(10);
+}  // namespace
 
 namespace chromeos {
 
-UpdateRequiredScreen::UpdateRequiredScreen(UpdateRequiredView* view)
-    : BaseScreen(UpdateRequiredView::kScreenId), view_(view) {
+UpdateRequiredScreen::UpdateRequiredScreen(UpdateRequiredView* view,
+                                           ErrorScreen* error_screen)
+    : BaseScreen(UpdateRequiredView::kScreenId),
+      view_(view),
+      error_screen_(error_screen),
+      histogram_helper_(
+          std::make_unique<ErrorScreensHistogramHelper>("UpdateRequired")),
+      version_updater_(std::make_unique<VersionUpdater>(this)),
+      network_state_helper_(std::make_unique<login::NetworkStateHelper>()) {
   if (view_)
     view_->Bind(this);
 }
 
 UpdateRequiredScreen::~UpdateRequiredScreen() {
+  UnsubscribeNetworkNotification();
   if (view_)
     view_->Unbind();
 }
@@ -28,16 +59,251 @@
 }
 
 void UpdateRequiredScreen::Show() {
+  ash::LoginScreen::Get()->SetAllowLoginAsGuest(false);
+  RefreshNetworkState();
+  SubscribeNetworkNotification();
+
   is_shown_ = true;
 
-  if (view_)
-    view_->Show();
+  if (first_time_shown_) {
+    first_time_shown_ = false;
+    if (view_) {
+      view_->SetUIState(UpdateRequiredView::UPDATE_REQUIRED_MESSAGE);
+      view_->Show();
+    }
+
+    version_updater_->GetEolStatus(
+        base::BindOnce(&UpdateRequiredScreen::OnGetEndOfLifeStatus,
+                       weak_factory_.GetWeakPtr()));
+  }
 }
 
 void UpdateRequiredScreen::Hide() {
   if (view_)
     view_->Hide();
   is_shown_ = false;
+  UnsubscribeNetworkNotification();
+}
+
+void UpdateRequiredScreen::OnUserAction(const std::string& action_id) {
+  if (action_id == kUserActionSelectNetworkButtonClicked) {
+    OnSelectNetworkButtonClicked();
+  } else if (action_id == kUserActionUpdateButtonClicked) {
+    OnUpdateButtonClicked();
+  } else if (action_id == kUserActionAcceptUpdateOverCellular) {
+    version_updater_->SetUpdateOverCellularOneTimePermission();
+  } else if (action_id == kUserActionRejectUpdateOverCellular) {
+    version_updater_->RejectUpdateOverCellular();
+    version_updater_->StartExitUpdate(VersionUpdater::Result::UPDATE_ERROR);
+  } else {
+    BaseScreen::OnUserAction(action_id);
+  }
+}
+
+void UpdateRequiredScreen::NetworkConnectionStateChanged(
+    const NetworkState* network) {
+  RefreshNetworkState();
+}
+
+void UpdateRequiredScreen::DefaultNetworkChanged(const NetworkState* network) {
+  RefreshNetworkState();
+}
+
+void UpdateRequiredScreen::RefreshNetworkState() {
+  if (!view_)
+    return;
+
+  view_->SetIsConnected(network_state_helper_->IsConnected());
+}
+
+void UpdateRequiredScreen::RefreshView(
+    const VersionUpdater::UpdateInfo& update_info) {
+  if (!view_)
+    return;
+
+  if (update_info.requires_permission_for_cellular) {
+    view_->SetUIState(UpdateRequiredView::UPDATE_NEED_PERMISSION);
+    waiting_for_permission_ = true;
+  } else if (waiting_for_permission_) {
+    // Return UI state after getting permission.
+    view_->SetUIState(UpdateRequiredView::UPDATE_PROCESS);
+    waiting_for_permission_ = false;
+  }
+
+  view_->SetUpdateProgressUnavailable(update_info.progress_unavailable);
+  view_->SetUpdateProgressValue(update_info.progress);
+  view_->SetUpdateProgressMessage(update_info.progress_message);
+  view_->SetEstimatedTimeLeftVisible(update_info.show_estimated_time_left);
+  view_->SetEstimatedTimeLeft(update_info.estimated_time_left_in_secs);
+}
+
+void UpdateRequiredScreen::SubscribeNetworkNotification() {
+  if (!is_network_subscribed_) {
+    is_network_subscribed_ = true;
+    NetworkHandler::Get()->network_state_handler()->AddObserver(this,
+                                                                FROM_HERE);
+  }
+}
+
+void UpdateRequiredScreen::UnsubscribeNetworkNotification() {
+  if (is_network_subscribed_) {
+    is_network_subscribed_ = false;
+    NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
+                                                                   FROM_HERE);
+  }
+}
+
+void UpdateRequiredScreen::OnSelectNetworkButtonClicked() {
+  ash::SystemTray::Get()->ShowNetworkDetailedViewBubble(
+      true /* show_by_click */);
+}
+
+void UpdateRequiredScreen::OnUpdateButtonClicked() {
+  if (is_updating_now_)
+    return;
+  is_updating_now_ = true;
+  if (view_)
+    view_->SetUIState(UpdateRequiredView::UPDATE_PROCESS);
+
+  version_updater_->StartNetworkCheck();
+}
+
+void UpdateRequiredScreen::OnWaitForRebootTimeElapsed() {
+  EnsureScreenIsShown();
+  if (view_)
+    view_->SetUIState(UpdateRequiredView::UPDATE_COMPLETED_NEED_REBOOT);
+}
+
+void UpdateRequiredScreen::PrepareForUpdateCheck() {
+  error_message_timer_.Stop();
+  error_screen_->HideCaptivePortal();
+
+  connect_request_subscription_.reset();
+  if (version_updater_->update_info().state ==
+      VersionUpdater::State::STATE_ERROR)
+    HideErrorMessage();
+}
+
+void UpdateRequiredScreen::ShowErrorMessage() {
+  error_message_timer_.Stop();
+
+  is_shown_ = false;
+
+  connect_request_subscription_ = error_screen_->RegisterConnectRequestCallback(
+      base::BindRepeating(&UpdateRequiredScreen::OnConnectRequested,
+                          weak_factory_.GetWeakPtr()));
+  error_screen_->SetUIState(NetworkError::UI_STATE_UPDATE);
+  error_screen_->SetParentScreen(UpdateRequiredView::kScreenId);
+  error_screen_->SetHideCallback(base::BindRepeating(
+      &UpdateRequiredScreen::OnErrorScreenHidden, weak_factory_.GetWeakPtr()));
+  error_screen_->SetIsPersistentError(true /* is_persistent */);
+  error_screen_->Show();
+  histogram_helper_->OnErrorShow(error_screen_->GetErrorState());
+}
+
+void UpdateRequiredScreen::UpdateErrorMessage(
+    const NetworkPortalDetector::CaptivePortalStatus status,
+    const NetworkError::ErrorState& error_state,
+    const std::string& network_name) {
+  error_screen_->SetErrorState(error_state, network_name);
+  if (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL) {
+    if (is_first_portal_notification_) {
+      is_first_portal_notification_ = false;
+      error_screen_->FixCaptivePortal();
+    }
+  }
+}
+
+void UpdateRequiredScreen::DelayErrorMessage() {
+  if (error_message_timer_.IsRunning())
+    return;
+
+  error_message_timer_.Start(FROM_HERE, kDelayErrorMessage, this,
+                             &UpdateRequiredScreen::ShowErrorMessage);
+}
+
+void UpdateRequiredScreen::UpdateInfoChanged(
+    const VersionUpdater::UpdateInfo& update_info) {
+  switch (update_info.status.current_operation()) {
+    case update_engine::Operation::CHECKING_FOR_UPDATE:
+    case update_engine::Operation::ATTEMPTING_ROLLBACK:
+    case update_engine::Operation::DISABLED:
+    case update_engine::Operation::IDLE:
+      break;
+    case update_engine::Operation::UPDATE_AVAILABLE:
+    case update_engine::Operation::DOWNLOADING:
+    case update_engine::Operation::VERIFYING:
+    case update_engine::Operation::FINALIZING:
+    case update_engine::Operation::NEED_PERMISSION_TO_UPDATE:
+      EnsureScreenIsShown();
+      break;
+    case update_engine::Operation::UPDATED_NEED_REBOOT:
+      EnsureScreenIsShown();
+      waiting_for_reboot_ = true;
+      version_updater_->RebootAfterUpdate();
+      break;
+    case update_engine::Operation::ERROR:
+    case update_engine::Operation::REPORTING_ERROR_EVENT:
+      version_updater_->StartExitUpdate(VersionUpdater::Result::UPDATE_ERROR);
+      break;
+    default:
+      NOTREACHED();
+  }
+  RefreshView(update_info);
+}
+
+void UpdateRequiredScreen::FinishExitUpdate(VersionUpdater::Result result) {
+  if (waiting_for_reboot_)
+    return;
+
+  is_updating_now_ = false;
+  if (view_)
+    view_->SetUIState(UpdateRequiredView::UPDATE_ERROR);
+}
+
+VersionUpdater* UpdateRequiredScreen::GetVersionUpdaterForTesting() {
+  return version_updater_.get();
+}
+
+void UpdateRequiredScreen::EnsureScreenIsShown() {
+  if (is_shown_ || !view_)
+    return;
+
+  is_shown_ = true;
+  histogram_helper_->OnScreenShow();
+
+  view_->Show();
+}
+
+void UpdateRequiredScreen::HideErrorMessage() {
+  error_screen_->Hide();
+  if (view_)
+    view_->Show();
+  histogram_helper_->OnErrorHide();
+}
+
+void UpdateRequiredScreen::OnGetEndOfLifeStatus(
+    update_engine::EndOfLifeStatus status) {
+  if (status == update_engine::EndOfLifeStatus::kEol) {
+    EnsureScreenIsShown();
+    if (view_)
+      view_->SetUIState(UpdateRequiredView::EOL);
+  }
+}
+
+void UpdateRequiredScreen::OnConnectRequested() {
+  if (version_updater_->update_info().state ==
+      VersionUpdater::State::STATE_ERROR) {
+    LOG(WARNING) << "Hiding error message since AP was reselected";
+    version_updater_->StartUpdateCheck();
+  }
+}
+
+void UpdateRequiredScreen::OnErrorScreenHidden() {
+  error_screen_->SetParentScreen(OobeScreen::SCREEN_UNKNOWN);
+  // Return to the default state.
+  error_screen_->SetIsPersistentError(false /* is_persistent */);
+  Show();
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/screens/update_required_screen.h b/chrome/browser/chromeos/login/screens/update_required_screen.h
index 3ee8420..c2ca1681 100644
--- a/chrome/browser/chromeos/login/screens/update_required_screen.h
+++ b/chrome/browser/chromeos/login/screens/update_required_screen.h
@@ -5,7 +5,9 @@
 #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_UPDATE_REQUIRED_SCREEN_H_
 #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_UPDATE_REQUIRED_SCREEN_H_
 
+#include <memory>
 #include <set>
+#include <string>
 
 #include "base/callback.h"
 #include "base/compiler_specific.h"
@@ -13,28 +15,107 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/chromeos/login/screens/base_screen.h"
 #include "chrome/browser/chromeos/login/screens/error_screen.h"
+#include "chrome/browser/chromeos/login/version_updater/version_updater.h"
 
 namespace chromeos {
 
+class ErrorScreensHistogramHelper;
 class UpdateRequiredView;
 
+namespace login {
+class NetworkStateHelper;
+}  // namespace login
+
 // Controller for the update required screen.
-class UpdateRequiredScreen : public BaseScreen {
+class UpdateRequiredScreen : public BaseScreen,
+                             public VersionUpdater::Delegate,
+                             public NetworkStateHandlerObserver {
  public:
-  explicit UpdateRequiredScreen(UpdateRequiredView* view);
+  explicit UpdateRequiredScreen(UpdateRequiredView* view,
+                                ErrorScreen* error_screen);
   ~UpdateRequiredScreen() override;
 
   // Called when the being destroyed. This should call Unbind() on the
   // associated View if this class is destroyed before it.
   void OnViewDestroyed(UpdateRequiredView* view);
 
- private:
   // BaseScreen:
   void Show() override;
   void Hide() override;
+  void OnUserAction(const std::string& action_id) override;
+
+  // VersionUpdater::Delegate:
+  void OnWaitForRebootTimeElapsed() override;
+  void PrepareForUpdateCheck() override;
+  void ShowErrorMessage() override;
+  void UpdateErrorMessage(
+      const NetworkPortalDetector::CaptivePortalStatus status,
+      const NetworkError::ErrorState& error_state,
+      const std::string& network_name) override;
+  void DelayErrorMessage() override;
+  void UpdateInfoChanged(
+      const VersionUpdater::UpdateInfo& update_info) override;
+  void FinishExitUpdate(VersionUpdater::Result result) override;
+
+  VersionUpdater* GetVersionUpdaterForTesting();
+
+ private:
+  void EnsureScreenIsShown();
+
+  void OnSelectNetworkButtonClicked();
+  void OnUpdateButtonClicked();
+
+  // NetworkStateHandlerObserver:
+  void NetworkConnectionStateChanged(const NetworkState* network) override;
+  void DefaultNetworkChanged(const NetworkState* network) override;
+
+  void RefreshNetworkState();
+  void RefreshView(const VersionUpdater::UpdateInfo& update_info);
+
+  // Subscribes to network change notifications.
+  void SubscribeNetworkNotification();
+
+  // Unsubscribes from network change notifications.
+  void UnsubscribeNetworkNotification();
+
+  void HideErrorMessage();
+
+  void OnGetEndOfLifeStatus(update_engine::EndOfLifeStatus status);
+
+  // The user requested an attempt to connect to the network should be made.
+  void OnConnectRequested();
+
+  void OnErrorScreenHidden();
+
+  // True if there was no notification about captive portal state for
+  // the default network.
+  bool is_first_portal_notification_ = true;
 
   UpdateRequiredView* view_ = nullptr;
-  bool is_shown_;
+  ErrorScreen* error_screen_;
+  std::unique_ptr<ErrorScreensHistogramHelper> histogram_helper_;
+
+  // Whether the screen is shown.
+  bool is_shown_ = false;
+
+  // True if subscribed to network change notification.
+  bool is_network_subscribed_ = false;
+
+  // True if Show() has never been called yet.
+  bool first_time_shown_ = true;
+  bool is_updating_now_ = false;
+  bool waiting_for_reboot_ = false;
+  bool waiting_for_permission_ = false;
+
+  std::unique_ptr<VersionUpdater> version_updater_;
+  std::unique_ptr<login::NetworkStateHelper> network_state_helper_;
+
+  // Timer for the captive portal detector to show portal login page.
+  // If redirect did not happen during this delay, error message is shown
+  // instead.
+  base::OneShotTimer error_message_timer_;
+
+  ErrorScreen::ConnectRequestCallbackSubscription connect_request_subscription_;
 
   base::WeakPtrFactory<UpdateRequiredScreen> weak_factory_{this};
 
diff --git a/chrome/browser/chromeos/login/screens/update_required_screen_unittest.cc b/chrome/browser/chromeos/login/screens/update_required_screen_unittest.cc
new file mode 100644
index 0000000..9a321a6c
--- /dev/null
+++ b/chrome/browser/chromeos/login/screens/update_required_screen_unittest.cc
@@ -0,0 +1,196 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/login/screens/update_required_screen.h"
+
+#include "base/command_line.h"
+#include "base/optional.h"
+#include "base/test/scoped_mock_time_message_loop_task_runner.h"
+#include "chrome/browser/chromeos/login/screens/mock_error_screen.h"
+#include "chrome/browser/chromeos/login/startup_utils.h"
+#include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/chromeos/settings/device_settings_service.h"
+#include "chrome/browser/ui/ash/test_login_screen.h"
+#include "chrome/browser/ui/webui/chromeos/login/fake_update_required_screen_handler.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chromeos/constants/chromeos_switches.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/fake_update_engine_client.h"
+#include "chromeos/dbus/update_engine_client.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/portal_detector/mock_network_portal_detector.h"
+#include "chromeos/network/portal_detector/network_portal_detector.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::Return;
+
+namespace chromeos {
+
+class UpdateRequiredScreenUnitTest : public testing::Test {
+ public:
+  UpdateRequiredScreenUnitTest()
+      : local_state_(TestingBrowserProcess::GetGlobal()) {}
+
+  void SetUpdateEngineStatus(update_engine::Operation operation) {
+    update_engine::StatusResult status;
+    status.set_current_operation(operation);
+    fake_update_engine_client_->NotifyObserversThatStatusChanged(status);
+  }
+
+  // testing::Test:
+  void SetUp() override {
+    // Configure the browser to use Hands-Off Enrollment.
+    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+        switches::kEnterpriseEnableZeroTouchEnrollment, "hands-off");
+
+    // Initialize objects needed by |UpdateRequiredScreen|.
+    fake_view_ = std::make_unique<FakeUpdateRequiredScreenHandler>();
+    fake_update_engine_client_ = new FakeUpdateEngineClient();
+    DBusThreadManager::GetSetterForTesting()->SetUpdateEngineClient(
+        std::unique_ptr<UpdateEngineClient>(fake_update_engine_client_));
+    NetworkHandler::Initialize();
+    mock_network_portal_detector_ = new MockNetworkPortalDetector();
+    network_portal_detector::SetNetworkPortalDetector(
+        mock_network_portal_detector_);
+    mock_error_screen_.reset(new MockErrorScreen(mock_error_view_.get()));
+
+    // Ensure proper behavior of |UpdateRequiredScreen|'s supporting objects.
+    EXPECT_CALL(*mock_network_portal_detector_, IsEnabled())
+        .Times(AnyNumber())
+        .WillRepeatedly(Return(false));
+
+    update_required_screen_ = std::make_unique<UpdateRequiredScreen>(
+        fake_view_.get(), mock_error_screen_.get());
+
+    update_required_screen_->GetVersionUpdaterForTesting()
+        ->set_wait_for_reboot_time_for_testing(base::TimeDelta::FromSeconds(0));
+  }
+
+  void TearDown() override {
+    TestingBrowserProcess::GetGlobal()->SetShuttingDown(true);
+
+    update_required_screen_.reset();
+    mock_error_view_.reset();
+    mock_error_screen_.reset();
+
+    network_portal_detector::Shutdown();
+    NetworkHandler::Shutdown();
+    DBusThreadManager::Shutdown();
+  }
+
+ protected:
+  // A pointer to the |UpdateRequiredScreen| used in this test.
+  std::unique_ptr<UpdateRequiredScreen> update_required_screen_;
+
+  // Accessory objects needed by |UpdateRequiredScreen|.
+  TestLoginScreen test_login_screen_;
+  std::unique_ptr<FakeUpdateRequiredScreenHandler> fake_view_;
+  std::unique_ptr<MockErrorScreenView> mock_error_view_;
+  std::unique_ptr<MockErrorScreen> mock_error_screen_;
+  // Will be deleted in |network_portal_detector::Shutdown()|.
+  MockNetworkPortalDetector* mock_network_portal_detector_;
+  // Will be deleted in |DBusThreadManager::Shutdown()|.
+  FakeUpdateEngineClient* fake_update_engine_client_;
+
+ private:
+  // Test versions of core browser infrastructure.
+  content::BrowserTaskEnvironment task_environment_;
+  ScopedTestingLocalState local_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateRequiredScreenUnitTest);
+};
+
+namespace {
+constexpr char kUserActionUpdateButtonClicked[] = "update";
+constexpr char kUserActionAcceptUpdateOverCellular[] = "update-accept-cellular";
+}  // namespace
+
+TEST_F(UpdateRequiredScreenUnitTest, HandlesNoUpdate) {
+  // DUT reaches |UpdateRequiredScreen|.
+  update_required_screen_->Show();
+  EXPECT_EQ(fake_view_->ui_state(),
+            UpdateRequiredView::UPDATE_REQUIRED_MESSAGE);
+  update_required_screen_->OnUserAction(kUserActionUpdateButtonClicked);
+
+  // Verify that the DUT checks for an update.
+  EXPECT_EQ(fake_update_engine_client_->request_update_check_call_count(), 1);
+
+  // No updates are available.
+  SetUpdateEngineStatus(update_engine::Operation::CHECKING_FOR_UPDATE);
+  SetUpdateEngineStatus(update_engine::Operation::IDLE);
+
+  EXPECT_EQ(fake_view_->ui_state(), UpdateRequiredView::UPDATE_ERROR);
+}
+
+TEST_F(UpdateRequiredScreenUnitTest, HandlesUpdateExists) {
+  // DUT reaches |UpdateRequiredScreen|.
+  update_required_screen_->Show();
+  EXPECT_EQ(fake_view_->ui_state(),
+            UpdateRequiredView::UPDATE_REQUIRED_MESSAGE);
+  update_required_screen_->OnUserAction(kUserActionUpdateButtonClicked);
+
+  // Verify that the DUT checks for an update.
+  EXPECT_EQ(fake_update_engine_client_->request_update_check_call_count(), 1);
+
+  SetUpdateEngineStatus(update_engine::Operation::CHECKING_FOR_UPDATE);
+
+  SetUpdateEngineStatus(update_engine::Operation::UPDATE_AVAILABLE);
+
+  SetUpdateEngineStatus(update_engine::Operation::DOWNLOADING);
+  EXPECT_EQ(fake_view_->ui_state(), UpdateRequiredView::UPDATE_PROCESS);
+
+  SetUpdateEngineStatus(update_engine::Operation::VERIFYING);
+
+  SetUpdateEngineStatus(update_engine::Operation::FINALIZING);
+
+  SetUpdateEngineStatus(update_engine::Operation::UPDATED_NEED_REBOOT);
+  EXPECT_GE(fake_update_engine_client_->reboot_after_update_call_count(), 1);
+
+  EXPECT_EQ(fake_view_->ui_state(),
+            UpdateRequiredView::UPDATE_COMPLETED_NEED_REBOOT);
+}
+
+TEST_F(UpdateRequiredScreenUnitTest, HandlesCellularPermissionNeeded) {
+  // DUT reaches |UpdateRequiredScreen|.
+  update_required_screen_->Show();
+  EXPECT_EQ(fake_view_->ui_state(),
+            UpdateRequiredView::UPDATE_REQUIRED_MESSAGE);
+  update_required_screen_->OnUserAction(kUserActionUpdateButtonClicked);
+
+  // Verify that the DUT checks for an update.
+  EXPECT_EQ(fake_update_engine_client_->request_update_check_call_count(), 1);
+
+  SetUpdateEngineStatus(update_engine::Operation::CHECKING_FOR_UPDATE);
+
+  SetUpdateEngineStatus(update_engine::Operation::UPDATE_AVAILABLE);
+
+  SetUpdateEngineStatus(update_engine::Operation::NEED_PERMISSION_TO_UPDATE);
+
+  update_required_screen_->OnUserAction(kUserActionAcceptUpdateOverCellular);
+
+  EXPECT_GE(
+      fake_update_engine_client_->update_over_cellular_permission_count() +
+          fake_update_engine_client_
+              ->update_over_cellular_one_time_permission_count(),
+      1);
+
+  SetUpdateEngineStatus(update_engine::Operation::DOWNLOADING);
+  EXPECT_EQ(fake_view_->ui_state(), UpdateRequiredView::UPDATE_PROCESS);
+
+  SetUpdateEngineStatus(update_engine::Operation::VERIFYING);
+
+  SetUpdateEngineStatus(update_engine::Operation::FINALIZING);
+
+  SetUpdateEngineStatus(update_engine::Operation::UPDATED_NEED_REBOOT);
+  EXPECT_GE(fake_update_engine_client_->reboot_after_update_call_count(), 1);
+
+  EXPECT_EQ(fake_view_->ui_state(),
+            UpdateRequiredView::UPDATE_COMPLETED_NEED_REBOOT);
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/version_updater/version_updater.cc b/chrome/browser/chromeos/login/version_updater/version_updater.cc
index ca315bc..9b83bc42 100644
--- a/chrome/browser/chromeos/login/version_updater/version_updater.cc
+++ b/chrome/browser/chromeos/login/version_updater/version_updater.cc
@@ -59,13 +59,28 @@
 VersionUpdater::UpdateInfo::UpdateInfo() {}
 
 VersionUpdater::VersionUpdater(VersionUpdater::Delegate* delegate)
-    : delegate_(delegate), tick_clock_(base::DefaultTickClock::GetInstance()) {}
+    : delegate_(delegate),
+      wait_for_reboot_time_(kWaitForRebootTime),
+      tick_clock_(base::DefaultTickClock::GetInstance()) {
+  Init();
+}
 
 VersionUpdater::~VersionUpdater() {
   DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
   network_portal_detector::GetInstance()->RemoveObserver(this);
 }
 
+void VersionUpdater::Init() {
+  download_start_progress_ = 0;
+  download_last_progress_ = 0;
+  is_download_average_speed_computed_ = false;
+  download_average_speed_ = 0;
+  is_downloading_update_ = false;
+  ignore_idle_status_ = true;
+  is_first_detection_notification_ = true;
+  update_info_ = UpdateInfo();
+}
+
 void VersionUpdater::StartNetworkCheck() {
   // If portal detector is enabled and portal detection before AU is
   // allowed, initiate network state check. Otherwise, directly
@@ -107,14 +122,20 @@
 void VersionUpdater::RebootAfterUpdate() {
   VLOG(1) << "Initiate reboot after update";
   DBusThreadManager::Get()->GetUpdateEngineClient()->RebootAfterUpdate();
-  reboot_timer_.Start(FROM_HERE, kWaitForRebootTime, this,
-                      &VersionUpdater::OnWaitForRebootTimeElapsed);
+  if (wait_for_reboot_time_.is_zero())  // Primarily for testing.
+    OnWaitForRebootTimeElapsed();
+  else
+    reboot_timer_.Start(FROM_HERE, wait_for_reboot_time_, this,
+                        &VersionUpdater::OnWaitForRebootTimeElapsed);
 }
 
 void VersionUpdater::StartExitUpdate(Result result) {
   DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
   network_portal_detector::GetInstance()->RemoveObserver(this);
   delegate_->FinishExitUpdate(result);
+  // Reset internal state, because in case of error user may make another
+  // update attempt.
+  Init();
 }
 
 void VersionUpdater::GetEolStatus(EolStatusCallback callback) {
@@ -172,6 +193,7 @@
     ignore_idle_status_ = false;
   }
 
+  bool exit_update = false;
   switch (status.current_operation()) {
     case update_engine::Operation::CHECKING_FOR_UPDATE:
       break;
@@ -232,7 +254,7 @@
       // Otherwise, it's possible that the update request has not yet been
       // started.
       if (!ignore_idle_status_)
-        StartExitUpdate(Result::UPDATE_NOT_REQUIRED);
+        exit_update = true;
       break;
     case update_engine::Operation::DISABLED:
     case update_engine::Operation::ERROR:
@@ -243,6 +265,8 @@
   }
 
   delegate_->UpdateInfoChanged(update_info_);
+  if (exit_update)
+    StartExitUpdate(Result::UPDATE_NOT_REQUIRED);
 }
 
 void VersionUpdater::UpdateDownloadingStats(
diff --git a/chrome/browser/chromeos/login/version_updater/version_updater.h b/chrome/browser/chromeos/login/version_updater/version_updater.h
index b0f03b2..3441b99 100644
--- a/chrome/browser/chromeos/login/version_updater/version_updater.h
+++ b/chrome/browser/chromeos/login/version_updater/version_updater.h
@@ -102,6 +102,9 @@
   explicit VersionUpdater(VersionUpdater::Delegate* delegate);
   ~VersionUpdater() override;
 
+  // Resets |VersionUpdater| to initial state.
+  void Init();
+
   // Starts network check. If success, starts update check.
   void StartNetworkCheck();
   void StartUpdateCheck();
@@ -120,6 +123,11 @@
     tick_clock_ = tick_clock;
   }
 
+  void set_wait_for_reboot_time_for_testing(
+      base::TimeDelta wait_for_reboot_time) {
+    wait_for_reboot_time_ = wait_for_reboot_time;
+  }
+
   base::OneShotTimer* GetRebootTimerForTesting();
   void UpdateStatusChangedForTesting(const update_engine::StatusResult& status);
 
@@ -180,6 +188,10 @@
   // Timer for the interval to wait for the reboot.
   // If reboot didn't happen - ask user to reboot manually.
   base::OneShotTimer reboot_timer_;
+  // Time in seconds after which we decide that the device has not rebooted
+  // automatically. If reboot didn't happen during this interval, ask user to
+  // reboot device manually.
+  base::TimeDelta wait_for_reboot_time_;
 
   // True if there was no notification from NetworkPortalDetector
   // about state for the default network.
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index 45b97d1..9684e3f 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -518,7 +518,8 @@
       base::BindRepeating(&WizardController::OnSupervisionTransitionScreenExit,
                           weak_factory_.GetWeakPtr())));
   append(std::make_unique<UpdateRequiredScreen>(
-      oobe_ui->GetView<UpdateRequiredScreenHandler>()));
+      oobe_ui->GetView<UpdateRequiredScreenHandler>(),
+      oobe_ui->GetErrorScreen()));
   append(std::make_unique<AssistantOptInFlowScreen>(
       oobe_ui->GetView<AssistantOptInFlowScreenHandler>(),
       base::BindRepeating(&WizardController::OnAssistantOptInFlowScreenExit,
diff --git a/chrome/browser/chromeos/network_change_manager_client.cc b/chrome/browser/chromeos/network_change_manager_client.cc
index 692365e..d3662e04 100644
--- a/chrome/browser/chromeos/network_change_manager_client.cc
+++ b/chrome/browser/chromeos/network_change_manager_client.cc
@@ -84,10 +84,12 @@
 }
 
 void NetworkChangeManagerClient::ConnectToNetworkChangeManager() {
-  network::mojom::NetworkChangeManagerRequest request(
-      mojo::MakeRequest(&network_change_manager_));
-  content::GetNetworkService()->GetNetworkChangeManager(std::move(request));
-  network_change_manager_.set_connection_error_handler(base::BindOnce(
+  if (network_change_manager_.is_bound())
+    network_change_manager_.reset();
+
+  content::GetNetworkService()->GetNetworkChangeManager(
+      network_change_manager_.BindNewPipeAndPassReceiver());
+  network_change_manager_.set_disconnect_handler(base::BindOnce(
       &NetworkChangeManagerClient::ReconnectToNetworkChangeManager,
       base::Unretained(this)));
 }
diff --git a/chrome/browser/chromeos/network_change_manager_client.h b/chrome/browser/chromeos/network_change_manager_client.h
index 36cc507f..af47c95 100644
--- a/chrome/browser/chromeos/network_change_manager_client.h
+++ b/chrome/browser/chromeos/network_change_manager_client.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/network/network_state_handler_observer.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/network_change_notifier.h"
 #include "services/network/public/mojom/network_change_manager.mojom.h"
 
@@ -87,7 +88,7 @@
   std::string service_path_;
 
   net::NetworkChangeNotifierPosix* network_change_notifier_;
-  network::mojom::NetworkChangeManagerPtr network_change_manager_;
+  mojo::Remote<network::mojom::NetworkChangeManager> network_change_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(NetworkChangeManagerClient);
 };
diff --git a/chrome/browser/chromeos/printing/cups_printers_manager.cc b/chrome/browser/chromeos/printing/cups_printers_manager.cc
index 67f4ab77..1f6c1aa 100644
--- a/chrome/browser/chromeos/printing/cups_printers_manager.cc
+++ b/chrome/browser/chromeos/printing/cups_printers_manager.cc
@@ -33,6 +33,7 @@
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_member.h"
 #include "components/prefs/pref_service.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
 namespace chromeos {
@@ -75,10 +76,8 @@
     ash::GetNetworkConfigService(
         remote_cros_network_config_.BindNewPipeAndPassReceiver());
 
-    chromeos::network_config::mojom::CrosNetworkConfigObserverPtr observer_ptr;
-    cros_network_config_observer_receiver_.Bind(
-        mojo::MakeRequest(&observer_ptr));
-    remote_cros_network_config_->AddObserver(std::move(observer_ptr));
+    remote_cros_network_config_->AddObserver(
+        cros_network_config_observer_receiver_.BindNewPipeAndPassRemote());
 
     // Prime the printer cache with the saved and enterprise printers.
     printers_.ReplacePrintersInClass(
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
index 98483bc6..4c7a4ed 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
@@ -260,7 +260,7 @@
   HandleError(navigation_handle);
 }
 
-void WebNavigationTabObserver::DocumentLoadedInFrame(
+void WebNavigationTabObserver::DOMContentLoaded(
     content::RenderFrameHost* render_frame_host) {
   if (!navigation_state_.CanSendEvents(render_frame_host))
     return;
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_api.h b/chrome/browser/extensions/api/web_navigation/web_navigation_api.h
index d5169ed..2bfcc07 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_api.h
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api.h
@@ -51,8 +51,7 @@
       content::NavigationHandle* navigation_handle) override;
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
-  void DocumentLoadedInFrame(
-      content::RenderFrameHost* render_frame_host) override;
+  void DOMContentLoaded(content::RenderFrameHost* render_frame_host) override;
   void DidFinishLoad(content::RenderFrameHost* render_frame_host,
                      const GURL& validated_url) override;
   void DidFailLoad(content::RenderFrameHost* render_frame_host,
diff --git a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
index 14289ab0..86724d2 100644
--- a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
+++ b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
@@ -439,7 +439,7 @@
     SystemLogsResponse* response) {
   std::string reason;
   bool result =
-      base::win::IsKeyboardPresentOnSlate(&reason, ui::GetHiddenWindow());
+      base::win::IsKeyboardPresentOnSlate(ui::GetHiddenWindow(), &reason);
   reason.insert(0, result ? "Keyboard Detected:\n" : "No Keyboard:\n");
   response->emplace(kUsbKeyboardDetected, reason);
 }
diff --git a/chrome/browser/lookalikes/safety_tips/reputation_service.cc b/chrome/browser/lookalikes/safety_tips/reputation_service.cc
index 1d4f149..ef34c9d 100644
--- a/chrome/browser/lookalikes/safety_tips/reputation_service.cc
+++ b/chrome/browser/lookalikes/safety_tips/reputation_service.cc
@@ -21,6 +21,7 @@
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "components/safe_browsing/db/v4_protocol_manager_util.h"
+#include "url/url_constants.h"
 
 namespace {
 
@@ -40,7 +41,8 @@
 bool ShouldTriggerSafetyTipFromLookalike(
     const GURL& url,
     const DomainInfo& navigated_domain,
-    const std::vector<DomainInfo>& engaged_sites) {
+    const std::vector<DomainInfo>& engaged_sites,
+    GURL* safe_url) {
   std::string matched_domain;
   LookalikeMatchType match_type;
 
@@ -55,6 +57,8 @@
     return false;
   }
 
+  *safe_url = GURL(std::string(url::kHttpScheme) +
+                   url::kStandardSchemeSeparator + matched_domain);
   // Edit distance has higher false positives, so it gets its own feature param
   if (match_type == LookalikeMatchType::kEditDistance) {
     return kEnableLookalikeEditDistance.Get();
@@ -222,14 +226,14 @@
                    });
   if (already_engaged != engaged_sites.end()) {
     std::move(callback).Run(security_state::SafetyTipStatus::kNone,
-                            IsIgnored(url), url);
+                            IsIgnored(url), url, GURL());
     return;
   }
 
   // 2. Server-side blocklist check.
   security_state::SafetyTipStatus status = GetUrlBlockType(url);
   if (status != security_state::SafetyTipStatus::kNone) {
-    std::move(callback).Run(status, IsIgnored(url), url);
+    std::move(callback).Run(status, IsIgnored(url), url, GURL());
     return;
   }
 
@@ -238,21 +242,22 @@
   if (navigated_domain.domain_and_registry.empty() ||
       lookalikes::IsTopDomain(navigated_domain)) {
     std::move(callback).Run(security_state::SafetyTipStatus::kNone,
-                            IsIgnored(url), url);
+                            IsIgnored(url), url, GURL());
     return;
   }
 
   // 4. Lookalike heuristics.
-  if (ShouldTriggerSafetyTipFromLookalike(url, navigated_domain,
-                                          engaged_sites)) {
+  GURL safe_url;
+  if (ShouldTriggerSafetyTipFromLookalike(url, navigated_domain, engaged_sites,
+                                          &safe_url)) {
     std::move(callback).Run(security_state::SafetyTipStatus::kLookalike,
-                            IsIgnored(url), url);
+                            IsIgnored(url), url, safe_url);
     return;
   }
 
   // TODO(crbug/984725): 5. Additional client-side heuristics
   std::move(callback).Run(security_state::SafetyTipStatus::kNone,
-                          IsIgnored(url), url);
+                          IsIgnored(url), url, GURL());
 }
 
 security_state::SafetyTipStatus GetUrlBlockType(const GURL& url) {
diff --git a/chrome/browser/lookalikes/safety_tips/reputation_service.h b/chrome/browser/lookalikes/safety_tips/reputation_service.h
index 2b13147..e422be9 100644
--- a/chrome/browser/lookalikes/safety_tips/reputation_service.h
+++ b/chrome/browser/lookalikes/safety_tips/reputation_service.h
@@ -24,8 +24,11 @@
 // Callback type used for retrieving reputation status. |ignored| indicates
 // whether the user has dismissed the warning and thus should not be warned
 // again. |url| is the URL applicable for this result,
-using ReputationCheckCallback = base::OnceCallback<
-    void(security_state::SafetyTipStatus, bool ignored, const GURL& url)>;
+using ReputationCheckCallback =
+    base::OnceCallback<void(security_state::SafetyTipStatus,
+                            bool ignored,
+                            const GURL& url,
+                            const GURL& suggested_url)>;
 
 // Provides reputation information on URLs for Safety Tips.
 class ReputationService : public KeyedService {
diff --git a/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.cc b/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.cc
index e4d62fa..2ef9247 100644
--- a/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.cc
+++ b/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.cc
@@ -129,7 +129,8 @@
 void ReputationWebContentsObserver::HandleReputationCheckResult(
     security_state::SafetyTipStatus safety_tip_status,
     bool user_ignored,
-    const GURL& url) {
+    const GURL& url,
+    const GURL& suggested_url) {
   UMA_HISTOGRAM_ENUMERATION("Security.SafetyTips.SafetyTipShown",
                             safety_tip_status);
 
@@ -162,7 +163,7 @@
     return;
   }
   ShowSafetyTipDialog(
-      web_contents(), safety_tip_status, url,
+      web_contents(), safety_tip_status, url, suggested_url,
       base::BindOnce(OnSafetyTipClosed, safety_tip_status, base::Time::Now()));
 }
 
diff --git a/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.h b/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.h
index f2adca9..d3fcd62 100644
--- a/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.h
+++ b/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.h
@@ -59,7 +59,8 @@
   void HandleReputationCheckResult(
       security_state::SafetyTipStatus safety_tip_status,
       bool user_ignored,
-      const GURL& url);
+      const GURL& url,
+      const GURL& suggested_url);
 
   // A helper method that calls and resets
   // |reputation_check_callback_for_testing_| if it is set.
diff --git a/chrome/browser/lookalikes/safety_tips/safety_tip_infobar_delegate.cc b/chrome/browser/lookalikes/safety_tips/safety_tip_infobar_delegate.cc
index 94e6318e..28147b3 100644
--- a/chrome/browser/lookalikes/safety_tips/safety_tip_infobar_delegate.cc
+++ b/chrome/browser/lookalikes/safety_tips/safety_tip_infobar_delegate.cc
@@ -26,11 +26,13 @@
     content::WebContents* web_contents,
     security_state::SafetyTipStatus safety_tip_status,
     const GURL& url,
+    const GURL& suggested_url,
     base::OnceCallback<void(SafetyTipInteraction)> close_callback) {
   InfoBarService* infobar_service =
       InfoBarService::FromWebContents(web_contents);
   auto delegate = std::make_unique<SafetyTipInfoBarDelegate>(
-      safety_tip_status, url, web_contents, std::move(close_callback));
+      safety_tip_status, url, suggested_url, web_contents,
+      std::move(close_callback));
   infobar_service->AddInfoBar(
       SafetyTipInfoBar::CreateInfoBar(std::move(delegate)));
 }
@@ -40,10 +42,12 @@
 SafetyTipInfoBarDelegate::SafetyTipInfoBarDelegate(
     security_state::SafetyTipStatus safety_tip_status,
     const GURL& url,
+    const GURL& suggested_url,
     WebContents* web_contents,
     base::OnceCallback<void(SafetyTipInteraction)> close_callback)
     : safety_tip_status_(safety_tip_status),
       url_(url),
+      suggested_url_(suggested_url),
       close_callback_(std::move(close_callback)),
       web_contents_(web_contents) {}
 
@@ -52,7 +56,7 @@
 }
 
 base::string16 SafetyTipInfoBarDelegate::GetMessageText() const {
-  return l10n_util::GetStringUTF16(IDS_PAGE_INFO_SAFETY_TIP_SUMMARY);
+  return GetSafetyTipTitle(safety_tip_status_, suggested_url_);
 }
 
 int SafetyTipInfoBarDelegate::GetButtons() const {
@@ -63,7 +67,8 @@
     InfoBarButton button) const {
   switch (button) {
     case BUTTON_OK:
-      return l10n_util::GetStringUTF16(IDS_SAFETY_TIP_ANDROID_LEAVE_BUTTON);
+      return l10n_util::GetStringUTF16(
+          GetSafetyTipLeaveButtonId(safety_tip_status_));
     case BUTTON_CANCEL:
       return l10n_util::GetStringUTF16(IDS_SAFETY_TIP_ANDROID_IGNORE_BUTTON);
     case BUTTON_NONE:
@@ -75,7 +80,10 @@
 
 bool SafetyTipInfoBarDelegate::Accept() {
   action_taken_ = SafetyTipInteraction::kLeaveSite;
-  LeaveSite(web_contents_);
+  auto url = safety_tip_status_ == security_state::SafetyTipStatus::kLookalike
+                 ? suggested_url_
+                 : GURL(safety_tips::kSafeUrl);
+  LeaveSite(web_contents_, url);
   return true;
 }
 
diff --git a/chrome/browser/lookalikes/safety_tips/safety_tip_infobar_delegate.h b/chrome/browser/lookalikes/safety_tips/safety_tip_infobar_delegate.h
index d72236bf..84bf4de 100644
--- a/chrome/browser/lookalikes/safety_tips/safety_tip_infobar_delegate.h
+++ b/chrome/browser/lookalikes/safety_tips/safety_tip_infobar_delegate.h
@@ -18,6 +18,7 @@
   SafetyTipInfoBarDelegate(
       security_state::SafetyTipStatus safety_tip_status,
       const GURL& url,
+      const GURL& suggested_url,
       content::WebContents* web_contents,
       base::OnceCallback<void(SafetyTipInteraction)> close_callback);
   ~SafetyTipInfoBarDelegate() override;
@@ -40,7 +41,14 @@
 
  private:
   security_state::SafetyTipStatus safety_tip_status_;
+
+  // The URL of the page on which the Safety Tip was triggered.
   GURL url_;
+
+  // The URL of the page the Safety Tip suggests you intended to go to, when
+  // applicable (for SafetyTipStatus::kLookalike).
+  GURL suggested_url_;
+
   SafetyTipInteraction action_taken_ = SafetyTipInteraction::kNoAction;
   base::OnceCallback<void(SafetyTipInteraction)> close_callback_;
   content::WebContents* web_contents_;
diff --git a/chrome/browser/lookalikes/safety_tips/safety_tip_ui.h b/chrome/browser/lookalikes/safety_tips/safety_tip_ui.h
index bda321c..baa3260 100644
--- a/chrome/browser/lookalikes/safety_tips/safety_tip_ui.h
+++ b/chrome/browser/lookalikes/safety_tips/safety_tip_ui.h
@@ -38,14 +38,15 @@
 
 // Shows Safety Tip UI using the specified information if it is not already
 // showing. |virtual_url| is the virtual url of the page/frame the info applies
-// to. |safe_url| is the URL that the "Leave" action redirects
-// to. |close_callback| will be called when the dialog is closed; the argument
-// indicates the action that the user took (if any) to close the
-// dialog. Implemented in platform-specific files.
+// to. |suggested_url| is the URL that Chrome thinks the user may have wanted to
+// navigate to (if applicable). |close_callback| will be called when the dialog
+// is closed; the argument indicates the action that the user took (if any) to
+// close the dialog. Implemented in platform-specific files.
 void ShowSafetyTipDialog(
     content::WebContents* web_contents,
     security_state::SafetyTipStatus type,
     const GURL& virtual_url,
+    const GURL& suggested_url,
     base::OnceCallback<void(SafetyTipInteraction)> close_callback);
 
 }  // namespace safety_tips
diff --git a/chrome/browser/lookalikes/safety_tips/safety_tip_ui_helper.cc b/chrome/browser/lookalikes/safety_tips/safety_tip_ui_helper.cc
index f3cf517..ea1b25f 100644
--- a/chrome/browser/lookalikes/safety_tips/safety_tip_ui_helper.cc
+++ b/chrome/browser/lookalikes/safety_tips/safety_tip_ui_helper.cc
@@ -7,20 +7,17 @@
 #include "base/metrics/histogram_functions.h"
 #include "build/build_config.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
+#include "components/security_interstitials/core/common_string_util.h"
 #include "components/security_state/core/security_state.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/browser/web_contents.h"
-#include "url/gurl.h"
+#include "ui/base/l10n/l10n_util.h"
 
-namespace {
+namespace safety_tips {
 
 // URL that the "leave site" button aborts to.
 const char kSafeUrl[] = "chrome://newtab";
 
-}  // namespace
-
-namespace safety_tips {
-
 void RecordSafetyTipInteractionHistogram(content::WebContents* web_contents,
                                          SafetyTipInteraction interaction) {
   SecurityStateTabHelper* helper =
@@ -33,46 +30,67 @@
       interaction);
 }
 
-void LeaveSite(content::WebContents* web_contents) {
+void LeaveSite(content::WebContents* web_contents, const GURL& safe_url) {
   RecordSafetyTipInteractionHistogram(web_contents,
                                       SafetyTipInteraction::kLeaveSite);
   content::OpenURLParams params(
-      GURL(kSafeUrl), content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
+      safe_url, content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
       ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false /* is_renderer_initiated */);
   params.should_replace_current_entry = true;
   web_contents->OpenURL(params);
 }
 
-int GetSafetyTipTitleId(security_state::SafetyTipStatus warning_type) {
-  switch (warning_type) {
+base::string16 GetSafetyTipTitle(
+    security_state::SafetyTipStatus safety_tip_status,
+    const GURL& url) {
+  switch (safety_tip_status) {
     case security_state::SafetyTipStatus::kBadReputation:
+      return l10n_util::GetStringUTF16(
+          IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_TITLE);
     case security_state::SafetyTipStatus::kLookalike:
-#if defined(OS_ANDROID)
-      return IDS_SAFETY_TIP_ANDROID_BAD_REPUTATION_TITLE;
-#else
-      return IDS_PAGE_INFO_SAFETY_TIP_SUMMARY;
-#endif
+      return l10n_util::GetStringFUTF16(
+          IDS_PAGE_INFO_SAFETY_TIP_LOOKALIKE_TITLE,
+          security_interstitials::common_string_util::GetFormattedHostName(
+              url));
     case security_state::SafetyTipStatus::kUnknown:
     case security_state::SafetyTipStatus::kNone:
       NOTREACHED();
+  }
+
+  NOTREACHED();
+  return base::string16();
+}
+
+int GetSafetyTipDescriptionId(security_state::SafetyTipStatus warning_type) {
+  switch (warning_type) {
+    case security_state::SafetyTipStatus::kBadReputation:
+      return IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_DESCRIPTION;
+    case security_state::SafetyTipStatus::kLookalike:
+      return IDS_PAGE_INFO_SAFETY_TIP_LOOKALIKE_DESCRIPTION;
+    case security_state::SafetyTipStatus::kNone:
+    case security_state::SafetyTipStatus::kUnknown:
+      NOTREACHED();
       return 0;
   }
   NOTREACHED();
   return 0;
 }
 
-int GetSafetyTipDescriptionId(security_state::SafetyTipStatus warning_type) {
+int GetSafetyTipLeaveButtonId(security_state::SafetyTipStatus warning_type) {
   switch (warning_type) {
-    case security_state::SafetyTipStatus::kBadReputation:
 #if defined(OS_ANDROID)
-      return IDS_SAFETY_TIP_ANDROID_BAD_REPUTATION_DESCRIPTION;
-#else
-      return IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_DESCRIPTION;
-#endif
+    case security_state::SafetyTipStatus::kBadReputation:
+      return IDS_SAFETY_TIP_ANDROID_LEAVE_BUTTON;
     case security_state::SafetyTipStatus::kLookalike:
-      return IDS_LOOKALIKE_URL_PRIMARY_PARAGRAPH;
-    case security_state::SafetyTipStatus::kNone:
+      return IDS_SAFETY_TIP_ANDROID_GO_TO_SITE_BUTTON;
+#else
+    case security_state::SafetyTipStatus::kBadReputation:
+      return IDS_PAGE_INFO_SAFETY_TIP_LEAVE_BUTTON;
+    case security_state::SafetyTipStatus::kLookalike:
+      return IDS_PAGE_INFO_SAFETY_TIP_GO_TO_SITE_BUTTON;
+#endif
     case security_state::SafetyTipStatus::kUnknown:
+    case security_state::SafetyTipStatus::kNone:
       NOTREACHED();
       return 0;
   }
diff --git a/chrome/browser/lookalikes/safety_tips/safety_tip_ui_helper.h b/chrome/browser/lookalikes/safety_tips/safety_tip_ui_helper.h
index 259bb26f..7485d2d 100644
--- a/chrome/browser/lookalikes/safety_tips/safety_tip_ui_helper.h
+++ b/chrome/browser/lookalikes/safety_tips/safety_tip_ui_helper.h
@@ -5,8 +5,10 @@
 #ifndef CHROME_BROWSER_LOOKALIKES_SAFETY_TIPS_SAFETY_TIP_UI_HELPER_H_
 #define CHROME_BROWSER_LOOKALIKES_SAFETY_TIPS_SAFETY_TIP_UI_HELPER_H_
 
+#include "base/strings/string16.h"
 #include "chrome/browser/lookalikes/safety_tips/safety_tip_ui.h"
 #include "components/security_state/core/security_state.h"
+#include "url/gurl.h"
 
 namespace content {
 class WebContents;
@@ -14,6 +16,9 @@
 
 namespace safety_tips {
 
+// URL that the "leave site" button aborts to by default.
+extern const char kSafeUrl[];
+
 // Records a histogram for a user's interaction with a Safety Tip in the given
 // |web_contents|.
 void RecordSafetyTipInteractionHistogram(content::WebContents* web_contents,
@@ -21,12 +26,15 @@
 
 // Invoke action when 'leave site' button is clicked, and records a histogram.
 // Navigates to a safe URL, replacing the current page in the process.
-void LeaveSite(content::WebContents* web_contents);
+void LeaveSite(content::WebContents* web_contents, const GURL& safe_url);
 
-// Get the title and description string IDs needed to describe the applicable
-// warning type.  Handles both Android and desktop warnings.
-int GetSafetyTipTitleId(security_state::SafetyTipStatus warning_type);
+// Get the titles, descriptions, and button strings or IDs needed to describe
+// the applicable warning type.  Handles both Android and desktop warnings.
+// |url| is used in formatting some strings.
+base::string16 GetSafetyTipTitle(security_state::SafetyTipStatus warning_type,
+                                 const GURL& url);
 int GetSafetyTipDescriptionId(security_state::SafetyTipStatus warning_type);
+int GetSafetyTipLeaveButtonId(security_state::SafetyTipStatus warning_type);
 
 }  // namespace safety_tips
 
diff --git a/chrome/browser/navigation_predictor/navigation_predictor.cc b/chrome/browser/navigation_predictor/navigation_predictor.cc
index c1fe980..ed06794 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor.cc
@@ -252,8 +252,8 @@
 }
 
 void NavigationPredictor::Create(
-    mojo::PendingReceiver<blink::mojom::AnchorElementMetricsHost> receiver,
-    content::RenderFrameHost* render_frame_host) {
+    content::RenderFrameHost* render_frame_host,
+    mojo::PendingReceiver<blink::mojom::AnchorElementMetricsHost> receiver) {
   DCHECK(base::FeatureList::IsEnabled(blink::features::kNavigationPredictor));
 
   // Only valid for the main frame.
diff --git a/chrome/browser/navigation_predictor/navigation_predictor.h b/chrome/browser/navigation_predictor/navigation_predictor.h
index f3037c2..0e58005 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor.h
+++ b/chrome/browser/navigation_predictor/navigation_predictor.h
@@ -47,8 +47,8 @@
   ~NavigationPredictor() override;
 
   // Create and bind NavigationPredictor.
-  static void Create(mojo::PendingReceiver<AnchorElementMetricsHost> receiver,
-                     content::RenderFrameHost* render_frame_host);
+  static void Create(content::RenderFrameHost* render_frame_host,
+                     mojo::PendingReceiver<AnchorElementMetricsHost> receiver);
 
   // Enum describing the possible set of actions that navigation predictor may
   // take. This enum should remain synchronized with enum
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
index dca5e9e..9c830624 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
@@ -387,7 +387,7 @@
       optimization_guide::kComponentHintsUpdatedResultHistogramString,
       hints_updated);
 
-  MaybeScheduleHintsFetch();
+  MaybeScheduleTopHostsHintsFetch();
 
   MaybeRunUpdateClosure(std::move(update_closure));
 }
@@ -409,7 +409,7 @@
   hints_fetcher_ = std::move(hints_fetcher);
 }
 
-void OptimizationGuideHintsManager::MaybeScheduleHintsFetch() {
+void OptimizationGuideHintsManager::MaybeScheduleTopHostsHintsFetch() {
   if (!optimization_guide::features::IsHintsFetchingEnabled() ||
       !top_host_provider_) {
     return;
@@ -417,14 +417,14 @@
 
   if (optimization_guide::switches::ShouldOverrideFetchHintsTimer()) {
     SetLastHintsFetchAttemptTime(clock_->Now());
-    FetchHints();
+    FetchTopHostsHints();
   } else {
-    ScheduleHintsFetch();
+    ScheduleTopHostsHintsFetch();
   }
 }
 
-void OptimizationGuideHintsManager::ScheduleHintsFetch() {
-  DCHECK(!hints_fetch_timer_.IsRunning());
+void OptimizationGuideHintsManager::ScheduleTopHostsHintsFetch() {
+  DCHECK(!top_hosts_hints_fetch_timer_.IsRunning());
 
   const base::TimeDelta time_until_update_time =
       hint_cache_->FetchedHintsUpdateTime() - clock_->Now();
@@ -436,8 +436,9 @@
     // Fetched hints in the store should be updated and an attempt has not
     // been made in last |kFetchRetryDelay|.
     SetLastHintsFetchAttemptTime(clock_->Now());
-    hints_fetch_timer_.Start(FROM_HERE, RandomFetchDelay(), this,
-                             &OptimizationGuideHintsManager::FetchHints);
+    top_hosts_hints_fetch_timer_.Start(
+        FROM_HERE, RandomFetchDelay(), this,
+        &OptimizationGuideHintsManager::FetchTopHostsHints);
   } else {
     if (time_until_update_time >= base::TimeDelta()) {
       // If the fetched hints in the store are still up-to-date, set a timer
@@ -449,13 +450,13 @@
       // delay.
       fetcher_delay = time_until_retry;
     }
-    hints_fetch_timer_.Start(
+    top_hosts_hints_fetch_timer_.Start(
         FROM_HERE, fetcher_delay, this,
-        &OptimizationGuideHintsManager::ScheduleHintsFetch);
+        &OptimizationGuideHintsManager::ScheduleTopHostsHintsFetch);
   }
 }
 
-void OptimizationGuideHintsManager::FetchHints() {
+void OptimizationGuideHintsManager::FetchTopHostsHints() {
   DCHECK(top_host_provider_);
 
   size_t max_hints_to_fetch = optimization_guide::features::
@@ -473,37 +474,54 @@
         pref_service_);
   }
   hints_fetcher_->FetchOptimizationGuideServiceHints(
-      top_hosts, base::BindOnce(&OptimizationGuideHintsManager::OnHintsFetched,
-                                ui_weak_ptr_factory_.GetWeakPtr()));
+      top_hosts, optimization_guide::proto::CONTEXT_BATCH_UPDATE,
+      base::BindOnce(&OptimizationGuideHintsManager::OnHintsFetched,
+                     ui_weak_ptr_factory_.GetWeakPtr()));
 }
 
 void OptimizationGuideHintsManager::OnHintsFetched(
+    optimization_guide::proto::RequestContext request_context,
+    base::Optional<std::unique_ptr<optimization_guide::proto::GetHintsResponse>>
+        get_hints_response) {
+  switch (request_context) {
+    case optimization_guide::proto::CONTEXT_BATCH_UPDATE:
+      OnTopHostsHintsFetched(std::move(get_hints_response));
+      return;
+    case optimization_guide::proto::CONTEXT_PAGE_NAVIGATION:
+    case optimization_guide::proto::CONTEXT_UNSPECIFIED:
+      NOTREACHED();
+  }
+  NOTREACHED();
+}
+
+void OptimizationGuideHintsManager::OnTopHostsHintsFetched(
     base::Optional<std::unique_ptr<optimization_guide::proto::GetHintsResponse>>
         get_hints_response) {
   if (get_hints_response) {
     hint_cache_->UpdateFetchedHints(
         std::move(*get_hints_response),
         clock_->Now() + kUpdateFetchedHintsDelay,
-        base::BindOnce(&OptimizationGuideHintsManager::OnFetchedHintsStored,
-                       ui_weak_ptr_factory_.GetWeakPtr()));
+        base::BindOnce(
+            &OptimizationGuideHintsManager::OnFetchedTopHostsHintsStored,
+            ui_weak_ptr_factory_.GetWeakPtr()));
   } else {
     // The fetch did not succeed so we will schedule to retry the fetch in
     // after delaying for |kFetchRetryDelay|
     // TODO(mcrouse): When the store is refactored from closures, the timer will
     // be scheduled on failure of the store instead.
-    hints_fetch_timer_.Start(
+    top_hosts_hints_fetch_timer_.Start(
         FROM_HERE, kFetchRetryDelay, this,
-        &OptimizationGuideHintsManager::ScheduleHintsFetch);
+        &OptimizationGuideHintsManager::ScheduleTopHostsHintsFetch);
   }
 }
 
-void OptimizationGuideHintsManager::OnFetchedHintsStored() {
+void OptimizationGuideHintsManager::OnFetchedTopHostsHintsStored() {
   LOCAL_HISTOGRAM_BOOLEAN("OptimizationGuide.FetchedHints.Stored", true);
 
-  hints_fetch_timer_.Stop();
-  hints_fetch_timer_.Start(
+  top_hosts_hints_fetch_timer_.Stop();
+  top_hosts_hints_fetch_timer_.Start(
       FROM_HERE, hint_cache_->FetchedHintsUpdateTime() - clock_->Now(), this,
-      &OptimizationGuideHintsManager::ScheduleHintsFetch);
+      &OptimizationGuideHintsManager::ScheduleTopHostsHintsFetch);
 }
 
 base::Time OptimizationGuideHintsManager::GetLastHintsFetchAttemptTime() const {
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.h b/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
index c151250..3659e8c4 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
@@ -172,27 +172,35 @@
 
   // Method to decide whether to fetch new hints for user's top sites and
   // proceeds to schedule the fetch.
-  void MaybeScheduleHintsFetch();
+  void MaybeScheduleTopHostsHintsFetch();
 
   // Schedules |hints_fetch_timer_| to fire based on:
   // 1. The update time for the fetched hints in the store and
   // 2. The last time a fetch attempt was made.
-  void ScheduleHintsFetch();
+  void ScheduleTopHostsHintsFetch();
 
   // Called to make a request to fetch hints from the remote Optimization Guide
-  // Service.
-  void FetchHints();
+  // Service. Used to fetch hints for origins frequently visited by the user.
+  void FetchTopHostsHints();
 
   // Called when the hints have been fetched from the remote Optimization Guide
   // Service and are ready for parsing.
   void OnHintsFetched(
+      optimization_guide::proto::RequestContext request_context,
+      base::Optional<
+          std::unique_ptr<optimization_guide::proto::GetHintsResponse>>
+          get_hints_response);
+
+  // Called when the hints for the top hosts have been fetched from the remote
+  // Optimization Guide Service and are ready for parsing.
+  void OnTopHostsHintsFetched(
       base::Optional<
           std::unique_ptr<optimization_guide::proto::GetHintsResponse>>
           get_hints_response);
 
   // Called when the fetched hints have been stored in |hint_cache| and are
   // ready to be used.
-  void OnFetchedHintsStored();
+  void OnFetchedTopHostsHintsStored();
 
   // Returns the time when a hints fetch request was last attempted.
   base::Time GetLastHintsFetchAttemptTime() const;
@@ -256,7 +264,7 @@
 
   // The timer used to schedule fetching hints from the remote Optimization
   // Guide Service.
-  base::OneShotTimer hints_fetch_timer_;
+  base::OneShotTimer top_hosts_hints_fetch_timer_;
 
   // The clock used to schedule fetching from the remote Optimization Guide
   // Service.
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
index b279ecb..cb8fe40 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
@@ -170,19 +170,22 @@
 
   bool FetchOptimizationGuideServiceHints(
       const std::vector<std::string>& hosts,
+      optimization_guide::proto::RequestContext request_context,
       optimization_guide::HintsFetchedCallback hints_fetched_callback)
       override {
     switch (fetch_state_) {
       case HintsFetcherEndState::kFetchFailed:
-        std::move(hints_fetched_callback).Run(base::nullopt);
+        std::move(hints_fetched_callback).Run(request_context, base::nullopt);
         return false;
       case HintsFetcherEndState::kFetchSuccessWithHints:
         hints_fetched_ = true;
-        std::move(hints_fetched_callback).Run(BuildHintsResponse({"host.com"}));
+        std::move(hints_fetched_callback)
+            .Run(request_context, BuildHintsResponse({"host.com"}));
         return true;
       case HintsFetcherEndState::kFetchSuccessWithNoHints:
         hints_fetched_ = true;
-        std::move(hints_fetched_callback).Run(BuildHintsResponse({}));
+        std::move(hints_fetched_callback)
+            .Run(request_context, BuildHintsResponse({}));
         return true;
     }
     return true;
diff --git a/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.cc
index 462d7ad3..f9b0ef2a 100644
--- a/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.cc
@@ -11,12 +11,16 @@
 
 #define HISTOGRAM_SXG_PREFIX "PageLoad.Clients.SignedExchange."
 #define HISTOGRAM_CACHED_SXG_PREFIX "PageLoad.Clients.SignedExchange.Cached."
+#define HISTOGRAM_NOTCACHED_SXG_PREFIX \
+  "PageLoad.Clients.SignedExchange.NotCached."
 #define HISTOGRAM_ALT_SUB_SXG_PREFIX \
   "PageLoad.Clients.SignedExchange.AltSubSXG."
 
 constexpr char kHistogramSignedExchangePrefix[] = HISTOGRAM_SXG_PREFIX;
 constexpr char kHistogramCachedSignedExchangePrefix[] =
     HISTOGRAM_CACHED_SXG_PREFIX;
+constexpr char kHistogramNotCachedSignedExchangePrefix[] =
+    HISTOGRAM_NOTCACHED_SXG_PREFIX;
 constexpr char kHistogramAltSubSxgSignedExchangePrefix[] =
     HISTOGRAM_ALT_SUB_SXG_PREFIX;
 
@@ -25,6 +29,8 @@
       HISTOGRAM_SXG_PREFIX suffix;                           \
   constexpr char kHistogramCachedSignedExchange##name[] =    \
       HISTOGRAM_CACHED_SXG_PREFIX suffix;                    \
+  constexpr char kHistogramNotCachedSignedExchange##name[] = \
+      HISTOGRAM_NOTCACHED_SXG_PREFIX suffix;                 \
   constexpr char kHistogramAltSubSxgSignedExchange##name[] = \
       HISTOGRAM_ALT_SUB_SXG_PREFIX suffix;
 
@@ -53,6 +59,9 @@
     if (was_cached_) {                                                       \
       PAGE_LOAD_HISTOGRAM(internal::kHistogramCachedSignedExchange##name,    \
                           value);                                            \
+    } else {                                                                 \
+      PAGE_LOAD_HISTOGRAM(internal::kHistogramNotCachedSignedExchange##name, \
+                          value);                                            \
     }                                                                        \
     if (had_prefetched_alt_sxg_) {                                           \
       PAGE_LOAD_HISTOGRAM(internal::kHistogramAltSubSxgSignedExchange##name, \
@@ -63,6 +72,7 @@
 #undef SXG_LOAD_METRIC_VARIABLE
 #undef HISTOGRAM_ALT_SUB_SXG_PREFIX
 #undef HISTOGRAM_CACHED_SXG_PREFIX
+#undef HISTOGRAM_NOTCACHED_SXG_PREFIX
 #undef HISTOGRAM_SXG_PREFIX
 
 }  // namespace internal
@@ -166,6 +176,12 @@
         timing.interactive_timing->first_input_delay.value(),
         base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromSeconds(60),
         50);
+  } else {
+    UMA_HISTOGRAM_CUSTOM_TIMES(
+        internal::kHistogramNotCachedSignedExchangeFirstInputDelay,
+        timing.interactive_timing->first_input_delay.value(),
+        base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromSeconds(60),
+        50);
   }
   if (had_prefetched_alt_sxg_) {
     UMA_HISTOGRAM_CUSTOM_TIMES(
diff --git a/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h
index 7f83730f..d8aaf7e 100644
--- a/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h
@@ -35,6 +35,19 @@
 extern const char kHistogramCachedSignedExchangeDomContentLoaded[];
 extern const char kHistogramCachedSignedExchangeLoad[];
 
+extern const char kHistogramNotCachedSignedExchangePrefix[];
+extern const char kHistogramNotCachedSignedExchangeParseStart[];
+extern const char kHistogramNotCachedSignedExchangeFirstInputDelay[];
+extern const char kHistogramNotCachedSignedExchangeFirstPaint[];
+extern const char kHistogramNotCachedSignedExchangeFirstContentfulPaint[];
+extern const char
+    kHistogramNotCachedSignedExchangeParseStartToFirstContentfulPaint[];
+extern const char kHistogramNotCachedSignedExchangeFirstMeaningfulPaint[];
+extern const char
+    kHistogramNotCachedSignedExchangeParseStartToFirstMeaningfulPaint[];
+extern const char kHistogramNotCachedSignedExchangeDomContentLoaded[];
+extern const char kHistogramNotCachedSignedExchangeLoad[];
+
 extern const char kHistogramAltSubSxgSignedExchangePrefix[];
 extern const char kHistogramAltSubSxgSignedExchangeParseStart[];
 extern const char kHistogramAltSubSxgSignedExchangeFirstInputDelay[];
diff --git a/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer_unittest.cc
index 6900bd5..651b22b 100644
--- a/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer_unittest.cc
@@ -37,26 +37,19 @@
     navigation->Commit();
   }
 
-  void AssertNoSignedExchangeHistogramsLogged() {
-    base::HistogramTester::CountsMap counts_map =
-        histogram_tester().GetTotalCountsForPrefix(
-            internal::kHistogramSignedExchangePrefix);
-    for (const auto& it : counts_map) {
+  void AssertNoSignedExchangeHistogramsLoggedFor(const std::string& prefix) {
+    base::HistogramTester::CountsMap empty_counts_map =
+        histogram_tester().GetTotalCountsForPrefix(prefix);
+    for (const auto& it : empty_counts_map) {
       base::HistogramBase::Count count = it.second;
       EXPECT_EQ(0, count) << "Histogram \"" << it.first
                           << "\" should be empty.";
     }
   }
 
-  void AssertNoCachedSignedExchangeHistogramsLogged() {
-    base::HistogramTester::CountsMap counts_map =
-        histogram_tester().GetTotalCountsForPrefix(
-            internal::kHistogramCachedSignedExchangePrefix);
-    for (const auto& it : counts_map) {
-      base::HistogramBase::Count count = it.second;
-      EXPECT_EQ(0, count) << "Histogram \"" << it.first
-                          << "\" should be empty.";
-    }
+  void AssertNoSignedExchangeHistogramsLogged() {
+    AssertNoSignedExchangeHistogramsLoggedFor(
+        internal::kHistogramSignedExchangePrefix);
   }
 
   void InitializeTestPageLoadTiming(
@@ -83,7 +76,10 @@
 
 TEST_F(SignedExchangePageLoadMetricsObserverTest, NoMetrics) {
   AssertNoSignedExchangeHistogramsLogged();
-  AssertNoCachedSignedExchangeHistogramsLogged();
+  AssertNoSignedExchangeHistogramsLoggedFor(
+      internal::kHistogramCachedSignedExchangePrefix);
+  AssertNoSignedExchangeHistogramsLoggedFor(
+      internal::kHistogramNotCachedSignedExchangePrefix);
 }
 
 TEST_F(SignedExchangePageLoadMetricsObserverTest, NoSignedExchange) {
@@ -94,7 +90,10 @@
   SimulateTimingUpdate(timing);
 
   AssertNoSignedExchangeHistogramsLogged();
-  AssertNoCachedSignedExchangeHistogramsLogged();
+  AssertNoSignedExchangeHistogramsLoggedFor(
+      internal::kHistogramCachedSignedExchangePrefix);
+  AssertNoSignedExchangeHistogramsLoggedFor(
+      internal::kHistogramNotCachedSignedExchangePrefix);
 }
 
 TEST_F(SignedExchangePageLoadMetricsObserverTest, WithSignedExchange) {
@@ -151,7 +150,64 @@
       internal::kHistogramSignedExchangeParseStart,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
 
-  AssertNoCachedSignedExchangeHistogramsLogged();
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramNotCachedSignedExchangeParseStart, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramNotCachedSignedExchangeParseStart,
+      timing.parse_timing->parse_start.value().InMilliseconds(), 1);
+
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramNotCachedSignedExchangeFirstInputDelay, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramNotCachedSignedExchangeFirstInputDelay,
+      timing.interactive_timing->first_input_delay.value().InMilliseconds(), 1);
+
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramNotCachedSignedExchangeFirstPaint, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramNotCachedSignedExchangeFirstPaint,
+      timing.paint_timing->first_paint.value().InMilliseconds(), 1);
+
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramNotCachedSignedExchangeFirstContentfulPaint, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramNotCachedSignedExchangeFirstContentfulPaint,
+      timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
+
+  histogram_tester().ExpectTotalCount(
+      internal::
+          kHistogramNotCachedSignedExchangeParseStartToFirstContentfulPaint,
+      1);
+  histogram_tester().ExpectBucketCount(
+      internal::
+          kHistogramNotCachedSignedExchangeParseStartToFirstContentfulPaint,
+      (timing.paint_timing->first_contentful_paint.value() -
+       timing.parse_timing->parse_start.value())
+          .InMilliseconds(),
+      1);
+
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramNotCachedSignedExchangeDomContentLoaded, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramNotCachedSignedExchangeDomContentLoaded,
+      timing.document_timing->dom_content_loaded_event_start.value()
+          .InMilliseconds(),
+      1);
+
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramNotCachedSignedExchangeLoad, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramNotCachedSignedExchangeLoad,
+      timing.document_timing->load_event_start.value().InMilliseconds(), 1);
+
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramNotCachedSignedExchangeParseStart, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramNotCachedSignedExchangeParseStart,
+      timing.parse_timing->parse_start.value().InMilliseconds(), 1);
+
+  AssertNoSignedExchangeHistogramsLoggedFor(
+      internal::kHistogramCachedSignedExchangePrefix);
 }
 
 TEST_F(SignedExchangePageLoadMetricsObserverTest, WithCachedSignedExchange) {
@@ -255,6 +311,9 @@
   histogram_tester().ExpectBucketCount(
       internal::kHistogramCachedSignedExchangeParseStart,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
+
+  AssertNoSignedExchangeHistogramsLoggedFor(
+      internal::kHistogramNotCachedSignedExchangePrefix);
 }
 
 TEST_F(SignedExchangePageLoadMetricsObserverTest,
@@ -274,5 +333,8 @@
   SimulateTimingUpdate(timing);
 
   AssertNoSignedExchangeHistogramsLogged();
-  AssertNoCachedSignedExchangeHistogramsLogged();
+  AssertNoSignedExchangeHistogramsLoggedFor(
+      internal::kHistogramCachedSignedExchangePrefix);
+  AssertNoSignedExchangeHistogramsLoggedFor(
+      internal::kHistogramNotCachedSignedExchangePrefix);
 }
diff --git a/chrome/browser/permissions/grouped_permission_infobar_delegate_android.cc b/chrome/browser/permissions/grouped_permission_infobar_delegate_android.cc
index f7d44939..7ce95616 100644
--- a/chrome/browser/permissions/grouped_permission_infobar_delegate_android.cc
+++ b/chrome/browser/permissions/grouped_permission_infobar_delegate_android.cc
@@ -55,7 +55,7 @@
 }
 
 int GroupedPermissionInfoBarDelegate::GetIconId() const {
-  return permission_prompt_->GetIconId();
+  return IDR_ANDROID_INFOBAR_NOTIFICATIONS_OFF;
 }
 
 base::string16 GroupedPermissionInfoBarDelegate::GetMessageText() const {
diff --git a/chrome/browser/permissions/permission_request_manager.cc b/chrome/browser/permissions/permission_request_manager.cc
index ce6872a1..39a76ab 100644
--- a/chrome/browser/permissions/permission_request_manager.cc
+++ b/chrome/browser/permissions/permission_request_manager.cc
@@ -199,7 +199,7 @@
   ScheduleShowBubble();
 }
 
-void PermissionRequestManager::DocumentLoadedInFrame(
+void PermissionRequestManager::DOMContentLoaded(
     content::RenderFrameHost* render_frame_host) {
   ScheduleShowBubble();
 }
diff --git a/chrome/browser/permissions/permission_request_manager.h b/chrome/browser/permissions/permission_request_manager.h
index 4df8797..2d57512c 100644
--- a/chrome/browser/permissions/permission_request_manager.h
+++ b/chrome/browser/permissions/permission_request_manager.h
@@ -96,8 +96,7 @@
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
   void DocumentOnLoadCompletedInMainFrame() override;
-  void DocumentLoadedInFrame(
-      content::RenderFrameHost* render_frame_host) override;
+  void DOMContentLoaded(content::RenderFrameHost* render_frame_host) override;
   void WebContentsDestroyed() override;
   void OnVisibilityChanged(content::Visibility visibility) override;
 
diff --git a/chrome/browser/permissions/permission_request_manager_unittest.cc b/chrome/browser/permissions/permission_request_manager_unittest.cc
index 8ca1aa2..9e4d8f5 100644
--- a/chrome/browser/permissions/permission_request_manager_unittest.cc
+++ b/chrome/browser/permissions/permission_request_manager_unittest.cc
@@ -86,7 +86,7 @@
 
   void WaitForFrameLoad() {
     // PermissionRequestManager ignores all parameters. Yay?
-    manager_->DocumentLoadedInFrame(NULL);
+    manager_->DOMContentLoaded(NULL);
     base::RunLoop().RunUntilIdle();
   }
 
diff --git a/chrome/browser/permissions/permission_request_notification_android.cc b/chrome/browser/permissions/permission_request_notification_android.cc
index 76ff836..fa8acbe 100644
--- a/chrome/browser/permissions/permission_request_notification_android.cc
+++ b/chrome/browser/permissions/permission_request_notification_android.cc
@@ -26,12 +26,11 @@
 
 namespace {
 const gfx::Image GetNotificationsSmallImage() {
-  // TODO(andypaicu): when the "notifications blocked" icon is submitted, use
-  // that icon instead of this one.
-  return gfx::Image(CreateVectorIcon(
-      vector_icons::kNotificationsIcon, message_center::kNotificationIconSize,
-      ui::NativeTheme::GetInstanceForWeb()->GetSystemColor(
-          ui::NativeTheme::kColorId_DefaultIconColor)));
+  return gfx::Image(
+      CreateVectorIcon(vector_icons::kNotificationsOffIcon,
+                       message_center::kNotificationIconSize,
+                       ui::NativeTheme::GetInstanceForWeb()->GetSystemColor(
+                           ui::NativeTheme::kColorId_DefaultIconColor)));
 }
 
 constexpr char kNotificationIdPrefix[] = "notification_permission_request_";
diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc
index 2f9030164..fa32f55 100644
--- a/chrome/browser/prerender/prerender_contents.cc
+++ b/chrome/browser/prerender/prerender_contents.cc
@@ -537,7 +537,7 @@
   NotifyPrerenderStopLoading();
 }
 
-void PrerenderContents::DocumentLoadedInFrame(
+void PrerenderContents::DOMContentLoaded(
     content::RenderFrameHost* render_frame_host) {
   if (!render_frame_host->GetParent())
     NotifyPrerenderDomContentLoaded();
diff --git a/chrome/browser/prerender/prerender_contents.h b/chrome/browser/prerender/prerender_contents.h
index cec41baf..c8f8eba 100644
--- a/chrome/browser/prerender/prerender_contents.h
+++ b/chrome/browser/prerender/prerender_contents.h
@@ -172,8 +172,7 @@
   // content::WebContentsObserver implementation.
   void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
   void DidStopLoading() override;
-  void DocumentLoadedInFrame(
-      content::RenderFrameHost* render_frame_host) override;
+  void DOMContentLoaded(content::RenderFrameHost* render_frame_host) override;
   void DidStartNavigation(
       content::NavigationHandle* navigation_handle) override;
   void DidRedirectNavigation(
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc
index b7754605..7c6730dc 100644
--- a/chrome/browser/prerender/prerender_manager.cc
+++ b/chrome/browser/prerender/prerender_manager.cc
@@ -159,6 +159,35 @@
 PrerenderManager::PrerenderManagerMode PrerenderManager::mode_ =
     PRERENDER_MODE_NOSTATE_PREFETCH;
 
+// static
+bool PrerenderManager::MaybeUsePrerenderedPage(
+    Profile* profile,
+    content::WebContents* web_contents,
+    const GURL& url,
+    bool* loaded) {
+  DCHECK(loaded) << "|loaded| cannot be null";
+  auto* manager = PrerenderManagerFactory::GetForBrowserContext(profile);
+
+  // Getting the load status before MaybeUsePrerenderedPage() b/c it resets.
+  *loaded = false;
+  auto contents = manager->GetAllPrerenderingContents();
+  for (content::WebContents* content : contents) {
+    auto* prerender_contents = manager->GetPrerenderContents(content);
+    if (prerender_contents->prerender_url() == url &&
+        prerender_contents->has_finished_loading()) {
+      *loaded = true;
+      break;
+    }
+  }
+  if (!*loaded)
+    return false;
+
+  PrerenderManager::Params params(
+      /*uses_post=*/false, /*extra_headers=*/std::string(),
+      /*should_replace_current_entry=*/false, web_contents);
+  return manager->MaybeUsePrerenderedPage(url, &params);
+}
+
 struct PrerenderManager::NavigationRecord {
   NavigationRecord(const GURL& url, base::TimeTicks time, Origin origin)
       : url(url), time(time), origin(origin) {}
diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h
index 38d6dbe..8cae2ab2 100644
--- a/chrome/browser/prerender/prerender_manager.h
+++ b/chrome/browser/prerender/prerender_manager.h
@@ -101,6 +101,22 @@
     CLEAR_MAX = 0x1 << 2
   };
 
+  // If |url| matches a valid prerendered page in one of the contents,
+  // try to swap it and merge browsing histories.
+  //
+  // Returns true if a prerendered page is swapped in. When this happens, the
+  // PrerenderManager has already swapped out |contents_being_navigated| with
+  // |replaced_contents| in the WebContents container [e.g. TabStripModel on
+  // desktop]. |loaded| is set to true if the page finished loading.
+  //
+  // Returns false if nothing is swapped.
+  //
+  // |loaded| cannot be null.
+  static bool MaybeUsePrerenderedPage(Profile* profile,
+                                      content::WebContents* web_contents,
+                                      const GURL& url,
+                                      bool* loaded);
+
   // Owned by a Profile object for the lifetime of the profile.
   explicit PrerenderManager(Profile* profile);
   ~PrerenderManager() override;
diff --git a/chrome/browser/prerender/prerender_test_helper.cc b/chrome/browser/prerender/prerender_test_helper.cc
new file mode 100644
index 0000000..ef3082c
--- /dev/null
+++ b/chrome/browser/prerender/prerender_test_helper.cc
@@ -0,0 +1,45 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "chrome/android/test_support_jni_headers/PrerenderTestHelper_jni.h"
+#include "chrome/browser/prerender/prerender_contents.h"
+#include "chrome/browser/prerender/prerender_manager.h"
+#include "chrome/browser/prerender/prerender_manager_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "content/public/browser/web_contents.h"
+
+// Test helper method required by Java layer.
+
+static jboolean JNI_PrerenderTestHelper_HasPrerenderedUrl(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jweb_contents,
+    const base::android::JavaParamRef<jstring>& jurl) {
+  auto* web_contents = content::WebContents::FromJavaWebContents(jweb_contents);
+  if (!web_contents)
+    return false;
+
+  auto* profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+  if (!profile)
+    return false;
+
+  auto* prerender_manager =
+      prerender::PrerenderManagerFactory::GetForBrowserContext(profile);
+  if (!prerender_manager)
+    return false;
+
+  GURL url(base::android::ConvertJavaStringToUTF8(env, jurl));
+  auto contents = prerender_manager->GetAllPrerenderingContents();
+  for (auto* content : contents) {
+    auto* prerender_contents = prerender_manager->GetPrerenderContents(content);
+    if (prerender_contents->prerender_url() == url &&
+        prerender_contents->has_finished_loading()) {
+      return true;
+    }
+  }
+  return false;
+}
diff --git a/chrome/browser/printing/pdf_nup_converter_client.cc b/chrome/browser/printing/pdf_nup_converter_client.cc
index a251fe6..275e664 100644
--- a/chrome/browser/printing/pdf_nup_converter_client.cc
+++ b/chrome/browser/printing/pdf_nup_converter_client.cc
@@ -24,7 +24,7 @@
     const gfx::Rect& printable_area,
     std::vector<base::ReadOnlySharedMemoryRegion> pdf_page_regions,
     mojom::PdfNupConverter::NupPageConvertCallback callback) {
-  auto& nup_converter = GetPdfNupConverterRequest(document_cookie);
+  auto& nup_converter = GetPdfNupConverterRemote(document_cookie);
   nup_converter->NupPageConvert(pages_per_sheet, page_size, printable_area,
                                 std::move(pdf_page_regions),
                                 std::move(callback));
@@ -37,7 +37,7 @@
     const gfx::Rect& printable_area,
     base::ReadOnlySharedMemoryRegion src_pdf_document,
     mojom::PdfNupConverter::NupDocumentConvertCallback callback) {
-  auto& nup_converter = GetPdfNupConverterRequest(document_cookie);
+  auto& nup_converter = GetPdfNupConverterRemote(document_cookie);
   nup_converter->NupDocumentConvert(
       pages_per_sheet, page_size, printable_area, std::move(src_pdf_document),
       base::BindOnce(&PdfNupConverterClient::OnDidNupPdfDocumentConvert,
@@ -50,12 +50,12 @@
     mojom::PdfNupConverter::NupDocumentConvertCallback callback,
     mojom::PdfNupConverter::Status status,
     base::ReadOnlySharedMemoryRegion region) {
-  RemovePdfNupConverterRequest(document_cookie);
+  RemovePdfNupConverterRemote(document_cookie);
   std::move(callback).Run(status, std::move(region));
 }
 
-mojom::PdfNupConverterPtr& PdfNupConverterClient::GetPdfNupConverterRequest(
-    int cookie) {
+mojo::Remote<mojom::PdfNupConverter>&
+PdfNupConverterClient::GetPdfNupConverterRemote(int cookie) {
   auto iter = pdf_nup_converter_map_.find(cookie);
   if (iter != pdf_nup_converter_map_.end()) {
     DCHECK(iter->second.is_bound());
@@ -63,21 +63,21 @@
   }
 
   auto iterator =
-      pdf_nup_converter_map_.emplace(cookie, CreatePdfNupConverterRequest())
+      pdf_nup_converter_map_.emplace(cookie, CreatePdfNupConverterRemote())
           .first;
   return iterator->second;
 }
 
-void PdfNupConverterClient::RemovePdfNupConverterRequest(int cookie) {
+void PdfNupConverterClient::RemovePdfNupConverterRemote(int cookie) {
   size_t erased = pdf_nup_converter_map_.erase(cookie);
   DCHECK_EQ(erased, 1u);
 }
 
-mojom::PdfNupConverterPtr
-PdfNupConverterClient::CreatePdfNupConverterRequest() {
-  mojom::PdfNupConverterPtr pdf_nup_converter;
+mojo::Remote<mojom::PdfNupConverter>
+PdfNupConverterClient::CreatePdfNupConverterRemote() {
+  mojo::Remote<mojom::PdfNupConverter> pdf_nup_converter;
   GetPrintingService()->BindPdfNupConverter(
-      mojo::MakeRequest(&pdf_nup_converter));
+      pdf_nup_converter.BindNewPipeAndPassReceiver());
   pdf_nup_converter->SetWebContentsURL(web_contents_->GetLastCommittedURL());
   return pdf_nup_converter;
 }
diff --git a/chrome/browser/printing/pdf_nup_converter_client.h b/chrome/browser/printing/pdf_nup_converter_client.h
index 3efc158..794a215 100644
--- a/chrome/browser/printing/pdf_nup_converter_client.h
+++ b/chrome/browser/printing/pdf_nup_converter_client.h
@@ -10,6 +10,7 @@
 
 #include "chrome/services/printing/public/mojom/pdf_nup_converter.mojom.h"
 #include "content/public/browser/web_contents_user_data.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/service_manager/public/cpp/connector.h"
 
 namespace printing {
@@ -48,19 +49,19 @@
       mojom::PdfNupConverter::Status status,
       base::ReadOnlySharedMemoryRegion region);
 
-  // Get the request or create a new one if none exists.
-  mojom::PdfNupConverterPtr& GetPdfNupConverterRequest(int cookie);
+  // Get the mojo::Remote or create a new one if none exists.
+  mojo::Remote<mojom::PdfNupConverter>& GetPdfNupConverterRemote(int cookie);
 
-  // Remove an existing request from |pdf_nup_converter_map_|.
-  void RemovePdfNupConverterRequest(int cookie);
+  // Remove an existing mojo::Remote from |pdf_nup_converter_map_|.
+  void RemovePdfNupConverterRemote(int cookie);
 
-  mojom::PdfNupConverterPtr CreatePdfNupConverterRequest();
+  mojo::Remote<mojom::PdfNupConverter> CreatePdfNupConverterRemote();
 
   std::unique_ptr<service_manager::Connector> connector_;
 
   // Stores the mapping between document cookies and their corresponding
-  // requests.
-  std::map<int, mojom::PdfNupConverterPtr> pdf_nup_converter_map_;
+  // mojo::Remote.
+  std::map<int, mojo::Remote<mojom::PdfNupConverter>> pdf_nup_converter_map_;
 
   content::WebContents* web_contents_;
 
diff --git a/chrome/browser/printing/print_dialog_cloud_win.cc b/chrome/browser/printing/print_dialog_cloud_win.cc
index b77e8bc..250d420 100644
--- a/chrome/browser/printing/print_dialog_cloud_win.cc
+++ b/chrome/browser/printing/print_dialog_cloud_win.cc
@@ -69,8 +69,7 @@
 
  private:
   // Overridden from content::WebContentsObserver:
-  void DocumentLoadedInFrame(
-      content::RenderFrameHost* render_frame_host) override {
+  void DOMContentLoaded(content::RenderFrameHost* render_frame_host) override {
     GURL url = web_contents()->GetURL();
     if (cloud_devices::IsCloudPrintURL(url)) {
       base::string16 origin = base::UTF8ToUTF16(url.GetOrigin().spec());
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.html b/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.html
index 4ddea63..14512fb 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.html
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.html
@@ -27,7 +27,8 @@
             </div>
             <div id="voice-match-animation-container">
               <div id="voice-match-animation">
-                <cr-lottie animation-url="voice_match_animation.json">
+                <cr-lottie id="voice-match-lottie"
+                    animation-url="voice_match_animation.json">
                 </cr-lottie>
               </div>
             </div>
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.js
index 9c63338..6711c4e 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_voice_match.js
@@ -47,6 +47,7 @@
     chrome.send(
         'login.AssistantOptInFlowScreen.VoiceMatchScreen.userActed',
         ['skip-pressed']);
+    this.$['voice-match-lottie'].setPlay(false);
   },
 
   /**
@@ -147,6 +148,7 @@
       chrome.send(
           'login.AssistantOptInFlowScreen.VoiceMatchScreen.userActed',
           ['voice-match-done']);
+      this.$['voice-match-lottie'].setPlay(false);
     }, this.doneActionDelayMs_);
   },
 
@@ -155,6 +157,7 @@
    */
   onShow: function() {
     chrome.send('login.AssistantOptInFlowScreen.VoiceMatchScreen.screenShown');
+    this.$['voice-match-lottie'].setPlay(true);
     this.$['agree-button'].focus();
     if (loadTimeData.getBoolean('hotwordDspAvailable')) {
       this.$['no-dsp-message'].hidden = true;
diff --git a/chrome/browser/resources/chromeos/crostini_installer/BUILD.gn b/chrome/browser/resources/chromeos/crostini_installer/BUILD.gn
index 2249cc3..b51117c 100644
--- a/chrome/browser/resources/chromeos/crostini_installer/BUILD.gn
+++ b/chrome/browser/resources/chromeos/crostini_installer/BUILD.gn
@@ -3,8 +3,14 @@
 # found in the LICENSE file.
 
 import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/polymer/polymer.gni")
 
 js_type_check("closure_compile") {
+  is_polymer3 = true
+  closure_flags = default_closure_args + [
+                    "js_module_root=../../chrome/browser/resources/chromeos/crostini_installer/",
+                    "js_module_root=./gen/chrome/browser/resources/chromeos/crostini_installer/",
+                  ]
   deps = [
     ":app",
     ":browser_proxy",
@@ -12,14 +18,31 @@
 }
 
 js_library("app") {
+  sources = [
+    "$root_gen_dir/chrome/browser/resources/chromeos/crostini_installer/app.js",
+  ]
   deps = [
     ":browser_proxy",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
+  extra_deps = [ ":app_module" ]
 }
 
 js_library("browser_proxy") {
   deps = [
     "//chrome/browser/ui/webui/chromeos/crostini_installer:mojo_bindings_js_library_for_compile",
-    "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js:cr.m",
+  ]
+}
+
+polymer_modulizer("app") {
+  js_file = "app.js"
+  html_file = "app.html"
+  html_type = "v3-ready"
+}
+
+group("polymer3_elements") {
+  deps = [
+    ":app_module",
   ]
 }
diff --git a/chrome/browser/resources/chromeos/crostini_installer/app.html b/chrome/browser/resources/chromeos/crostini_installer/app.html
index 2248a580..90ffed3 100644
--- a/chrome/browser/resources/chromeos/crostini_installer/app.html
+++ b/chrome/browser/resources/chromeos/crostini_installer/app.html
@@ -1,13 +1,4 @@
-<link rel="import" href="chrome://crostini-installer/browser_proxy.html">
-<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
-
-<dom-module id="crostini-installer-app">
-  <template>
-    <div>
-      Hello, Crostini Installer!!!
-      <cr-button on-click="onInstallButtonTap_">Install</cr-button>
-    </div>
-  </template>
-  <script src="chrome://crostini-installer/app.js"></script>
-</dom-module>
+<div>
+  Hello, Crostini Installer!!!
+  <cr-button on-click="onInstallButtonClick_">Install</cr-button>
+</div>
diff --git a/chrome/browser/resources/chromeos/crostini_installer/app.js b/chrome/browser/resources/chromeos/crostini_installer/app.js
index 1fc0ec7..2a733855 100644
--- a/chrome/browser/resources/chromeos/crostini_installer/app.js
+++ b/chrome/browser/resources/chromeos/crostini_installer/app.js
@@ -2,11 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {BrowserProxy} from './browser_proxy.js';
+
 Polymer({
   is: 'crostini-installer-app',
 
+  _template: html`{__html_template__}`,
+
   /** @override */
   attached: function() {
+    const callbackRouter = BrowserProxy.getInstance().callbackRouter;
     // TODO(lxj)
   },
 
@@ -16,7 +25,7 @@
   },
 
   /** @private */
-  onInstallButtonTap_: function() {
+  onInstallButtonClick_: function() {
     // TODO(lxj)
   },
 });
diff --git a/chrome/browser/resources/chromeos/crostini_installer/browser_proxy.html b/chrome/browser/resources/chromeos/crostini_installer/browser_proxy.html
deleted file mode 100644
index ea784c4e..0000000
--- a/chrome/browser/resources/chromeos/crostini_installer/browser_proxy.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.html">
-<link rel="import" href="chrome://crostini-installer/crostini_installer.mojom.html">
-
-<script src="browser_proxy.js"></script>
diff --git a/chrome/browser/resources/chromeos/crostini_installer/browser_proxy.js b/chrome/browser/resources/chromeos/crostini_installer/browser_proxy.js
index cde666ad..c9a34b3 100644
--- a/chrome/browser/resources/chromeos/crostini_installer/browser_proxy.js
+++ b/chrome/browser/resources/chromeos/crostini_installer/browser_proxy.js
@@ -2,24 +2,25 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-cr.define('crostini_installer', function() {
-  class BrowserProxy {
-    constructor() {
-      /** @type {chromeos.crostiniInstaller.mojom.PageCallbackRouter} */
-      this.callbackRouter =
-          new chromeos.crostiniInstaller.mojom.PageCallbackRouter();
-      /** @type {chromeos.crostiniInstaller.mojom.PageHandlerRemote} */
-      this.handler = new chromeos.crostiniInstaller.mojom.PageHandlerRemote();
+// TODO(lxj): use es6 module when it is ready crbug/1004256
+import './crostini_installer.mojom-lite.js';
 
-      const factory =
-          chromeos.crostiniInstaller.mojom.PageHandlerFactory.getRemote();
-      factory.createPageHandler(
-          this.callbackRouter.$.bindNewPipeAndPassRemote(),
-          this.handler.$.bindNewPipeAndPassReceiver());
-    }
+import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
+
+export class BrowserProxy {
+  constructor() {
+    /** @type {chromeos.crostiniInstaller.mojom.PageCallbackRouter} */
+    this.callbackRouter =
+        new chromeos.crostiniInstaller.mojom.PageCallbackRouter();
+    /** @type {chromeos.crostiniInstaller.mojom.PageHandlerRemote} */
+    this.handler = new chromeos.crostiniInstaller.mojom.PageHandlerRemote();
+
+    const factory =
+        chromeos.crostiniInstaller.mojom.PageHandlerFactory.getRemote();
+    factory.createPageHandler(
+        this.callbackRouter.$.bindNewPipeAndPassRemote(),
+        this.handler.$.bindNewPipeAndPassReceiver());
   }
+}
 
-  cr.addSingletonGetter(BrowserProxy);
-
-  return {BrowserProxy: BrowserProxy};
-});
+addSingletonGetter(BrowserProxy);
diff --git a/chrome/browser/resources/chromeos/crostini_installer/index.html b/chrome/browser/resources/chromeos/crostini_installer/index.html
index 23f7bb8..e72a408 100644
--- a/chrome/browser/resources/chromeos/crostini_installer/index.html
+++ b/chrome/browser/resources/chromeos/crostini_installer/index.html
@@ -2,9 +2,11 @@
 <html dir="$i18n{textdirection}" lang="$i18n{language}">
 <head>
   <meta charset="utf8">
+  <script src="chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js">
+  </script>
+  <script type="module" src="app.js"></script>
 </head>
 <body>
   <crostini-installer-app></crostini-installer-app>
-  <link rel="import" href="chrome://crostini-installer/app.html">
 </body>
 </html>
diff --git a/chrome/browser/resources/chromeos/login/md_login.html b/chrome/browser/resources/chromeos/login/md_login.html
index 7ba5cf9..6eaa8bc 100644
--- a/chrome/browser/resources/chromeos/login/md_login.html
+++ b/chrome/browser/resources/chromeos/login/md_login.html
@@ -68,7 +68,6 @@
 <link rel="stylesheet" href="screen_fatal_error.css">
 <link rel="stylesheet" href="screen_device_disabled.css">
 <link rel="stylesheet" href="screen_active_directory_password_change.css">
-<link rel="stylesheet" href="screen_update_required.css">
 
 <script src="chrome://oobe/keyboard_utils.js"></script>
 <include src="notification_card.html">
diff --git a/chrome/browser/resources/chromeos/login/oobe.html b/chrome/browser/resources/chromeos/login/oobe.html
index 48bfbaa..7f5dde3d 100644
--- a/chrome/browser/resources/chromeos/login/oobe.html
+++ b/chrome/browser/resources/chromeos/login/oobe.html
@@ -74,7 +74,6 @@
 <link rel="stylesheet" href="screen_fatal_error.css">
 <link rel="stylesheet" href="screen_device_disabled.css">
 <link rel="stylesheet" href="screen_active_directory_password_change.css">
-<link rel="stylesheet" href="screen_update_required.css">
 
 <script src="chrome://oobe/keyboard_utils.js"></script>
 
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_autolaunch.js b/chrome/browser/resources/chromeos/login/oobe_screen_autolaunch.js
index c905ac0..11d7cad 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screen_autolaunch.js
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_autolaunch.js
@@ -78,7 +78,7 @@
     confirmAutoLaunchForTesting: function(confirm) {
       var button = confirm ? $('autolaunch-confirm-button') :
                              $('autolaunch-cancel-button');
-      var clickEvent = cr.doc.createEvent('Event');
+      var clickEvent = document.createEvent('Event');
       clickEvent.initEvent('click', true, true);
       button.dispatchEvent(clickEvent);
     }
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_enable_kiosk.js b/chrome/browser/resources/chromeos/login/oobe_screen_enable_kiosk.js
index 1211dbf..195f204 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screen_enable_kiosk.js
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_enable_kiosk.js
@@ -79,7 +79,7 @@
     enableKioskForTesting: function(confirm) {
       var button =
           confirm ? $('kiosk-enable-button') : $('kiosk-cancel-button');
-      var clickEvent = cr.doc.createEvent('Event');
+      var clickEvent = document.createEvent('Event');
       clickEvent.initEvent('click', true, true);
       button.dispatchEvent(clickEvent);
     },
diff --git a/chrome/browser/resources/chromeos/login/screen_update_required.css b/chrome/browser/resources/chromeos/login/screen_update_required.css
deleted file mode 100644
index 086ea95..0000000
--- a/chrome/browser/resources/chromeos/login/screen_update_required.css
+++ /dev/null
@@ -1,7 +0,0 @@
-/* Copyright 2017 The Chromium Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-#update-required {
-  width: 448px; /* Should be the same as #gaia-signin. */
-}
diff --git a/chrome/browser/resources/chromeos/login/screen_update_required.js b/chrome/browser/resources/chromeos/login/screen_update_required.js
index 0495969..647408a 100644
--- a/chrome/browser/resources/chromeos/login/screen_update_required.js
+++ b/chrome/browser/resources/chromeos/login/screen_update_required.js
@@ -7,5 +7,97 @@
  */
 
 login.createScreen('UpdateRequiredScreen', 'update-required', function() {
-  return {};
+  /* Possible UI states of the error screen. */
+  /** @const */ var UI_STATE = {
+    UPDATE_REQUIRED_MESSAGE: 'update-required-message',
+    UPDATE_PROCESS: 'update-process',
+    UPDATE_NEED_PERMISSION: 'update-need-permission',
+    UPDATE_COMPLETED_NEED_REBOOT: 'update-completed-need-reboot',
+    UPDATE_ERROR: 'update-error',
+    EOL: 'eol'
+  };
+
+  // Array of the possible UI states of the screen. Must be in the
+  // same order as UpdateRequiredView::UIState enum values.
+  /** @const */ var UI_STATES = [
+    UI_STATE.UPDATE_REQUIRED_MESSAGE, UI_STATE.UPDATE_PROCESS,
+    UI_STATE.UPDATE_NEED_PERMISSION, UI_STATE.UPDATE_COMPLETED_NEED_REBOOT,
+    UI_STATE.UPDATE_ERROR, UI_STATE.EOL
+  ];
+
+  return {
+    EXTERNAL_API: [
+      'setIsConnected', 'setUpdateProgressUnavailable',
+      'setUpdateProgressValue', 'setUpdateProgressMessage',
+      'setEstimatedTimeLeftVisible', 'setEstimatedTimeLeft', 'setUIState'
+    ],
+
+    /** @param {boolean} connected */
+    setIsConnected: function(connected) {
+      $('update-required-card').isConnected = connected;
+    },
+
+    /**
+     * @param {boolean} unavailable.
+     */
+    setUpdateProgressUnavailable: function(unavailable) {
+      $('update-required-card').updateProgressUnavailable = unavailable;
+    },
+
+    /**
+     * Sets update's progress bar value.
+     * @param {number} progress Percentage of the progress bar.
+     */
+    setUpdateProgressValue: function(progress) {
+      $('update-required-card').updateProgressValue = progress;
+    },
+
+    /**
+     * Sets message below progress bar.
+     * @param {string} message Message that should be shown.
+     */
+    setUpdateProgressMessage: function(message) {
+      $('update-required-card').updateProgressMessage = message;
+    },
+
+    /**
+     * Shows or hides downloading ETA message.
+     * @param {boolean} visible Are ETA message visible?
+     */
+    setEstimatedTimeLeftVisible: function(visible) {
+      $('update-required-card').estimatedTimeLeftVisible = visible;
+    },
+
+    /**
+     * Sets estimated time left until download will complete.
+     * @param {number} seconds Time left in seconds.
+     */
+    setEstimatedTimeLeft: function(seconds) {
+      var minutes = Math.ceil(seconds / 60);
+      var message = '';
+      if (minutes > 60) {
+        message = loadTimeData.getString('downloadingTimeLeftLong');
+      } else if (minutes > 55) {
+        message = loadTimeData.getString('downloadingTimeLeftStatusOneHour');
+      } else if (minutes > 20) {
+        message = loadTimeData.getStringF(
+            'downloadingTimeLeftStatusMinutes', Math.ceil(minutes / 5) * 5);
+      } else if (minutes > 1) {
+        message = loadTimeData.getStringF(
+            'downloadingTimeLeftStatusMinutes', minutes);
+      } else {
+        message = loadTimeData.getString('downloadingTimeLeftSmall');
+      }
+      $('update-required-card').estimatedTimeLeft =
+          loadTimeData.getStringF('downloading', message);
+    },
+
+    /**
+     * Sets current UI state of the screen.
+     * @param {number} ui_state New UI state of the screen.
+     */
+    setUIState: function(ui_state) {
+      $('update-required-card').ui_state = UI_STATES[ui_state];
+    },
+  };
 });
diff --git a/chrome/browser/resources/chromeos/login/update_required_card.css b/chrome/browser/resources/chromeos/login/update_required_card.css
index dccd287..ffc49529 100644
--- a/chrome/browser/resources/chromeos/login/update_required_card.css
+++ b/chrome/browser/resources/chromeos/login/update_required_card.css
@@ -10,3 +10,7 @@
   margin-bottom: 25px;
   margin-top: 20px;
 }
+
+.update-error {
+  font-weight: bold;
+}
diff --git a/chrome/browser/resources/chromeos/login/update_required_card.html b/chrome/browser/resources/chromeos/login/update_required_card.html
index c29558f..f6c2476 100644
--- a/chrome/browser/resources/chromeos/login/update_required_card.html
+++ b/chrome/browser/resources/chromeos/login/update_required_card.html
@@ -11,13 +11,92 @@
   Events: none
 -->
 <dom-module id="update-required-card">
+  <link rel="stylesheet" href="oobe_dialog_host.css">
   <link rel="stylesheet" href="oobe_flex_layout.css">
   <link rel="stylesheet" href="update_required_card.css">
 
   <template>
-    <div class="content">
-      <div class="message" i18n-content="updateRequiredMessage">
+    <oobe-dialog has-buttons hidden="[[showOn_(ui_state,
+        'update-required-message',
+        'update-error')]]">
+      <div slot="subtitle">
+        <div hidden="[[showOn_(ui_state, 'update-error')]]"
+            class="update-error">
+          $i18n{errorMessage}
+        </div>
+        <div>
+          $i18n{updateRequiredMessage}
+        </div>
       </div>
-    </div>
+
+      <div slot="bottom-buttons" class="layout horizontal end-justified">
+        <cr-button disabled="[[selectNetworkDisabled]]"
+            on-click="onSelectNetworkClicked_">
+          $i18n{selectNetworkButtonCaption}
+        </cr-button>
+        <cr-button disabled="[[!isConnected]]" on-click="onUpdateClicked_">
+          $i18n{updateButtonCaption}
+        </cr-button>
+      </div>
+    </oobe-dialog>
+
+    <oobe-dialog hidden="[[showOn_(ui_state, 'update-process')]]" has-buttons>
+      <h1 slot="title" hidden="[[!updateProgressUnavailable]]">
+        $i18n{checkingForUpdatesTitle}
+      </h1>
+      <paper-progress slot="progress" indeterminate
+          hidden="[[!updateProgressUnavailable]]">
+      </paper-progress>
+
+      <h1 slot="title" hidden="[[updateProgressUnavailable]]">
+          $i18n{updatingTitle}
+      </h1>
+      <paper-progress slot="progress" min="0" max="100"
+          value="[[updateProgressValue]]"
+          hidden="[[updateProgressUnavailable]]">
+      </paper-progress>
+
+      <div slot="footer" hidden="[[updateProgressUnavailable]]"
+          class="flex layout vertical">
+        <div id="estimated-time-left" class="progress-message text"
+            hidden="[[!estimatedTimeLeftVisible]]">
+          [[estimatedTimeLeft]]
+        </div>
+        <div id="progress-message" class="progress-message text"
+            hidden="[[!updateProgressMessage]]">
+          [[updateProgressMessage]]
+        </div>
+      </div>
+    </oobe-dialog>
+
+    <oobe-dialog hidden="[[showOn_(ui_state, 'update-need-permission')]]"
+        has-buttons>
+      <h1 slot="title">
+        $i18n{updateOverCellularPromptTitle}
+      </h1>
+      <div slot="subtitle" class="update-subtitle">
+        $i18n{updateOverCellularPromptMessage}
+      </div>
+      <div slot="bottom-buttons" class="layout horizontal end-justified">
+        <cr-button on-click="onCellularPermissionRejected_">
+          $i18n{RejectUpdateOverCellularButton}
+        </cr-button>
+        <cr-button on-click="onCellularPermissionAccepted_">
+          $i18n{AcceptUpdateOverCellularButton}
+        </cr-button>
+      </div>
+    </oobe-dialog>
+
+    <oobe-dialog
+        hidden="[[showOn_(ui_state, 'update-completed-need-reboot', 'eol')]]">
+      <div slot="subtitle">
+        <div hidden="[[showOn_(ui_state, 'update-completed-need-reboot')]]">
+          $i18n{rebootNeededMessage}
+        </div>
+        <div hidden="[[showOn_(ui_state, 'eol')]]">
+          $i18n{eolMessage}
+        </div>
+      </div>
+    </oobe-dialog>
   </template>
 </dom-module>
diff --git a/chrome/browser/resources/chromeos/login/update_required_card.js b/chrome/browser/resources/chromeos/login/update_required_card.js
index ad7e012..74d9fea 100644
--- a/chrome/browser/resources/chromeos/login/update_required_card.js
+++ b/chrome/browser/resources/chromeos/login/update_required_card.js
@@ -4,4 +4,73 @@
 
 Polymer({
   is: 'update-required-card',
+
+  properties: {
+    /**
+     * Is device connected to network?
+     */
+    isConnected: {type: Boolean, value: false},
+
+    updateProgressUnavailable: {type: Boolean, value: true},
+
+    updateProgressValue: {type: Number, value: 0},
+
+    updateProgressMessage: {type: String, value: ''},
+
+    estimatedTimeLeftVisible: {type: Boolean, value: false},
+
+    /**
+     * Message "3 minutes left".
+     */
+    estimatedTimeLeft: {
+      type: String,
+    },
+
+    ui_state: {type: String},
+  },
+
+  /**
+   * @private
+   */
+  onSelectNetworkClicked_: function() {
+    chrome.send('login.UpdateRequiredScreen.userActed', ['select-network']);
+  },
+
+  /**
+   * @private
+   */
+  onUpdateClicked_: function() {
+    chrome.send('login.UpdateRequiredScreen.userActed', ['update']);
+  },
+
+  /**
+   * @private
+   */
+  onFinishClicked_: function() {
+    chrome.send('login.UpdateRequiredScreen.userActed', ['finish']);
+  },
+
+  /**
+   * @private
+   */
+  onCellularPermissionRejected_: function() {
+    chrome.send(
+        'login.UpdateRequiredScreen.userActed', ['update-reject-cellular']);
+  },
+
+  /**
+   * @private
+   */
+  onCellularPermissionAccepted_: function() {
+    chrome.send(
+        'login.UpdateRequiredScreen.userActed', ['update-accept-cellular']);
+  },
+
+  /**
+   * @private
+   */
+  showOn_: function(ui_state) {
+    // Negate the value as it used as |hidden| attribute's value.
+    return !(Array.prototype.slice.call(arguments, 1).includes(ui_state));
+  },
 });
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_images_grid.js b/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_images_grid.js
index 97039df..039c06b 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_images_grid.js
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_images_grid.js
@@ -90,7 +90,7 @@
         return;
       }
 
-      var imageEl = cr.doc.createElement('img');
+      var imageEl = document.createElement('img');
       // Do not show the image until |cropImageToFitGrid_| is done.
       imageEl.style.visibility = 'hidden';
       imageEl.setAttribute('aria-hidden', 'true');
@@ -524,7 +524,7 @@
       // checkmark_ needs to be initialized before set data model. Otherwise, we
       // may try to access checkmark before initialization in
       // updateActiveThumb_().
-      this.checkmark_ = cr.doc.createElement('div');
+      this.checkmark_ = document.createElement('div');
       this.checkmark_.classList.add('check');
       this.checkmark_.setAttribute(
           'aria-label', loadTimeData.getString('setSuccessfullyMessage'));
diff --git a/chrome/browser/resources/identity_internals/identity_internals.js b/chrome/browser/resources/identity_internals/identity_internals.js
index 23cb355..76de89b 100644
--- a/chrome/browser/resources/identity_internals/identity_internals.js
+++ b/chrome/browser/resources/identity_internals/identity_internals.js
@@ -11,7 +11,7 @@
    * @constructor
    */
   function TokenListItem(tokenInfo) {
-    const el = cr.doc.createElement('div');
+    const el = document.createElement('div');
     el.data_ = tokenInfo;
     el.__proto__ = TokenListItem.prototype;
     el.decorate();
diff --git a/chrome/browser/resources/ntp4/apps_page.js b/chrome/browser/resources/ntp4/apps_page.js
index 026b458..460883b5 100644
--- a/chrome/browser/resources/ntp4/apps_page.js
+++ b/chrome/browser/resources/ntp4/apps_page.js
@@ -105,7 +105,7 @@
      */
     appendMenuItem_: function(opt_textId) {
       const button =
-          /** @type {!HTMLButtonElement} */ (cr.doc.createElement('button'));
+          /** @type {!HTMLButtonElement} */ (document.createElement('button'));
       this.menu.appendChild(button);
       cr.ui.decorate(button, cr.ui.MenuItem);
       if (opt_textId) {
@@ -248,7 +248,7 @@
    * @extends {HTMLDivElement}
    */
   function App(appData) {
-    const el = cr.doc.createElement('div');
+    const el = /** @type {!App} */ (document.createElement('div'));
     el.__proto__ = App.prototype;
     el.initialize(appData);
 
diff --git a/chrome/browser/resources/ntp4/incognito_tab.js b/chrome/browser/resources/ntp4/incognito_tab.js
index bca80d5..03fa5b3 100644
--- a/chrome/browser/resources/ntp4/incognito_tab.js
+++ b/chrome/browser/resources/ntp4/incognito_tab.js
@@ -3,6 +3,12 @@
 // found in the LICENSE file.
 
 window.addEventListener('load', function() {
+  cr.addWebUIListener('theme-changed', themeData => {
+    document.documentElement.setAttribute(
+        'hascustombackground', themeData.hasCustomBackground);
+    $('incognitothemecss').href =
+        'chrome://theme/css/incognito_new_tab_theme.css?' + Date.now();
+  });
   chrome.send('observeThemeChanges');
 });
 
@@ -13,12 +19,4 @@
   setBookmarkBarAttached: function(attached) {
     document.documentElement.setAttribute('bookmarkbarattached', attached);
   },
-
-  /** @param {!{hasCustomBackground: boolean}} themeData */
-  themeChanged: function(themeData) {
-    document.documentElement.setAttribute(
-        'hascustombackground', themeData.hasCustomBackground);
-    $('incognitothemecss').href =
-        'chrome://theme/css/incognito_new_tab_theme.css?' + Date.now();
-  },
 };
diff --git a/chrome/browser/resources/ntp4/nav_dot.js b/chrome/browser/resources/ntp4/nav_dot.js
index 22109133..8193fe1 100644
--- a/chrome/browser/resources/ntp4/nav_dot.js
+++ b/chrome/browser/resources/ntp4/nav_dot.js
@@ -22,7 +22,7 @@
    * @implements {cr.ui.DragWrapperDelegate}
    */
   function NavDot(page, title, titleIsEditable, animate) {
-    const dot = cr.doc.createElement('li');
+    const dot = /** @type {!NavDot} */ (document.createElement('li'));
     dot.__proto__ = NavDot.prototype;
     dot.initialize(page, title, titleIsEditable, animate);
 
diff --git a/chrome/browser/resources/ntp4/new_tab.js b/chrome/browser/resources/ntp4/new_tab.js
index 0be7cd9..d6f7b96 100644
--- a/chrome/browser/resources/ntp4/new_tab.js
+++ b/chrome/browser/resources/ntp4/new_tab.js
@@ -108,6 +108,10 @@
 
       startTime = Date.now();
 
+      cr.addWebUIListener('theme-changed', () => {
+        $('themecss').href =
+            'chrome://theme/css/new_tab_theme.css?' + Date.now();
+      });
       chrome.send('observeThemeChanges');
     });
   }
@@ -207,15 +211,6 @@
     }
   }
 
-  /**
-   * Called when the theme has changed.
-   * @param {Object=} opt_themeData Not used; only exists to match equivalent
-   *     function in incognito NTP.
-   */
-  function themeChanged(opt_themeData) {
-    $('themecss').href = 'chrome://theme/css/new_tab_theme.css?' + Date.now();
-  }
-
   function setBookmarkBarAttached(attached) {
     document.documentElement.setAttribute('bookmarkbarattached', attached);
   }
@@ -390,7 +385,6 @@
     setAppToBeHighlighted: setAppToBeHighlighted,
     setBookmarkBarAttached: setBookmarkBarAttached,
     setFaviconDominantColor: setFaviconDominantColor,
-    themeChanged: themeChanged,
     updateLogin: updateLogin
   };
 });
diff --git a/chrome/browser/resources/ntp4/tile_page.js b/chrome/browser/resources/ntp4/tile_page.js
index f6de15a..600fc1f 100644
--- a/chrome/browser/resources/ntp4/tile_page.js
+++ b/chrome/browser/resources/ntp4/tile_page.js
@@ -41,7 +41,7 @@
    * @extends {HTMLDivElement}
    */
   function Tile(contents) {
-    const tile = cr.doc.createElement('div');
+    const tile = /** @type {!Tile} */ (document.createElement('div'));
     tile.__proto__ = Tile.prototype;
     tile.initialize(contents);
 
@@ -372,7 +372,7 @@
    * @implements {cr.ui.DragWrapperDelegate}
    */
   function TilePage(gridValues) {
-    const el = cr.doc.createElement('div');
+    const el = /** @type {!TilePage} */ (document.createElement('div'));
     el.gridValues_ = gridValues;
     el.__proto__ = TilePage.prototype;
     el.initialize();
diff --git a/chrome/browser/resources/quota_internals/event_handler.js b/chrome/browser/resources/quota_internals/event_handler.js
index ef5d3c1..e4594b73 100644
--- a/chrome/browser/resources/quota_internals/event_handler.js
+++ b/chrome/browser/resources/quota_internals/event_handler.js
@@ -378,7 +378,7 @@
   for (const key in data) {
     let entry = statistics[key];
     if (!entry) {
-      entry = cr.doc.createElement('tr');
+      entry = document.createElement('tr');
       $('stat-entries').appendChild(entry);
       statistics[key] = entry;
     }
@@ -417,7 +417,7 @@
 
       const normalize = keyAndLabel[i][2] || stringToText_;
 
-      const row = cr.doc.createElement('tr');
+      const row = document.createElement('tr');
       row.innerHTML = '<td>' + label + '</td>' +
           '<td>' + normalize(entry) + '</td>';
       tbody.appendChild(row);
@@ -493,5 +493,5 @@
   $('dump-button').addEventListener('click', dump, false);
 }
 
-cr.doc.addEventListener('DOMContentLoaded', onLoad, false);
+document.addEventListener('DOMContentLoaded', onLoad, false);
 })();
diff --git a/chrome/browser/resources/quota_internals/message_dispatcher.js b/chrome/browser/resources/quota_internals/message_dispatcher.js
index b1024b6..a387094 100644
--- a/chrome/browser/resources/quota_internals/message_dispatcher.js
+++ b/chrome/browser/resources/quota_internals/message_dispatcher.js
@@ -63,7 +63,7 @@
         break;
     }
     if (target) {
-      const event = cr.doc.createEvent('CustomEvent');
+      const event = document.createEvent('CustomEvent');
       event.initCustomEvent('update', false, false, detail);
       target.dispatchEvent(event);
     }
diff --git a/chrome/browser/resources/sandbox_internals/sandbox_internals.js b/chrome/browser/resources/sandbox_internals/sandbox_internals.js
index ab15fb205..336d9799 100644
--- a/chrome/browser/resources/sandbox_internals/sandbox_internals.js
+++ b/chrome/browser/resources/sandbox_internals/sandbox_internals.js
@@ -22,10 +22,10 @@
  * @return {Element} The newly added TR.
  */
 function addStatusRow(name, value, cssClass) {
-  const row = cr.doc.createElement('tr');
+  const row = document.createElement('tr');
 
-  const nameCol = row.appendChild(cr.doc.createElement('td'));
-  const valueCol = row.appendChild(cr.doc.createElement('td'));
+  const nameCol = row.appendChild(document.createElement('td'));
+  const valueCol = row.appendChild(document.createElement('td'));
 
   nameCol.textContent = name;
   valueCol.textContent = value;
diff --git a/chrome/browser/resources/settings/device_page/BUILD.gn b/chrome/browser/resources/settings/device_page/BUILD.gn
index d7d0c1e..1826cc4 100644
--- a/chrome/browser/resources/settings/device_page/BUILD.gn
+++ b/chrome/browser/resources/settings/device_page/BUILD.gn
@@ -12,7 +12,6 @@
     ":display_layout",
     ":display_overscan_dialog",
     ":drag_behavior",
-    ":drive_cache_dialog",
     ":keyboard",
     ":layout_behavior",
     ":night_light_slider",
@@ -148,10 +147,3 @@
     "//ui/webui/resources/js/cr/ui:focus_without_ink",
   ]
 }
-
-js_library("drive_cache_dialog") {
-  deps = [
-    "//ui/webui/resources/js:i18n_behavior",
-  ]
-  externs_list = [ "$externs_path/chrome_send.js" ]
-}
diff --git a/chrome/browser/resources/settings/device_page/drive_cache_dialog.html b/chrome/browser/resources/settings/device_page/drive_cache_dialog.html
deleted file mode 100644
index 00e43a4..0000000
--- a/chrome/browser/resources/settings/device_page/drive_cache_dialog.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="../settings_shared_css.html">
-
-<dom-module id="settings-drive-cache-dialog">
-  <template>
-    <style include="settings-shared"></style>
-    <cr-dialog id="dialog" close-text="$i18n{close}">
-      <div slot="title">
-        $i18n{storageClearDriveCacheDialogTitle}
-      </div>
-      <div slot="body">
-        <span>$i18n{storageClearDriveCacheDialogDescription}</span>
-      </div>
-      <div slot="button-container">
-        <cr-button id="cancelButton" class="cancel-button"
-            on-click="onCancelTap_">
-          $i18n{cancel}
-        </cr-button>
-        <cr-button id="deleteButton" class="action-button"
-            on-click="onDeleteTap_">
-          $i18n{storageDeleteAllButtonTitle}
-        </cr-button>
-      </div>
-    </cr-dialog>
-  </template>
-  <script src="drive_cache_dialog.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/settings/device_page/drive_cache_dialog.js b/chrome/browser/resources/settings/device_page/drive_cache_dialog.js
deleted file mode 100644
index fd63087..0000000
--- a/chrome/browser/resources/settings/device_page/drive_cache_dialog.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview
- * 'settings-drive-cache-dialog' is the dialog to delete Google Drive temporary
- * offline files.
- */
-Polymer({
-  is: 'settings-drive-cache-dialog',
-
-  behaviors: [
-    I18nBehavior,
-  ],
-
-  open: function() {
-    this.$.dialog.showModal();
-  },
-
-  /** @private */
-  onCancelTap_: function() {
-    this.$.dialog.cancel();
-  },
-
-  /** @private */
-  onDeleteTap_: function() {
-    chrome.send('clearDriveCache');
-    this.$.dialog.close();
-  },
-});
diff --git a/chrome/browser/resources/settings/device_page/storage.html b/chrome/browser/resources/settings/device_page/storage.html
index f0a518e..6526d4f1f 100644
--- a/chrome/browser/resources/settings/device_page/storage.html
+++ b/chrome/browser/resources/settings/device_page/storage.html
@@ -8,7 +8,6 @@
 <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="drive_cache_dialog.html">
 <link rel="import" href="storage_external.html">
 <link rel="import" href="../prefs/prefs.html">
 <link rel="import" href="../route.html">
@@ -201,20 +200,6 @@
     <cr-link-row id="downloadsSize" class="hr" on-click="onDownloadsTap_"
         label="$i18n{storageItemDownloads}"
         sub-label="$i18n{storageSizeComputing}" external></cr-link-row>
-    <template is="dom-if" if="[[driveEnabled_]]">
-      <div class="settings-box two-line" on-click="onDriveCacheTap_"
-          actionable$="[[hasDriveCache_]]" >
-        <div class="start">
-          $i18n{storageItemDriveCache}
-          <div id="driveCacheSize" class="secondary">
-            $i18n{storageSizeComputing}
-          </div>
-        </div>
-        <cr-icon-button class="icon-delete-gray" id="deleteButton"
-            aria-label="$i18n{storageItemDriveCache}"
-            aria-describedby="driveSizeCache"></cr-icon-button>
-      </div>
-    </template>
     <cr-link-row id="browsingDataSize" class="hr" on-click="onBrowsingDataTap_"
         label="$i18n{storageItemBrowsingData}"
         sub-label="$i18n{storageSizeComputing}" external></cr-link-row>
@@ -238,10 +223,6 @@
           on-click="onExternalStoragePreferencesTap_"
           label="$i18n{storageExternal}"></cr-link-row>
     </template>
-
-    <settings-drive-cache-dialog id="storageDriveCache"
-        on-close="onCloseDriveCacheDialog_">
-    </settings-drive-cache-dialog>
   </template>
   <script src="storage.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/device_page/storage.js b/chrome/browser/resources/settings/device_page/storage.js
index 2717462..ae7598fd 100644
--- a/chrome/browser/resources/settings/device_page/storage.js
+++ b/chrome/browser/resources/settings/device_page/storage.js
@@ -37,12 +37,6 @@
   behaviors: [settings.RouteObserverBehavior, WebUIListenerBehavior],
 
   properties: {
-    /** @private */
-    driveEnabled_: {
-      type: Boolean,
-      value: false,
-    },
-
     androidEnabled: Boolean,
 
     /** @private */
@@ -68,12 +62,6 @@
       }
     },
 
-    /** @private */
-    hasDriveCache_: {
-      type: Boolean,
-      value: false,
-    },
-
     /** @private {settings.StorageSizeStat} */
     sizeStat_: Object,
   },
@@ -94,9 +82,6 @@
         'storage-downloads-size-changed',
         this.handleDownloadsSizeChanged_.bind(this));
     this.addWebUIListener(
-        'storage-drive-cache-size-changed',
-        this.handleDriveCacheSizeChanged_.bind(this));
-    this.addWebUIListener(
         'storage-browsing-data-size-changed',
         this.handleBrowsingDataSizeChanged_.bind(this));
     this.addWebUIListener(
@@ -111,9 +96,6 @@
           this.handleOtherUsersSizeChanged_.bind(this));
     }
     this.addWebUIListener(
-        'storage-drive-enabled-changed',
-        this.handleDriveEnabledChanged_.bind(this));
-    this.addWebUIListener(
         'storage-android-running-changed',
         this.handleAndroidRunningChanged_.bind(this));
   },
@@ -146,18 +128,6 @@
   },
 
   /**
-   * Handler for tapping the "Offline files" item.
-   * @param {!Event} e
-   * @private
-   */
-  onDriveCacheTap_: function(e) {
-    e.preventDefault();
-    if (this.hasDriveCache_) {
-      this.$.storageDriveCache.open();
-    }
-  },
-
-  /**
    * Handler for tapping the "Browsing data" item.
    * @private
    */
@@ -221,19 +191,6 @@
   },
 
   /**
-   * @param {string} size Formatted string representing the size of Offline
-   *     files.
-   * @param {boolean} hasCache True if the device has at least one offline file.
-   * @private
-   */
-  handleDriveCacheSizeChanged_: function(size, hasCache) {
-    if (this.driveEnabled_) {
-      this.$$('#driveCacheSize').textContent = size;
-      this.hasDriveCache_ = hasCache;
-    }
-  },
-
-  /**
    * @param {string} size Formatted string representing the size of Browsing
    *     data.
    * @private
@@ -275,14 +232,6 @@
   },
 
   /**
-   * @param {boolean} enabled True if Google Drive is enabled.
-   * @private
-   */
-  handleDriveEnabledChanged_: function(enabled) {
-    this.driveEnabled_ = enabled;
-  },
-
-  /**
    * @param {boolean} running True if Android (ARC) is running.
    * @private
    */
@@ -362,9 +311,4 @@
         return '';
     }
   },
-
-  /** @private */
-  onCloseDriveCacheDialog_: function() {
-    cr.ui.focusWithoutInk(assert(this.$$('#deleteButton')));
-  },
 });
diff --git a/chrome/browser/resources/settings/internet_page/internet_detail_page.js b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
index d330382..4dd3706 100644
--- a/chrome/browser/resources/settings/internet_page/internet_detail_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
@@ -519,15 +519,15 @@
     switch (networkState.type) {
       case mojom.NetworkType.kCellular:
         managedProperties.cellular.signalStrength =
-            networkState.cellular.signalStrength;
+            networkState.typeState.cellular.signalStrength;
         break;
       case mojom.NetworkType.kTether:
         managedProperties.tether.signalStrength =
-            networkState.tether.signalStrength;
+            networkState.typeState.tether.signalStrength;
         break;
       case mojom.NetworkType.kWiFi:
         managedProperties.wifi.signalStrength =
-            networkState.wifi.signalStrength;
+            networkState.typeState.wifi.signalStrength;
         break;
     }
     this.managedProperties_ = managedProperties;
diff --git a/chrome/browser/resources/settings/internet_page/internet_page.js b/chrome/browser/resources/settings/internet_page/internet_page.js
index bd6463d0..8f2b2d7 100644
--- a/chrome/browser/resources/settings/internet_page/internet_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_page.js
@@ -507,7 +507,7 @@
 
     if (!event.detail.bypassConnectionDialog &&
         type == mojom.NetworkType.kTether &&
-        !networkState.tether.hasConnectedToHost) {
+        !networkState.typeState.tether.hasConnectedToHost) {
       const params = new URLSearchParams;
       params.append('guid', networkState.guid);
       params.append('type', OncMojo.getNetworkTypeString(type));
diff --git a/chrome/browser/resources/settings/internet_page/internet_subpage.js b/chrome/browser/resources/settings/internet_page/internet_subpage.js
index c3fb9596..279d651 100644
--- a/chrome/browser/resources/settings/internet_page/internet_subpage.js
+++ b/chrome/browser/resources/settings/internet_page/internet_subpage.js
@@ -309,7 +309,7 @@
       const thirdPartyVpns = {};
       networkStates.forEach(state => {
         assert(state.type == mojom.NetworkType.kVPN);
-        switch (state.vpn.type) {
+        switch (state.typeState.vpn.type) {
           case mojom.VpnType.kL2TPIPsec:
           case mojom.VpnType.kOpenVPN:
             builtinNetworkStates.push(state);
@@ -321,7 +321,7 @@
             }
             // Otherwise Arc VPNs are treated the same as Extension VPNs.
           case mojom.VpnType.kExtension:
-            const providerId = state.vpn.providerId;
+            const providerId = state.typeState.vpn.providerId;
             thirdPartyVpns[providerId] = thirdPartyVpns[providerId] || [];
             thirdPartyVpns[providerId].push(state);
             break;
@@ -349,7 +349,7 @@
     for (const vpnList of Object.values(thirdPartyVpns)) {
       assert(vpnList.length > 0);
       // All vpns in the list will have the same type and provider id.
-      const vpn = vpnList[0].vpn;
+      const vpn = vpnList[0].typeState.vpn;
       const provider = {
         type: vpn.type,
         providerId: vpn.providerId,
@@ -579,7 +579,8 @@
         (!!this.globalPolicy.allowOnlyPolicyNetworksToConnectIfAvailable &&
          !!this.deviceState && !!this.deviceState.managedNetworkAvailable) ||
         (!!this.globalPolicy.blockedHexSsids &&
-         this.globalPolicy.blockedHexSsids.includes(state.wifi.hexSsid));
+         this.globalPolicy.blockedHexSsids.includes(
+             state.typeState.wifi.hexSsid));
   },
 
   /**
diff --git a/chrome/browser/resources/settings/os_settings_resources.grd b/chrome/browser/resources/settings/os_settings_resources.grd
index e163278..1e37d143d 100644
--- a/chrome/browser/resources/settings/os_settings_resources.grd
+++ b/chrome/browser/resources/settings/os_settings_resources.grd
@@ -523,12 +523,6 @@
       <structure name="IDR_OS_SETTINGS_DEVICE_DISPLAY_OVERSCAN_DIALOG_JS"
                  file="device_page/display_overscan_dialog.js"
                  type="chrome_html" />
-      <structure name="IDR_OS_SETTINGS_DEVICE_DRIVE_CACHE_DIALOG_HTML"
-                 file="device_page/drive_cache_dialog.html"
-                 type="chrome_html" />
-      <structure name="IDR_OS_SETTINGS_DEVICE_DRIVE_CACHE_DIALOG_JS"
-                 file="device_page/drive_cache_dialog.js"
-                 type="chrome_html" />
       <structure name="IDR_OS_SETTINGS_DEVICE_KEYBOARD_HTML"
                  file="device_page/keyboard.html"
                  type="chrome_html" />
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index 14f631a..fc7e730 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -563,12 +563,6 @@
         <structure name="IDR_SETTINGS_DEVICE_DISPLAY_OVERSCAN_DIALOG_JS"
                    file="device_page/display_overscan_dialog.js"
                    type="chrome_html" />
-        <structure name="IDR_SETTINGS_DEVICE_DRIVE_CACHE_DIALOG_HTML"
-                   file="device_page/drive_cache_dialog.html"
-                   type="chrome_html" />
-        <structure name="IDR_SETTINGS_DEVICE_DRIVE_CACHE_DIALOG_JS"
-                   file="device_page/drive_cache_dialog.js"
-                   type="chrome_html" />
         <structure name="IDR_SETTINGS_DEVICE_KEYBOARD_HTML"
                    file="device_page/keyboard.html"
                    type="chrome_html" />
diff --git a/chrome/browser/supervised_user/supervised_user_navigation_observer.cc b/chrome/browser/supervised_user/supervised_user_navigation_observer.cc
index aa033d3..ad665620 100644
--- a/chrome/browser/supervised_user/supervised_user_navigation_observer.cc
+++ b/chrome/browser/supervised_user/supervised_user_navigation_observer.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/supervised_user/supervised_user_navigation_throttle.h"
 #include "chrome/browser/supervised_user/supervised_user_service.h"
 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
+#include "chrome/browser/supervised_user/supervised_user_url_filter.h"
 #include "chrome/browser/tab_contents/tab_util.h"
 #include "components/history/content/browser/history_context_helper.h"
 #include "components/history/core/browser/history_service.h"
@@ -60,6 +61,13 @@
                                                 callback);
 }
 
+void SupervisedUserNavigationObserver::UpdateMainFrameFilteringStatus(
+    SupervisedUserURLFilter::FilteringBehavior behavior,
+    supervised_user_error_page::FilteringBehaviorReason reason) {
+  main_frame_filtering_behavior_ = behavior;
+  main_frame_filtering_behavior_reason_ = reason;
+}
+
 void SupervisedUserNavigationObserver::DidFinishNavigation(
       content::NavigationHandle* navigation_handle) {
   // If this is a different navigation than the one that triggered the
diff --git a/chrome/browser/supervised_user/supervised_user_navigation_observer.h b/chrome/browser/supervised_user/supervised_user_navigation_observer.h
index 376e872..0b3513e 100644
--- a/chrome/browser/supervised_user/supervised_user_navigation_observer.h
+++ b/chrome/browser/supervised_user/supervised_user_navigation_observer.h
@@ -49,6 +49,20 @@
       const base::Callback<
           void(SupervisedUserNavigationThrottle::CallbackActions)>& callback);
 
+  void UpdateMainFrameFilteringStatus(
+      SupervisedUserURLFilter::FilteringBehavior behavior,
+      supervised_user_error_page::FilteringBehaviorReason reason);
+
+  SupervisedUserURLFilter::FilteringBehavior main_frame_filtering_behavior()
+      const {
+    return main_frame_filtering_behavior_;
+  }
+
+  supervised_user_error_page::FilteringBehaviorReason
+  main_frame_filtering_behavior_reason() const {
+    return main_frame_filtering_behavior_reason_;
+  }
+
   // WebContentsObserver implementation.
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
@@ -100,7 +114,13 @@
   SupervisedUserService* supervised_user_service_;
 
   // Navigation ID of the navigation that triggered the last interstitial.
-  int64_t interstitial_navigation_id_;
+  int64_t interstitial_navigation_id_ = -1;
+
+  SupervisedUserURLFilter::FilteringBehavior main_frame_filtering_behavior_ =
+      SupervisedUserURLFilter::FilteringBehavior::ALLOW;
+  supervised_user_error_page::FilteringBehaviorReason
+      main_frame_filtering_behavior_reason_ =
+          supervised_user_error_page::FilteringBehaviorReason::DEFAULT;
 
   std::vector<std::unique_ptr<const sessions::SerializedNavigationEntry>>
       blocked_navigations_;
diff --git a/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc b/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc
index 7acb3f9..5b4a62f 100644
--- a/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc
+++ b/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc
@@ -115,18 +115,45 @@
     base::UmaHistogramSparse("ManagedUsers.FilteringResult", value);
 }
 
+bool IsMainFrameWhitelisted(content::WebContents* web_contents) {
+  auto* navigation_observer =
+      SupervisedUserNavigationObserver::FromWebContents(web_contents);
+  if (!navigation_observer)
+    return false;
+  auto behavior = navigation_observer->main_frame_filtering_behavior();
+  auto reason = navigation_observer->main_frame_filtering_behavior_reason();
+  bool is_allowed =
+      behavior == SupervisedUserURLFilter::FilteringBehavior::ALLOW;
+  bool is_whitelisted =
+      reason == supervised_user_error_page::FilteringBehaviorReason::WHITELIST;
+
+  return is_allowed && is_whitelisted;
+}
+
 }  // namespace
 
 // static
 std::unique_ptr<SupervisedUserNavigationThrottle>
 SupervisedUserNavigationThrottle::MaybeCreateThrottleFor(
     content::NavigationHandle* navigation_handle) {
-  if (!navigation_handle->IsInMainFrame())
-    return nullptr;
   Profile* profile = Profile::FromBrowserContext(
       navigation_handle->GetWebContents()->GetBrowserContext());
+
   if (!profile->IsSupervised())
     return nullptr;
+
+  if (!navigation_handle->IsInMainFrame()) {
+    SupervisedUserService* service =
+        SupervisedUserServiceFactory::GetForProfile(profile);
+    if (!service->IsSupervisedUserIframeFilterEnabled())
+      return nullptr;
+
+    // If the url in the main main frame has already been whitelisted by
+    // parents, then don't create the throttle for the subframe.
+    if (IsMainFrameWhitelisted(navigation_handle->GetWebContents()))
+      return nullptr;
+  }
+
   // Can't use std::make_unique because the constructor is private.
   return base::WrapUnique(
       new SupervisedUserNavigationThrottle(navigation_handle));
@@ -227,6 +254,15 @@
     RecordFilterResultEvent(true, behavior, reason, uncertain, transition);
   }
 
+  if (navigation_handle()->IsInMainFrame()) {
+    // Update navigation observer about the navigation state of the main frame.
+    auto* navigation_observer =
+        SupervisedUserNavigationObserver::FromWebContents(
+            navigation_handle()->GetWebContents());
+    if (navigation_observer)
+      navigation_observer->UpdateMainFrameFilteringStatus(behavior, reason);
+  }
+
   if (behavior == SupervisedUserURLFilter::BLOCK)
     ShowInterstitial(url, reason);
   else if (deferred_)
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 359e0693..79eca57 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1957,7 +1957,6 @@
       "//components/arc",
       "//components/captive_portal",
       "//components/consent_auditor:consent_auditor",
-      "//components/drive:drive_chromeos",
       "//components/exo",
       "//components/login",
       "//components/session_manager/core",
diff --git a/chrome/browser/ui/app_list/app_service/app_service_app_icon_loader.cc b/chrome/browser/ui/app_list/app_service/app_service_app_icon_loader.cc
index dbdd4317..65c5f32 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_app_icon_loader.cc
+++ b/chrome/browser/ui/app_list/app_service/app_service_app_icon_loader.cc
@@ -90,9 +90,8 @@
   if (base::StartsWith(app_id, crostini::kCrostiniAppIdPrefix,
                        base::CompareCase::SENSITIVE)) {
     apps::mojom::IconKeyPtr icon_key = apps::mojom::IconKey::New();
-    icon_key->resource_id = apps::mojom::IconKey::kInvalidResourceId;
     proxy->LoadIconFromIconKey(
-        apps::mojom::AppType::kCrostini, app_id, std::move(icon_key),
+        apps::mojom::AppType::kCrostini, std::string(), std::move(icon_key),
         apps::mojom::IconCompression::kUncompressed, icon_size_in_dip(),
         allow_placeholder_icon,
         base::BindOnce(&AppServiceAppIconLoader::OnLoadIcon,
diff --git a/chrome/browser/ui/page_info/page_info_ui.cc b/chrome/browser/ui/page_info/page_info_ui.cc
index e56bb96302..419d083b 100644
--- a/chrome/browser/ui/page_info/page_info_ui.cc
+++ b/chrome/browser/ui/page_info/page_info_ui.cc
@@ -281,12 +281,13 @@
 
   switch (identity_info.safety_tip_status) {
     case security_state::SafetyTipStatus::kBadReputation:
-    case security_state::SafetyTipStatus::kLookalike:
-      // TODO(jdeblasio): The BAD_REPUTATION string is generic enough to use for
-      // lookalikes too, but it probably deserves its own string.
       return CreateSecurityDescription(
-          SecuritySummaryColor::RED, IDS_PAGE_INFO_SAFETY_TIP_SUMMARY,
+          SecuritySummaryColor::RED,
+          IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_TITLE,
           IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_DESCRIPTION);
+    case security_state::SafetyTipStatus::kLookalike:
+      // Lookalikes have their own strings, but they're suggestions, not
+      // warnings, so we leave Page Info alone.
     case security_state::SafetyTipStatus::kNone:
     case security_state::SafetyTipStatus::kUnknown:
       break;
diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc
index bb0340e3..bc8bcbb 100644
--- a/chrome/browser/ui/search/search_tab_helper.cc
+++ b/chrome/browser/ui/search/search_tab_helper.cc
@@ -583,7 +583,8 @@
                     AutocompleteProvider::TYPE_HISTORY_QUICK |
                     AutocompleteProvider::TYPE_HISTORY_URL |
                     AutocompleteProvider::TYPE_SEARCH |
-                    AutocompleteProvider::TYPE_ZERO_SUGGEST;
+                    AutocompleteProvider::TYPE_ZERO_SUGGEST |
+                    AutocompleteProvider::TYPE_ZERO_SUGGEST_LOCAL_HISTORY;
     autocomplete_controller_ = std::make_unique<AutocompleteController>(
         std::make_unique<ChromeAutocompleteProviderClient>(profile()), this,
         providers);
diff --git a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view.cc
index 0f72622..4a04884 100644
--- a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view.cc
+++ b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view.cc
@@ -26,6 +26,8 @@
 #include "ui/views/window/dialog_client_view.h"
 #include "url/gurl.h"
 
+using security_state::SafetyTipStatus;
+
 SafetyTipPageInfoBubbleView::SafetyTipPageInfoBubbleView(
     views::View* anchor_view,
     const gfx::Rect& anchor_rect,
@@ -33,20 +35,23 @@
     content::WebContents* web_contents,
     security_state::SafetyTipStatus safety_tip_status,
     const GURL& url,
+    const GURL& suggested_url,
     base::OnceCallback<void(safety_tips::SafetyTipInteraction)> close_callback)
     : PageInfoBubbleViewBase(anchor_view,
                              anchor_rect,
                              parent_window,
                              PageInfoBubbleViewBase::BUBBLE_SAFETY_TIP,
                              web_contents),
+      safety_tip_status_(safety_tip_status),
       url_(url),
+      suggested_url_(suggested_url),
       close_callback_(std::move(close_callback)) {
   // Keep the bubble open until explicitly closed (or we navigate away, a tab is
   // created over it, etc).
   set_close_on_deactivate(false);
 
-  const base::string16 title_text = l10n_util::GetStringUTF16(
-      safety_tips::GetSafetyTipTitleId(safety_tip_status));
+  const base::string16 title_text =
+      safety_tips::GetSafetyTipTitle(safety_tip_status, suggested_url);
   set_window_title(title_text);
 
   views::BubbleDialogDelegateView::CreateBubble(this);
@@ -98,7 +103,8 @@
   std::unique_ptr<views::Button> button(
       views::MdTextButton::CreateSecondaryUiBlueButton(
           this,
-          l10n_util::GetStringUTF16(IDS_PAGE_INFO_SAFETY_TIP_LEAVE_BUTTON)));
+          l10n_util::GetStringUTF16(
+              safety_tips::GetSafetyTipLeaveButtonId(safety_tip_status))));
   button->SetID(PageInfoBubbleView::VIEW_ID_PAGE_INFO_BUTTON_LEAVE_SITE);
   leave_button_ =
       layout->AddView(std::move(button), 1, 1, views::GridLayout::TRAILING,
@@ -153,7 +159,10 @@
   switch (button->GetID()) {
     case PageInfoBubbleView::VIEW_ID_PAGE_INFO_BUTTON_LEAVE_SITE:
       action_taken_ = safety_tips::SafetyTipInteraction::kLeaveSite;
-      safety_tips::LeaveSite(web_contents());
+      auto url = safety_tip_status_ == SafetyTipStatus::kLookalike
+                     ? suggested_url_
+                     : GURL(safety_tips::kSafeUrl);
+      safety_tips::LeaveSite(web_contents(), url);
       return;
   }
   NOTREACHED();
@@ -165,6 +174,7 @@
     content::WebContents* web_contents,
     security_state::SafetyTipStatus safety_tip_status,
     const GURL& virtual_url,
+    const GURL& suggested_url,
     base::OnceCallback<void(SafetyTipInteraction)> close_callback) {
   Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
   if (!browser)
@@ -182,7 +192,7 @@
 
   views::BubbleDialogDelegateView* bubble = new SafetyTipPageInfoBubbleView(
       configuration.anchor_view, anchor_rect, parent_view, web_contents,
-      safety_tip_status, virtual_url, std::move(close_callback));
+      safety_tip_status, virtual_url, suggested_url, std::move(close_callback));
 
   bubble->SetHighlightedButton(configuration.highlighted_button);
   bubble->SetArrow(configuration.bubble_arrow);
@@ -196,9 +206,10 @@
     content::WebContents* web_contents,
     security_state::SafetyTipStatus safety_tip_status,
     const GURL& virtual_url,
+    const GURL& suggested_url,
     base::OnceCallback<void(safety_tips::SafetyTipInteraction)>
         close_callback) {
   return new SafetyTipPageInfoBubbleView(
       nullptr, gfx::Rect(), parent_view, web_contents, safety_tip_status,
-      virtual_url, std::move(close_callback));
+      virtual_url, suggested_url, std::move(close_callback));
 }
diff --git a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view.h b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view.h
index 70e0220a..69e0fb9 100644
--- a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view.h
+++ b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view.h
@@ -42,6 +42,7 @@
       content::WebContents* web_contents,
       security_state::SafetyTipStatus safety_tip_status,
       const GURL& url,
+      const GURL& suggested_url,
       base::OnceCallback<void(safety_tips::SafetyTipInteraction)>
           close_callback);
   ~SafetyTipPageInfoBubbleView() override;
@@ -57,7 +58,15 @@
 
   views::Button* GetLeaveButtonForTesting() { return leave_button_; }
 
+  const security_state::SafetyTipStatus safety_tip_status_;
+
+  // The URL of the page on which the Safety Tip was triggered.
   const GURL url_;
+
+  // The URL of the page the Safety Tip suggests you intended to go to, when
+  // applicable (for SafetyTipStatus::kLookalike).
+  const GURL suggested_url_;
+
   views::Button* leave_button_;
   base::OnceCallback<void(safety_tips::SafetyTipInteraction)> close_callback_;
   safety_tips::SafetyTipInteraction action_taken_ =
@@ -72,6 +81,7 @@
     content::WebContents* web_contents,
     security_state::SafetyTipStatus safety_tip_status,
     const GURL& virtual_url,
+    const GURL& suggested_url,
     base::OnceCallback<void(safety_tips::SafetyTipInteraction)> close_callback);
 
 #endif  // CHROME_BROWSER_UI_VIEWS_PAGE_INFO_SAFETY_TIP_PAGE_INFO_BUBBLE_VIEW_H_
diff --git a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc
index 9a62808..f45f06f3 100644
--- a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc
@@ -224,7 +224,8 @@
         PageInfoBubbleViewBase::GetPageInfoBubbleForTesting();
     ASSERT_TRUE(page_info);
     EXPECT_EQ(page_info->GetWindowTitle(),
-              l10n_util::GetStringUTF16(IDS_PAGE_INFO_SAFETY_TIP_SUMMARY));
+              l10n_util::GetStringUTF16(
+                  IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_TITLE));
   }
 
   void CheckPageInfoDoesNotShowSafetyTipInfo(Browser* browser) {
@@ -233,7 +234,8 @@
         PageInfoBubbleViewBase::GetPageInfoBubbleForTesting();
     ASSERT_TRUE(page_info);
     EXPECT_NE(page_info->GetWindowTitle(),
-              l10n_util::GetStringUTF16(IDS_PAGE_INFO_SAFETY_TIP_SUMMARY));
+              l10n_util::GetStringUTF16(
+                  IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_TITLE));
   }
 
  private:
@@ -398,14 +400,15 @@
 }
 
 // Tests that Safety Tips trigger on lookalike domains that don't qualify for an
-// interstitial.
+// interstitial, but do not impact Page Info.
 IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
                        TriggersOnLookalike) {
-  // This domain is a top domain, but not top 500.
+  // This domain is a lookalike of a top domain not in the top 500.
   const GURL kNavigatedUrl = GetURL("googlé.sk");
   SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
   NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
   EXPECT_TRUE(IsUIShowingIfEnabled());
+  ASSERT_NO_FATAL_FAILURE(CheckPageInfoDoesNotShowSafetyTipInfo(browser()));
 }
 
 // Tests that Safety Tips trigger (or not) on lookalike domains with edit
diff --git a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_unittest.cc b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_unittest.cc
index 2c88050..5e2041c 100644
--- a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_unittest.cc
@@ -56,7 +56,8 @@
     bubble_ = CreateSafetyTipBubbleForTesting(
         parent_window_->GetNativeView(), web_contents,
         security_state::SafetyTipStatus::kBadReputation,
-        GURL("https://www.fakegoogle.tld"), base::DoNothing());
+        GURL("https://www.fakegoogle.tld"), GURL("https://www.google.tld"),
+        base::DoNothing());
   }
 
   void TearDown() override { parent_window_->CloseNow(); }
diff --git a/chrome/browser/ui/web_applications/app_browser_controller.cc b/chrome/browser/ui/web_applications/app_browser_controller.cc
index 533d035..6c9f6fc 100644
--- a/chrome/browser/ui/web_applications/app_browser_controller.cc
+++ b/chrome/browser/ui/web_applications/app_browser_controller.cc
@@ -139,6 +139,11 @@
   return false;
 }
 
+web_app::WebAppBrowserController*
+AppBrowserController::AsWebAppBrowserController() {
+  return nullptr;
+}
+
 bool AppBrowserController::CanUninstall() const {
   return false;
 }
diff --git a/chrome/browser/ui/web_applications/app_browser_controller.h b/chrome/browser/ui/web_applications/app_browser_controller.h
index a300747d..5e75e43 100644
--- a/chrome/browser/ui/web_applications/app_browser_controller.h
+++ b/chrome/browser/ui/web_applications/app_browser_controller.h
@@ -20,6 +20,8 @@
 
 namespace web_app {
 
+class WebAppBrowserController;
+
 // Returns true if |app_url| and |page_url| are the same origin. To avoid
 // breaking Hosted Apps and Bookmark Apps that might redirect to sites in the
 // same domain but with "www.", this returns true if |page_url| is secure and in
@@ -97,6 +99,9 @@
   // Determines whether the specified url is 'inside' the app |this| controls.
   virtual bool IsUrlInAppScope(const GURL& url) const = 0;
 
+  // Safe downcast:
+  virtual WebAppBrowserController* AsWebAppBrowserController();
+
   virtual bool CanUninstall() const;
 
   virtual void Uninstall();
diff --git a/chrome/browser/ui/web_applications/web_app_browser_controller.cc b/chrome/browser/ui/web_applications/web_app_browser_controller.cc
index fae4c163..075f673 100644
--- a/chrome/browser/ui/web_applications/web_app_browser_controller.cc
+++ b/chrome/browser/ui/web_applications/web_app_browser_controller.cc
@@ -36,6 +36,11 @@
   return true;
 }
 
+void WebAppBrowserController::SetReadIconCallbackForTesting(
+    base::OnceClosure callback) {
+  callback_for_testing_ = std::move(callback);
+}
+
 bool WebAppBrowserController::ShouldShowCustomTabBar() const {
   // TODO(https://crbug.com/966290): Complete implementation.
   return false;
@@ -87,6 +92,10 @@
   return base::StartsWith(url_path, scope_path, base::CompareCase::SENSITIVE);
 }
 
+WebAppBrowserController* WebAppBrowserController::AsWebAppBrowserController() {
+  return this;
+}
+
 std::string WebAppBrowserController::GetAppShortName() const {
   return registrar().GetAppShortName(app_id_);
 }
@@ -124,6 +133,8 @@
 
   app_icon_ = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
   web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
+  if (callback_for_testing_)
+    std::move(callback_for_testing_).Run();
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/ui/web_applications/web_app_browser_controller.h b/chrome/browser/ui/web_applications/web_app_browser_controller.h
index 65b5d01..0e8596ea 100644
--- a/chrome/browser/ui/web_applications/web_app_browser_controller.h
+++ b/chrome/browser/ui/web_applications/web_app_browser_controller.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
@@ -46,11 +47,14 @@
   base::string16 GetFormattedUrlOrigin() const override;
   GURL GetAppLaunchURL() const override;
   bool IsUrlInAppScope(const GURL& url) const override;
+  WebAppBrowserController* AsWebAppBrowserController() override;
   bool CanUninstall() const override;
   void Uninstall() override;
   bool IsInstalled() const override;
   bool IsHostedApp() const override;
 
+  void SetReadIconCallbackForTesting(base::OnceClosure callback);
+
  private:
   const AppRegistrar& registrar() const;
 
@@ -60,6 +64,7 @@
   const AppId app_id_;
   mutable base::Optional<gfx::ImageSkia> app_icon_;
 
+  base::OnceClosure callback_for_testing_;
   mutable base::WeakPtrFactory<WebAppBrowserController> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(WebAppBrowserController);
diff --git a/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer.mojom b/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer.mojom
index 931227b9..0091ecb 100644
--- a/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer.mojom
+++ b/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer.mojom
@@ -14,8 +14,21 @@
 
 // Lives in the browser process. A renderer use this to control Crostini
 // installation.
-interface PageHandler {};
+interface PageHandler {
+  // Start installation
+  Install();
+  // Cancel an on-going installation
+  Cancel();
+  // If a user cancels the installation without starting it at all, this should
+  // be called so that metrics can be recorded.
+  CancelBeforeStart();
+  // The page normally is displayed in a dialog. Call this to close the dialog.
+  // chrome.send('dialogClose') should not be used, which could kill the page
+  // handler before previous mojom calls have been run.
+  Close();
+};
 
 // Lives in the renderer process. The browser uses this to sends installation
 // updates to the web page in the render.
-interface Page {};
+interface Page {
+};
diff --git a/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_page_handler.cc b/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_page_handler.cc
index 4299032..8b97142 100644
--- a/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_page_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_page_handler.cc
@@ -6,17 +6,59 @@
 
 #include <utility>
 
-#include "base/optional.h"
+#include "base/bind.h"
+#include "chrome/browser/chromeos/crostini/crostini_installer_ui_delegate.h"
 
 namespace chromeos {
 
 CrostiniInstallerPageHandler::CrostiniInstallerPageHandler(
+    crostini::CrostiniInstallerUIDelegate* installer_ui_delegate,
     mojo::PendingReceiver<chromeos::crostini_installer::mojom::PageHandler>
         pending_page_handler,
-    mojo::PendingRemote<chromeos::crostini_installer::mojom::Page> pending_page)
-    : receiver_{this, std::move(pending_page_handler)},
-      page_{std::move(pending_page)} {}
+    mojo::PendingRemote<chromeos::crostini_installer::mojom::Page> pending_page,
+    base::OnceClosure close_dialog_callback)
+    : installer_ui_delegate_{installer_ui_delegate},
+      receiver_{this, std::move(pending_page_handler)},
+      page_{std::move(pending_page)},
+      close_dialog_callback_{std::move(close_dialog_callback)} {}
 
 CrostiniInstallerPageHandler::~CrostiniInstallerPageHandler() = default;
 
+void CrostiniInstallerPageHandler::Install() {
+  installer_ui_delegate_->Install(
+      base::BindRepeating(&CrostiniInstallerPageHandler::OnProgressUpdate,
+                          weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&CrostiniInstallerPageHandler::OnInstallFinished,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CrostiniInstallerPageHandler::Cancel() {
+  installer_ui_delegate_->Cancel(
+      base::BindOnce(&CrostiniInstallerPageHandler::OnCanceled,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CrostiniInstallerPageHandler::CancelBeforeStart() {
+  installer_ui_delegate_->CancelBeforeStart();
+}
+
+void CrostiniInstallerPageHandler::Close() {
+  std::move(close_dialog_callback_).Run();
+}
+
+void CrostiniInstallerPageHandler::OnProgressUpdate(
+    crostini::mojom::InstallerState installer_state,
+    double progress_fraction) {
+  // TODO(lxj)
+}
+
+void CrostiniInstallerPageHandler::OnInstallFinished(
+    crostini::mojom::InstallerError error) {
+  // TODO(lxj)
+}
+
+void CrostiniInstallerPageHandler::OnCanceled() {
+  // TODO(lxj)
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_page_handler.h b/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_page_handler.h
index d816d8a..9060a950 100644
--- a/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_page_handler.h
+++ b/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_page_handler.h
@@ -5,28 +5,52 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_CROSTINI_INSTALLER_CROSTINI_INSTALLER_PAGE_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_CHROMEOS_CROSTINI_INSTALLER_CROSTINI_INSTALLER_PAGE_HANDLER_H_
 
+#include "base/callback.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/crostini/crostini_installer_types.mojom-forward.h"
 #include "chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
+namespace crostini {
+class CrostiniInstallerUIDelegate;
+}  // namespace crostini
+
 namespace chromeos {
 
 class CrostiniInstallerPageHandler
     : public chromeos::crostini_installer::mojom::PageHandler {
  public:
   CrostiniInstallerPageHandler(
+      crostini::CrostiniInstallerUIDelegate* installer_ui_delegate,
       mojo::PendingReceiver<chromeos::crostini_installer::mojom::PageHandler>
           pending_page_handler,
       mojo::PendingRemote<chromeos::crostini_installer::mojom::Page>
-          pending_page);
+          pending_page,
+      base::OnceClosure close_dialog_callback);
   ~CrostiniInstallerPageHandler() override;
 
+  // chromeos::crostini_installer::mojom::PageHandler:
+  void Install() override;
+  void Cancel() override;
+  void CancelBeforeStart() override;
+  void Close() override;
+
  private:
+  void OnProgressUpdate(crostini::mojom::InstallerState installer_state,
+                        double progress_fraction);
+  void OnInstallFinished(crostini::mojom::InstallerError error);
+  void OnCanceled();
+
+  crostini::CrostiniInstallerUIDelegate* installer_ui_delegate_;
   mojo::Receiver<chromeos::crostini_installer::mojom::PageHandler> receiver_;
   mojo::Remote<chromeos::crostini_installer::mojom::Page> page_;
+  base::OnceClosure close_dialog_callback_;
+
+  base::WeakPtrFactory<CrostiniInstallerPageHandler> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(CrostiniInstallerPageHandler);
 };
diff --git a/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.cc b/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.cc
index 7a69de01..02fe92c 100644
--- a/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "chrome/browser/chromeos/crostini/crostini_installer.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_page_handler.h"
 #include "chrome/common/webui_url_constants.h"
@@ -31,14 +32,9 @@
   content::WebUIDataSource* source =
       content::WebUIDataSource::Create(chrome::kChromeUICrostiniInstallerHost);
 
-  source->AddResourcePath("app.html", IDR_CROSTINI_INSTALLER_APP_HTML);
   source->AddResourcePath("app.js", IDR_CROSTINI_INSTALLER_APP_JS);
-  source->AddResourcePath("browser_proxy.html",
-                          IDR_CROSTINI_INSTALLER_BROWSER_PROXY_HTML);
   source->AddResourcePath("browser_proxy.js",
                           IDR_CROSTINI_INSTALLER_BROWSER_PROXY_JS);
-  source->AddResourcePath("crostini_installer.mojom.html",
-                          IDR_CROSTINI_INSTALLER_MOJO_HTML);
   source->AddResourcePath("crostini_installer.mojom-lite.js",
                           IDR_CROSTINI_INSTALLER_MOJO_LITE_JS);
   source->SetDefaultResource(IDR_CROSTINI_INSTALLER_INDEX_HTML);
@@ -68,7 +64,15 @@
   DCHECK(pending_page.is_valid());
 
   page_handler_ = std::make_unique<CrostiniInstallerPageHandler>(
-      std::move(pending_page_handler), std::move(pending_page));
+      crostini::CrostiniInstaller::GetForProfile(Profile::FromWebUI(web_ui())),
+      std::move(pending_page_handler), std::move(pending_page),
+      // Using Unretained(this) because |page_handler_| will not out-live
+      // |this|.
+      //
+      // CloseDialog() is a no-op if we are not in a dialog (e.g. user
+      // access the page using the URL directly, which is not supported).
+      base::BindOnce(&CrostiniInstallerUI::CloseDialog, base::Unretained(this),
+                     nullptr));
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/fake_update_required_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/fake_update_required_screen_handler.cc
new file mode 100644
index 0000000..ffbed8d
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/login/fake_update_required_screen_handler.cc
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/chromeos/login/fake_update_required_screen_handler.h"
+
+namespace chromeos {
+
+void FakeUpdateRequiredScreenHandler::SetUIState(
+    UpdateRequiredView::UIState ui_state) {
+  ui_state_ = ui_state;
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/fake_update_required_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/fake_update_required_screen_handler.h
new file mode 100644
index 0000000..7a36649
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/login/fake_update_required_screen_handler.h
@@ -0,0 +1,48 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_FAKE_UPDATE_REQUIRED_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_FAKE_UPDATE_REQUIRED_SCREEN_HANDLER_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/screens/update_required_screen.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/update_required_screen_handler.h"
+
+namespace chromeos {
+
+class UpdateRequiredScreen;
+
+class FakeUpdateRequiredScreenHandler : public UpdateRequiredView {
+ public:
+  FakeUpdateRequiredScreenHandler() = default;
+  ~FakeUpdateRequiredScreenHandler() override {}
+
+  UpdateRequiredView::UIState ui_state() { return ui_state_; }
+
+ private:
+  void Show() override {}
+  void Hide() override {}
+  void Bind(UpdateRequiredScreen* screen) override {}
+  void Unbind() override {}
+
+  void SetIsConnected(bool connected) override {}
+  void SetUpdateProgressUnavailable(bool unavailable) override {}
+  void SetUpdateProgressValue(int progress) override {}
+  void SetUpdateProgressMessage(const base::string16& message) override {}
+  void SetEstimatedTimeLeftVisible(bool visible) override {}
+  void SetEstimatedTimeLeft(int seconds_left) override {}
+  void SetUIState(UpdateRequiredView::UIState ui_state) override;
+
+  UpdateRequiredView::UIState ui_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeUpdateRequiredScreenHandler);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_FAKE_UPDATE_REQUIRED_SCREEN_HANDLER_H_
diff --git a/chrome/browser/ui/webui/chromeos/login/update_required_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/update_required_screen_handler.cc
index 1a557a31..d02af35 100644
--- a/chrome/browser/ui/webui/chromeos/login/update_required_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/update_required_screen_handler.cc
@@ -12,6 +12,9 @@
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/login/localized_values_builder.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/chromeos/devicetype_utils.h"
+#include "ui/strings/grit/ui_strings.h"
 
 namespace chromeos {
 
@@ -20,6 +23,7 @@
 UpdateRequiredScreenHandler::UpdateRequiredScreenHandler(
     JSCallsContainer* js_calls_container)
     : BaseScreenHandler(kScreenId, js_calls_container) {
+  set_user_acted_method_path("login.UpdateRequiredScreen.userActed");
 }
 
 UpdateRequiredScreenHandler::~UpdateRequiredScreenHandler() {
@@ -31,6 +35,35 @@
     ::login::LocalizedValuesBuilder* builder) {
   builder->Add("updateRequiredMessage",
                IDS_UPDATE_REQUIRED_LOGIN_SCREEN_MESSAGE);
+  builder->Add("errorMessage",
+               IDS_BROWSER_SHARING_ERROR_DIALOG_TEXT_INTERNAL_ERROR);
+  builder->Add("eolMessage",
+               ui::SubstituteChromeOSDeviceType(IDS_EOL_NOTIFICATION_EOL));
+  builder->Add("selectNetworkButtonCaption", IDS_APP_START_CONFIGURE_NETWORK);
+  builder->Add("updateButtonCaption",
+               IDS_SETTINGS_ABOUT_PAGE_CHECK_FOR_UPDATES);
+  builder->Add("rebootNeededMessage", IDS_UPDATE_COMPLETED);
+
+  builder->Add("checkingForUpdatesTitle", IDS_CHECKING_FOR_UPDATES);
+  builder->Add("updatingTitle", IDS_UPDATING_SCREEN_TITLE);
+
+  builder->Add("downloading", IDS_DOWNLOADING);
+  builder->Add("downloadingTimeLeftLong", IDS_DOWNLOADING_TIME_LEFT_LONG);
+  builder->Add("downloadingTimeLeftStatusOneHour",
+               IDS_DOWNLOADING_TIME_LEFT_STATUS_ONE_HOUR);
+  builder->Add("downloadingTimeLeftStatusMinutes",
+               IDS_DOWNLOADING_TIME_LEFT_STATUS_MINUTES);
+  builder->Add("downloadingTimeLeftSmall", IDS_DOWNLOADING_TIME_LEFT_SMALL);
+
+  builder->Add(
+      "updateOverCellularPromptTitle",
+      ui::SubstituteChromeOSDeviceType(IDS_UPDATE_OVER_CELLULAR_PROMPT_TITLE));
+  builder->Add("updateOverCellularPromptMessage",
+               IDS_UPDATE_OVER_CELLULAR_PROMPT_MESSAGE);
+  builder->Add("AcceptUpdateOverCellularButton",
+               IDS_OFFERS_CONSENT_INFOBAR_ENABLE_BUTTON);
+  builder->Add("RejectUpdateOverCellularButton",
+               IDS_OFFERS_CONSENT_INFOBAR_DISABLE_BUTTON);
 }
 
 void UpdateRequiredScreenHandler::Initialize() {
@@ -60,4 +93,36 @@
   BaseScreenHandler::SetBaseScreen(nullptr);
 }
 
+void UpdateRequiredScreenHandler::SetIsConnected(bool connected) {
+  CallJS("login.UpdateRequiredScreen.setIsConnected", connected);
+}
+
+void UpdateRequiredScreenHandler::SetUpdateProgressUnavailable(
+    bool unavailable) {
+  CallJS("login.UpdateRequiredScreen.setUpdateProgressUnavailable",
+         unavailable);
+}
+
+void UpdateRequiredScreenHandler::SetUpdateProgressValue(int progress) {
+  CallJS("login.UpdateRequiredScreen.setUpdateProgressValue", progress);
+}
+
+void UpdateRequiredScreenHandler::SetUpdateProgressMessage(
+    const base::string16& message) {
+  CallJS("login.UpdateRequiredScreen.setUpdateProgressMessage", message);
+}
+
+void UpdateRequiredScreenHandler::SetEstimatedTimeLeftVisible(bool visible) {
+  CallJS("login.UpdateRequiredScreen.setEstimatedTimeLeftVisible", visible);
+}
+
+void UpdateRequiredScreenHandler::SetEstimatedTimeLeft(int seconds_left) {
+  CallJS("login.UpdateRequiredScreen.setEstimatedTimeLeft", seconds_left);
+}
+
+void UpdateRequiredScreenHandler::SetUIState(
+    UpdateRequiredView::UIState ui_state) {
+  CallJS("login.UpdateRequiredScreen.setUIState", static_cast<int>(ui_state));
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/update_required_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/update_required_screen_handler.h
index 7a19433..cd52e9f 100644
--- a/chrome/browser/ui/webui/chromeos/login/update_required_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/update_required_screen_handler.h
@@ -21,6 +21,16 @@
 
 class UpdateRequiredView {
  public:
+  enum UIState {
+    UPDATE_REQUIRED_MESSAGE = 0,   // 'System update required' message.
+    UPDATE_PROCESS,                // Update is going on.
+    UPDATE_NEED_PERMISSION,        // Need user's permission to proceed.
+    UPDATE_COMPLETED_NEED_REBOOT,  // Update successful, manual reboot is
+                                   // needed.
+    UPDATE_ERROR,                  // An error has occurred.
+    EOL                            // End of Life message.
+  };
+
   constexpr static StaticOobeScreenId kScreenId{"update-required"};
 
   virtual ~UpdateRequiredView() {}
@@ -36,6 +46,21 @@
 
   // Unbinds the screen from the view.
   virtual void Unbind() = 0;
+
+  // Is device connected to some network?
+  virtual void SetIsConnected(bool connected) = 0;
+  // Is progress unavailable (e.g. we are checking for updates)?
+  virtual void SetUpdateProgressUnavailable(bool unavailable) = 0;
+  // Set progress percentage.
+  virtual void SetUpdateProgressValue(int progress) = 0;
+  // Set progress message (like "Verifying").
+  virtual void SetUpdateProgressMessage(const base::string16& message) = 0;
+  // Set the visibility of the estimated time left.
+  virtual void SetEstimatedTimeLeftVisible(bool visible) = 0;
+  // Set the estimated time left, in seconds.
+  virtual void SetEstimatedTimeLeft(int seconds_left) = 0;
+  // Set the UI state of the screen.
+  virtual void SetUIState(UpdateRequiredView::UIState ui_state) = 0;
 };
 
 class UpdateRequiredScreenHandler : public UpdateRequiredView,
@@ -52,6 +77,14 @@
   void Bind(UpdateRequiredScreen* screen) override;
   void Unbind() override;
 
+  void SetIsConnected(bool connected) override;
+  void SetUpdateProgressUnavailable(bool unavailable) override;
+  void SetUpdateProgressValue(int progress) override;
+  void SetUpdateProgressMessage(const base::string16& message) override;
+  void SetEstimatedTimeLeftVisible(bool visible) override;
+  void SetEstimatedTimeLeft(int seconds_left) override;
+  void SetUIState(UpdateRequiredView::UIState ui_state) override;
+
   // BaseScreenHandler:
   void DeclareLocalizedValues(
       ::login::LocalizedValuesBuilder* builder) override;
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc b/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc
index 53f2c79..c69c8e2 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/crostini/crostini_manager.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
-#include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/platform_util.h"
 #include "chrome/browser/profiles/profile.h"
@@ -42,7 +41,6 @@
 #include "components/arc/arc_util.h"
 #include "components/arc/session/arc_bridge_service.h"
 #include "components/browsing_data/content/conditional_cache_counting_helper.h"
-#include "components/drive/chromeos/file_system_interface.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
@@ -85,7 +83,6 @@
       browser_site_data_size_(-1),
       has_browser_site_data_size_(false),
       updating_downloads_size_(false),
-      updating_drive_cache_size_(false),
       updating_browsing_data_size_(false),
       updating_android_size_(false),
       updating_crostini_size_(false),
@@ -128,10 +125,6 @@
       base::BindRepeating(&StorageHandler::HandleOpenArcStorage,
                           base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      "clearDriveCache",
-      base::BindRepeating(&StorageHandler::HandleClearDriveCache,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
       "updateExternalStorages",
       base::BindRepeating(&StorageHandler::HandleUpdateExternalStorages,
                           base::Unretained(this)));
@@ -183,7 +176,6 @@
 
   UpdateSizeStat();
   UpdateDownloadsSize();
-  UpdateDriveCacheSize();
   UpdateBrowsingDataSize();
   UpdateAndroidRunning();
   UpdateAndroidSize();
@@ -207,25 +199,11 @@
     arc_storage_manager->OpenPrivateVolumeSettings();
 }
 
-void StorageHandler::HandleClearDriveCache(
-    const base::ListValue* unused_args) {
-  drive::FileSystemInterface* const file_system =
-      drive::util::GetFileSystemByProfile(profile_);
-  file_system->FreeDiskSpaceIfNeededFor(
-      std::numeric_limits<int64_t>::max(),  // Removes as much as possible.
-      base::Bind(&StorageHandler::OnClearDriveCacheDone,
-                 weak_ptr_factory_.GetWeakPtr()));
-}
-
 void StorageHandler::HandleUpdateExternalStorages(
     const base::ListValue* unused_args) {
   UpdateExternalStorages();
 }
 
-void StorageHandler::OnClearDriveCacheDone(bool /*success*/) {
-  UpdateDriveCacheSize();
-}
-
 void StorageHandler::UpdateSizeStat() {
   const base::FilePath downloads_path =
       file_manager::util::GetDownloadsFolderForProfile(profile_);
@@ -282,28 +260,6 @@
                     base::Value(ui::FormatBytes(size)));
 }
 
-void StorageHandler::UpdateDriveCacheSize() {
-  drive::FileSystemInterface* const file_system =
-      drive::util::GetFileSystemByProfile(profile_);
-  if (!file_system)
-    return;
-
-  if (updating_drive_cache_size_)
-    return;
-  updating_drive_cache_size_ = true;
-
-  // Shows the item "Offline cache" and starts calculating size.
-  FireWebUIListener("storage-drive-enabled-changed", base::Value(true));
-  file_system->CalculateCacheSize(base::Bind(
-      &StorageHandler::OnGetDriveCacheSize, weak_ptr_factory_.GetWeakPtr()));
-}
-
-void StorageHandler::OnGetDriveCacheSize(int64_t size) {
-  updating_drive_cache_size_ = false;
-  FireWebUIListener("storage-drive-cache-size-changed",
-                    base::Value(ui::FormatBytes(size)), base::Value(size > 0));
-}
-
 void StorageHandler::UpdateBrowsingDataSize() {
   if (updating_browsing_data_size_)
     return;
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h b/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h
index 32078b3..e4b5023c 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h
@@ -80,12 +80,8 @@
   void HandleUpdateStorageInfo(const base::ListValue* unused_args);
   void HandleOpenDownloads(const base::ListValue* unused_args);
   void HandleOpenArcStorage(const base::ListValue* unused_args);
-  void HandleClearDriveCache(const base::ListValue* unused_args);
   void HandleUpdateExternalStorages(const base::ListValue* unused_args);
 
-  // Callback called when clearing Drive cache is done.
-  void OnClearDriveCacheDone(bool success);
-
   // Requests updating disk space information.
   void UpdateSizeStat();
 
@@ -98,12 +94,6 @@
   // Callback to update the UI about the size of Downloads directory.
   void OnGetDownloadsSize(int64_t size);
 
-  // Requests updating the size of Drive Cache.
-  void UpdateDriveCacheSize();
-
-  // Callback to update the UI about the size of Drive Cache.
-  void OnGetDriveCacheSize(int64_t size);
-
   // Requests updating the size of browsing data.
   void UpdateBrowsingDataSize();
 
@@ -166,7 +156,6 @@
 
   // Flags indicating fetch operations for storage sizes are ongoing.
   bool updating_downloads_size_;
-  bool updating_drive_cache_size_;
   bool updating_browsing_data_size_;
   bool updating_android_size_;
   bool updating_crostini_size_;
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index e6472440..49cc12d8 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -1046,7 +1046,6 @@
       {"storageItemInUse", IDS_SETTINGS_STORAGE_ITEM_IN_USE},
       {"storageItemAvailable", IDS_SETTINGS_STORAGE_ITEM_AVAILABLE},
       {"storageItemDownloads", IDS_SETTINGS_STORAGE_ITEM_DOWNLOADS},
-      {"storageItemDriveCache", IDS_SETTINGS_STORAGE_ITEM_DRIVE_CACHE},
       {"storageItemBrowsingData", IDS_SETTINGS_STORAGE_ITEM_BROWSING_DATA},
       {"storageItemAndroid", IDS_SETTINGS_STORAGE_ITEM_ANDROID},
       {"storageItemCrostini", IDS_SETTINGS_STORAGE_ITEM_CROSTINI},
@@ -1065,12 +1064,6 @@
        IDS_SETTINGS_STORAGE_SPACE_CRITICALLY_LOW_MESSAGE_LINE_1},
       {"storageSpaceCriticallyLowMessageLine2",
        IDS_SETTINGS_STORAGE_SPACE_CRITICALLY_LOW_MESSAGE_LINE_2},
-      {"storageClearDriveCacheDialogTitle",
-       IDS_SETTINGS_STORAGE_CLEAR_DRIVE_CACHE_DIALOG_TITLE},
-      {"storageClearDriveCacheDialogDescription",
-       IDS_SETTINGS_STORAGE_CLEAR_DRIVE_CACHE_DESCRIPTION},
-      {"storageDeleteAllButtonTitle",
-       IDS_SETTINGS_STORAGE_DELETE_ALL_BUTTON_TITLE},
       {"storageExternal", IDS_SETTINGS_STORAGE_EXTERNAL},
       {"storageAndroidAppsExternalDrivesNote",
        IDS_SETTINGS_STORAGE_ANDROID_APPS_ACCESS_EXTERNAL_DRIVES_NOTE},
diff --git a/chrome/browser/ui/webui/theme_handler.cc b/chrome/browser/ui/webui/theme_handler.cc
index 9c52ec07..a1d7401a8 100644
--- a/chrome/browser/ui/webui/theme_handler.cc
+++ b/chrome/browser/ui/webui/theme_handler.cc
@@ -71,7 +71,7 @@
   // TODO(dbeam): why does this need to be a dictionary?
   base::DictionaryValue dictionary;
   dictionary.SetBoolean("hasCustomBackground", has_custom_bg);
-  CallJavascriptFunction("ntp.themeChanged", dictionary);
+  FireWebUIListener("theme-changed", dictionary);
 }
 
 void ThemeHandler::InitializeCSSCaches() {
diff --git a/chrome/browser/ui/webui/webui_load_timer.cc b/chrome/browser/ui/webui/webui_load_timer.cc
index 9bfadd5f..c09c69b 100644
--- a/chrome/browser/ui/webui/webui_load_timer.cc
+++ b/chrome/browser/ui/webui/webui_load_timer.cc
@@ -47,7 +47,7 @@
   timer_ = std::make_unique<base::ElapsedTimer>();
 }
 
-void WebuiLoadTimer::DocumentLoadedInFrame(
+void WebuiLoadTimer::DOMContentLoaded(
     content::RenderFrameHost* render_frame_host) {
   // See comment in DocumentOnLoadCompletedInMainFrame.
   if (!timer_ || render_frame_host != web_contents()->GetMainFrame())
diff --git a/chrome/browser/ui/webui/webui_load_timer.h b/chrome/browser/ui/webui/webui_load_timer.h
index 5474b64..83e089c 100644
--- a/chrome/browser/ui/webui/webui_load_timer.h
+++ b/chrome/browser/ui/webui/webui_load_timer.h
@@ -20,7 +20,7 @@
  public:
   // Load times are reported to UMA using the provided strings which
   // must not be empty.
-  // * |document_initial_load_uma_id| - corresponds to DocumentLoadedInFrame
+  // * |document_initial_load_uma_id| - corresponds to DOMContentLoaded
   // * |document_load_completed_uma_id| - corresponds to
   //   DocumentOnLoadCompletedInMainFrame
   WebuiLoadTimer(content::WebContents* web_contents,
@@ -31,8 +31,7 @@
   // WebContentsObserver
   void DidStartNavigation(
       content::NavigationHandle* navigation_handle) override;
-  void DocumentLoadedInFrame(
-      content::RenderFrameHost* render_frame_host) override;
+  void DOMContentLoaded(content::RenderFrameHost* render_frame_host) override;
   void DocumentOnLoadCompletedInMainFrame() override;
 
  private:
diff --git a/chrome/browser/ui/webui/welcome/welcome_ui.cc b/chrome/browser/ui/webui/welcome/welcome_ui.cc
index 0dba778..d143915 100644
--- a/chrome/browser/ui/webui/welcome/welcome_ui.cc
+++ b/chrome/browser/ui/webui/welcome/welcome_ui.cc
@@ -25,6 +25,7 @@
 #include "components/signin/public/base/signin_pref_names.h"
 #include "components/strings/grit/components_strings.h"
 #include "net/base/url_util.h"
+#include "ui/resources/grit/webui_resources.h"
 
 #if defined(OS_WIN)
 #include "base/win/windows_version.h"
@@ -127,6 +128,8 @@
 
   content::WebUIDataSource* html_source =
       content::WebUIDataSource::Create(url.host());
+  html_source->OverrideContentSecurityPolicyScriptSrc(
+      "script-src chrome://resources chrome://test 'self';");
 
   // Add welcome strings.
   AddStrings(html_source);
@@ -143,6 +146,8 @@
 
     html_source->AddResourcePath(path, kWelcomeResources[i].value);
   }
+  html_source->AddResourcePath("test_loader.js", IDR_WEBUI_JS_TEST_LOADER);
+  html_source->AddResourcePath("test_loader.html", IDR_WEBUI_HTML_TEST_LOADER);
 
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
   // Load unscaled images.
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index ac4a025..87d0999c 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -165,6 +165,7 @@
 
   sources = [
     "pending_app_manager_impl_browsertest.cc",
+    "web_app_icon_manager_browsertest.cc",
   ]
 
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
diff --git a/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc b/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc
new file mode 100644
index 0000000..ef394bb
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_icon_manager_browsertest.cc
@@ -0,0 +1,124 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/macros.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/apps/app_service/app_launch_params.h"
+#include "chrome/browser/apps/launch_service/launch_service.h"
+#include "chrome/browser/installable/installable_metrics.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/web_applications/web_app_browser_controller.h"
+#include "chrome/browser/web_applications/components/install_manager.h"
+#include "chrome/browser/web_applications/components/web_app_constants.h"
+#include "chrome/browser/web_applications/components/web_app_icon_generator.h"
+#include "chrome/browser/web_applications/components/web_app_install_utils.h"
+#include "chrome/browser/web_applications/components/web_app_provider_base.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/web_application_info.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "components/services/app_service/public/mojom/types.mojom.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/window_open_disposition.h"
+#include "ui/gfx/image/image_skia.h"
+#include "url/gurl.h"
+
+namespace web_app {
+
+class WebAppIconManagerBrowserTest : public InProcessBrowserTest {
+ public:
+  WebAppIconManagerBrowserTest() {
+    scoped_feature_list_.InitWithFeatures(
+        {features::kDesktopPWAsWithoutExtensions}, {});
+  }
+
+  ~WebAppIconManagerBrowserTest() override = default;
+
+ protected:
+  net::EmbeddedTestServer* https_server() { return &https_server_; }
+
+  // InProcessBrowserTest:
+  void SetUp() override {
+    https_server_.AddDefaultHandlers(GetChromeTestDataDir());
+    InProcessBrowserTest::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  net::EmbeddedTestServer https_server_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebAppIconManagerBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(WebAppIconManagerBrowserTest, SingleIcon) {
+  ASSERT_TRUE(https_server()->Start());
+  const GURL app_url =
+      https_server()->GetURL("/banners/manifest_test_page.html");
+
+  AppId app_id;
+  {
+    std::unique_ptr<WebApplicationInfo> web_application_info =
+        std::make_unique<WebApplicationInfo>();
+    web_application_info->app_url = app_url;
+    web_application_info->scope = app_url.GetWithoutFilename();
+    web_application_info->open_as_window = true;
+
+    {
+      WebApplicationInfo::IconInfo info;
+      info.width = icon_size::k32;
+      info.height = icon_size::k32;
+      info.data.allocN32Pixels(info.width, info.height, true);
+      info.data.eraseColor(SK_ColorBLUE);
+      web_application_info->icons.push_back(info);
+    }
+
+    InstallManager& install_manager =
+        WebAppProviderBase::GetProviderBase(browser()->profile())
+            ->install_manager();
+
+    base::RunLoop run_loop;
+    install_manager.InstallWebAppFromInfo(
+        std::move(web_application_info), ForInstallableSite::kYes,
+        WebappInstallSource::OMNIBOX_INSTALL_ICON,
+        base::BindLambdaForTesting(
+            [&app_id, &run_loop](const AppId& installed_app_id,
+                                 InstallResultCode code) {
+              EXPECT_EQ(web_app::InstallResultCode::kSuccessNewInstall, code);
+              app_id = installed_app_id;
+              run_loop.Quit();
+            }));
+
+    run_loop.Run();
+  }
+
+  WebAppBrowserController* controller;
+  {
+    AppLaunchParams params(browser()->profile(), app_id,
+                           apps::mojom::LaunchContainer::kLaunchContainerWindow,
+                           WindowOpenDisposition::NEW_WINDOW,
+                           apps::mojom::AppLaunchSource::kSourceTest);
+    content::WebContents* contents =
+        apps::LaunchService::Get(browser()->profile())->OpenApplication(params);
+    controller = chrome::FindBrowserWithWebContents(contents)
+                     ->app_controller()
+                     ->AsWebAppBrowserController();
+  }
+
+  base::RunLoop run_loop;
+  controller->SetReadIconCallbackForTesting(
+      base::BindLambdaForTesting([controller, &run_loop]() {
+        const SkBitmap* bitmap = controller->GetWindowAppIcon().bitmap();
+        EXPECT_EQ(SK_ColorBLUE, bitmap->getColor(0, 0));
+        EXPECT_EQ(32, bitmap->width());
+        EXPECT_EQ(32, bitmap->height());
+        run_loop.Quit();
+      }));
+
+  run_loop.Run();
+}
+
+}  // namespace web_app
diff --git a/chrome/common/extensions/api/autotest_private.idl b/chrome/common/extensions/api/autotest_private.idl
index b7bdecc..3c344a7 100644
--- a/chrome/common/extensions/api/autotest_private.idl
+++ b/chrome/common/extensions/api/autotest_private.idl
@@ -189,12 +189,26 @@
   callback HistogramCallback = void (Histogram histogram);
 
   dictionary AssistantQueryResponse {
+    // Text response returned from server.
     DOMString? text;
+    // HTML response returned from server, usually accompanied with
+    // fallback text.
     DOMString? htmlResponse;
+    // Text messages used as the "fallback" for HTML card rendering.
+    // Generally the fallback text is similar to transcribed TTS,
+    // e.g. "It's exactly 6 o'clock." or "Turning bluetooth off.".
     DOMString? htmlFallback;
   };
-  callback AssistantQueryResponseCallback =
-      void(AssistantQueryResponse response);
+  dictionary AssistantQueryStatus {
+    // Indicates whether this might be a voice interaction.
+    boolean isMicOpen;
+    // Query text sent to Assistant. In the event of a voice interaction,
+    // this field will be same as the speech recognition final result.
+    DOMString queryText;
+    // Response for the current query.
+    AssistantQueryResponse queryResponse;
+  };
+  callback AssistantQueryStatusCallback = void (AssistantQueryStatus status);
 
   callback IsAppShownCallback = void (boolean appShown);
 
@@ -452,15 +466,38 @@
     // |callback|: Called when the operation has completed.
     static void bootstrapMachineLearningService(VoidCallback callback);
 
-    // Enable/disable the Google Assistant
+    // Enables/disables the Google Assistant.
     // |callback|: Called when the operation has completed.
     static void setAssistantEnabled(boolean enabled, long timeout_ms,
                                     VoidCallback callback);
 
-    // Send a text query via Google Assistant.
+    // Sends a text query via Google Assistant.
     // |callback|: Called when response has been received.
     static void sendAssistantTextQuery(DOMString query, long timeout_ms,
-                                       AssistantQueryResponseCallback callback);
+                                       AssistantQueryStatusCallback callback);
+
+    // Invokes |callback| once the current text/voice interaction is completed.
+    // Responds with the the query status if any valid response was caught
+    // before the timeout. This API should be called before sending the query.
+    // To use it for testing a voice query via OKG in Autotest, for example,
+    // you can do:
+    //
+    //    // Enable hotword setting for Assistant.
+    //    assistant_util.enable_hotword();
+    //
+    //    // Call this API with your callback function.
+    //    chrome.autotestPrivate.waitForAssistantQueryStatus(timeout_s,
+    //        function(status) {...});
+    //
+    // then start Assistant via OKG and send voice query before timeout.
+    //
+    // TODO(meilinw@): disable warmer welcome to avoid an unintended early
+    // return of this API when launching Assistant via hotkey.
+    // TODO(meilinw@): update the comment above to use Tast instead after
+    // adding API to enable hotword in Tast.
+    static void waitForAssistantQueryStatus(
+        long timeout_s,
+        AssistantQueryStatusCallback callback);
 
     // Set value for the specified user pref in the pref tree.
     static void setWhitelistedPref(DOMString pref_name, any value,
diff --git a/chrome/common/extensions/api/file_manager_private.idl b/chrome/common/extensions/api/file_manager_private.idl
index 5e9ad42..b7835d7 100644
--- a/chrome/common/extensions/api/file_manager_private.idl
+++ b/chrome/common/extensions/api/file_manager_private.idl
@@ -194,12 +194,6 @@
   isArbitrarySyncFolder
 };
 
-// Entry property visibility for setEntryTag();
-enum EntryTagVisibility {
-  private,
-  public
-};
-
 // Source of the volume data.
 enum Source {
   file,
@@ -1101,14 +1095,6 @@
   static void computeChecksum([instanceof=Entry] object entry,
                               ComputeChecksumCallback callback);
 
-  // Sets a tag on a file or a directory. Only Drive files are supported.
-  [nocompile]
-  static void setEntryTag([instanceof=Entry] object entry,
-                          EntryTagVisibility visibility,
-                          DOMString key,
-                          DOMString value,
-                          SimpleCallback callback);
-
   // Returns if Piex loader is enabled.
   static void isPiexLoaderEnabled(BooleanCallback callback);
 
diff --git a/chrome/common/extensions/api/file_manager_private_internal.idl b/chrome/common/extensions/api/file_manager_private_internal.idl
index 0dfa422..5c3e3064 100644
--- a/chrome/common/extensions/api/file_manager_private_internal.idl
+++ b/chrome/common/extensions/api/file_manager_private_internal.idl
@@ -75,11 +75,6 @@
     static void getFileTasks(DOMString[] urls,
                              GetFileTasksCallback callback);
     static void getDownloadUrl(DOMString url, GetUrlCallback callback);
-    static void setEntryTag(DOMString url,
-                            fileManagerPrivate.EntryTagVisibility visibility,
-                            DOMString key,
-                            DOMString value,
-                            SimpleCallback callback);
     static void startCopy(DOMString url,
                           DOMString parentUrl,
                           DOMString newName,
diff --git a/chrome/renderer/resources/extensions/file_manager_private_custom_bindings.js b/chrome/renderer/resources/extensions/file_manager_private_custom_bindings.js
index 0d756ef..090d462 100644
--- a/chrome/renderer/resources/extensions/file_manager_private_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/file_manager_private_custom_bindings.js
@@ -151,13 +151,6 @@
     fileManagerPrivateInternal.getDownloadUrl(url, callback);
   });
 
-  apiFunctions.setHandleRequest('setEntryTag', function(
-        entry, visibility, key, value, callback) {
-    var url = getEntryURL(entry);
-    fileManagerPrivateInternal.setEntryTag(
-        url, visibility, key, value, callback);
-  });
-
   apiFunctions.setHandleRequest('startCopy', function(
         entry, parentEntry, newName, callback) {
     var url = getEntryURL(entry);
diff --git a/chrome/services/app_service/public/cpp/intent_filter_util.h b/chrome/services/app_service/public/cpp/intent_filter_util.h
index 705d2a5..d878bf8 100644
--- a/chrome/services/app_service/public/cpp/intent_filter_util.h
+++ b/chrome/services/app_service/public/cpp/intent_filter_util.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_SERVICES_APP_SERVICE_PUBLIC_CPP_INTENT_FILTER_UTIL_H_
 #define CHROME_SERVICES_APP_SERVICE_PUBLIC_CPP_INTENT_FILTER_UTIL_H_
 
-// Utility functions for providing an App Service intent filter.
+// Utility functions for creating an App Service intent filter.
 
 #include <string>
 
@@ -14,11 +14,20 @@
 
 namespace apps_util {
 
+// Creates condition value that makes up App Service intent filter
+// condition. Each condition contains a list of condition values.
+// For pattern type of condition, the value match will be based on the
+// |pattern_match_type| match type. If the |pattern_match_type| is kNone,
+// then an exact match with the value will be required.
 apps::mojom::ConditionValuePtr MakeConditionValue(
     const std::string& value,
     apps::mojom::PatternMatchType pattern_match_type);
 
-// Creates condition that makes up App Service intent filter.
+// Creates condition that makes up App Service intent filter. Each
+// intent filter contains a list of conditions with different
+// condition types. Each condition contains a list of |condition_values|.
+// For one condition, if the value matches one of the |condition_values|,
+// then this condition is matched.
 apps::mojom::ConditionPtr MakeCondition(
     apps::mojom::ConditionType condition_type,
     std::vector<apps::mojom::ConditionValuePtr> condition_values);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 32bfd09d..710ceff5 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -4154,6 +4154,8 @@
       "../browser/ui/ash/network/tether_notification_presenter_unittest.cc",
       "../browser/ui/ash/session_controller_client_impl_unittest.cc",
       "../browser/ui/ash/wallpaper_controller_client_unittest.cc",
+      "../browser/ui/webui/chromeos/login/fake_update_required_screen_handler.cc",
+      "../browser/ui/webui/chromeos/login/fake_update_required_screen_handler.h",
       "../browser/ui/window_sizer/window_sizer_ash_unittest.cc",
     ]
     deps += [
diff --git a/chrome/test/android/BUILD.gn b/chrome/test/android/BUILD.gn
index 4ab53ef0..c007551d 100644
--- a/chrome/test/android/BUILD.gn
+++ b/chrome/test/android/BUILD.gn
@@ -151,7 +151,6 @@
     "javatests/src/org/chromium/chrome/test/util/NewTabPageTestUtils.java",
     "javatests/src/org/chromium/chrome/test/util/OmniboxTestUtils.java",
     "javatests/src/org/chromium/chrome/test/util/OverviewModeBehaviorWatcher.java",
-    "javatests/src/org/chromium/chrome/test/util/PrerenderTestHelper.java",
     "javatests/src/org/chromium/chrome/test/util/RenderTestRule.java",
     "javatests/src/org/chromium/chrome/test/util/SadTabRule.java",
     "javatests/src/org/chromium/chrome/test/util/TabStripUtils.java",
diff --git a/chrome/test/data/extensions/api_test/autotest_private/test.js b/chrome/test/data/extensions/api_test/autotest_private/test.js
index 86ece693..80e3501 100644
--- a/chrome/test/data/extensions/api_test/autotest_private/test.js
+++ b/chrome/test/data/extensions/api_test/autotest_private/test.js
@@ -243,6 +243,11 @@
         chrome.test.callbackFail(
             'Assistant not allowed - state: 8'));
   },
+  function waitForAssistantQueryStatus() {
+    chrome.autotestPrivate.waitForAssistantQueryStatus(10 /* timeout_s */,
+        chrome.test.callbackFail(
+            'Assistant not allowed - state: 8'));
+  },
   function setWhitelistedPref() {
     chrome.autotestPrivate.setWhitelistedPref(
         'settings.voice_interaction.hotword.enabled' /* pref_name */,
diff --git a/chrome/test/data/webui/fake_chrome_event.js b/chrome/test/data/webui/fake_chrome_event.js
index 95261387..1a156a3 100644
--- a/chrome/test/data/webui/fake_chrome_event.js
+++ b/chrome/test/data/webui/fake_chrome_event.js
@@ -6,36 +6,32 @@
  * @fileoverview Fake implementations of ChromeEvent.
  */
 
-/**
- * @constructor
- * @extends {ChromeEvent}
- */
-function FakeChromeEvent() {
-  /** @type {!Set<!Function>} */
-  this.listeners_ = new Set();
-}
+class FakeChromeEvent {
+  constructor() {
+    /** @type {!Set<!Function>} */
+    this.listeners_ = new Set();
+  }
 
-FakeChromeEvent.prototype = {
   /** @param {Function} listener */
-  addListener: function(listener) {
+  addListener(listener) {
     assertFalse(
         this.listeners_.has(listener),
         'FakeChromeEvent.addListened: Listener already added');
     this.listeners_.add(listener);
-  },
+  }
 
   /** @param {Function} listener */
-  removeListener: function(listener) {
+  removeListener(listener) {
     assertTrue(
         this.listeners_.has(listener),
         'FakeChromeEvent.removeListener: Listener does not exist');
     this.listeners_.delete(listener);
-  },
+  }
 
   /** @param {...} args */
-  callListeners: function(...var_args) {
+  callListeners(...var_args) {
     this.listeners_.forEach(function(l) {
       l.apply(null, var_args);
     });
   }
-};
+}
diff --git a/chrome/test/data/webui/menu_test.html b/chrome/test/data/webui/menu_test.html
index 59fed04..005dcb6 100644
--- a/chrome/test/data/webui/menu_test.html
+++ b/chrome/test/data/webui/menu_test.html
@@ -33,7 +33,7 @@
     return cr.ui.Menu.prototype.findMenuItem_.apply(this, arguments);
   };
 
-  var over = new MouseEvent('mouseover', {bubbles: true, target: cr.doc.body});
+  var over = new MouseEvent('mouseover', {bubbles: true, target: document.body});
   assertFalse(called);
   menu.dispatchEvent(over);
   assertTrue(called);
diff --git a/chrome/test/data/webui/mock_controller.js b/chrome/test/data/webui/mock_controller.js
index 6405a81..3f344109 100644
--- a/chrome/test/data/webui/mock_controller.js
+++ b/chrome/test/data/webui/mock_controller.js
@@ -5,81 +5,81 @@
 /**
  * Create a mock function that records function calls and validates against
  * expectations.
- * @constructor.
  */
-function MockMethod() {
-  var fn = function() {
-    var args = Array.prototype.slice.call(arguments);
-    var callbacks = args.filter(function(arg) {
-      return (typeof arg == 'function');
-    });
+class MockMethod {
+  constructor() {
+    var fn = function() {
+      var args = Array.prototype.slice.call(arguments);
+      var callbacks = args.filter(function(arg) {
+        return (typeof arg == 'function');
+      });
 
-    if (callbacks.length > 1) {
-      console.error('Only support mocking function with at most one callback.');
-      return;
-    }
+      if (callbacks.length > 1) {
+        console.error(
+            'Only support mocking function with at most one callback.');
+        return;
+      }
 
-    fn.recordCall(args);
-    if (callbacks.length == 1) {
-      callbacks[0].apply(undefined, fn.callbackData);
-      return;
-    }
-    return fn.returnValue;
-  };
+      fn.recordCall(args);
+      if (callbacks.length == 1) {
+        callbacks[0].apply(undefined, fn.callbackData);
+        return;
+      }
+      return fn.returnValue;
+    };
 
-  /**
-   * List of signatures for fucntion calls.
-   * @type {!Array<!Array>}
-   * @private
-   */
-  fn.calls_ = [];
+    /**
+     * List of signatures for function calls.
+     * @type {!Array<!Array>}
+     * @private
+     */
+    fn.calls_ = [];
 
-  /**
-   * List of expected call signatures.
-   * @type {!Array<!Array>}
-   * @private
-   */
-  fn.expectations_ = [];
+    /**
+     * List of expected call signatures.
+     * @type {!Array<!Array>}
+     * @private
+     */
+    fn.expectations_ = [];
 
-  /**
-   * Value returned from call to function.
-   * @type {*}
-   */
-  fn.returnValue = undefined;
+    /**
+     * Value returned from call to function.
+     * @type {*}
+     */
+    fn.returnValue = undefined;
 
-  /**
-   * List of arguments for callback function.
-   * @type {!Array<!Array>}
-   */
-  fn.callbackData = [];
+    /**
+     * List of arguments for callback function.
+     * @type {!Array<!Array>}
+     */
+    fn.callbackData = [];
 
-  fn.__proto__ = MockMethod.prototype;
-  return fn;
-}
+    Object.setPrototypeOf(fn, MockMethod.prototype);
+    return fn;
+  }
 
-MockMethod.prototype = {
   /**
    * Adds an expected call signature.
    * @param {...}  var_args Expected arguments for the function call.
    */
-  addExpectation: function() {
+  addExpectation() {
     var args = Array.prototype.slice.call(arguments);
     this.expectations_.push(args.filter(this.notFunction_));
-  },
+  }
 
   /**
    * Adds a call signature.
    * @param {!Array} args.
    */
-  recordCall: function(args) {
+  recordCall(args) {
     this.calls_.push(args.filter(this.notFunction_));
-  },
+  }
 
   /**
    * Verifies that the function is called the expected number of times and with
    * the correct signature for each call.
    */
-  verifyMock: function() {
+  verifyMock() {
     var errorMessage = 'Number of method calls did not match expectation.';
     if (this.functionName) {
       errorMessage = 'Error in ' + this.functionName + ':\n' + errorMessage;
@@ -88,7 +88,7 @@
     for (var i = 0; i < this.expectations_.length; i++) {
       this.validateCall(i, this.expectations_[i], this.calls_[i]);
     }
-  },
+  }
 
   /**
    * Verifies that the observed function arguments match expectations.
@@ -99,43 +99,42 @@
    * @param {!Array} expected The expected arguments.
    * @parma {!Array} observed The observed arguments.
    */
-  validateCall: function(index, expected, observed) {
+  validateCall(index, expected, observed) {
     assertDeepEquals(expected, observed);
-  },
+  }
 
   /**
    * Test if arg is a function.
    * @param {*} arg The argument to test.
    * @return True if arg is not function type.
    */
-  notFunction_: function(arg) {
+  notFunction_(arg) {
     return typeof arg != 'function';
   }
-};
+}
 
 /**
  * Controller for mocking methods. Tracks calls to mocked methods and verifies
  * that call signatures match expectations.
- * @constructor.
  */
-function MockController() {
-  /**
-   * Original functions implementations, which are restored when |reset| is
-   * called.
-   * @type {!Array<!Object>}
-   * @private
-   */
-  this.overrides_ = [];
+class MockController {
+  constructor() {
+    /**
+     * Original functions implementations, which are restored when |reset| is
+     * called.
+     * @type {!Array<!Object>}
+     * @private
+     */
+    this.overrides_ = [];
 
-  /**
-   * List of registered mocks.
-   * @type {!Array<!MockMethod>}
-   * @private
-   */
-  this.mocks_ = [];
-}
+    /**
+     * List of registered mocks.
+     * @type {!Array<!MockMethod>}
+     * @private
+     */
+    this.mocks_ = [];
+  }
 
-MockController.prototype = {
   /**
    * Creates a mock function.
    * @param {Object=} opt_parent Optional parent object for the function.
@@ -144,7 +143,7 @@
    *     mock is automatically substituted for the original and replaced on
    *     reset.
    */
-  createFunctionMock: function(opt_parent, opt_functionName) {
+  createFunctionMock(opt_parent, opt_functionName) {
     var fn = new MockMethod();
 
     // Register mock.
@@ -160,26 +159,25 @@
     this.mocks_.push(fn);
 
     return fn;
-  },
+  }
 
   /**
    * Validates all mocked methods. An exception is thrown if the
    * expected and actual calls to a mocked function to not align.
    */
-  verifyMocks: function() {
+  verifyMocks() {
     for (var i = 0; i < this.mocks_.length; i++) {
       this.mocks_[i].verifyMock();
     }
-  },
+  }
 
   /**
    * Discard mocks reestoring default behavior.
    */
-  reset: function() {
+  reset() {
     for (var i = 0; i < this.overrides_.length; i++) {
       var override = this.overrides_[i];
       override.parent[override.functionName] = override.originalFunction;
     }
-  },
-
-};
+  }
+}
diff --git a/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js
index 0dc2b7f..a67852d 100644
--- a/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js
@@ -173,10 +173,12 @@
           name: 'vpn3',
           type: mojom.NetworkType.kVPN,
           connectionState: mojom.ConnectionStateType.kNotConnected,
-          vpn: {
-            type: mojom.VpnType.kExtension,
-            providerId: 'extension_id1',
-            providerName: 'MyExntensionVPN1',
+          typeState: {
+            vpn: {
+              type: mojom.VpnType.kExtension,
+              providerId: 'extension_id1',
+              providerName: 'MyExntensionVPN1',
+            }
           }
         },
         {
@@ -184,10 +186,12 @@
           name: 'vpn4',
           type: mojom.NetworkType.kVPN,
           connectionState: mojom.ConnectionStateType.kNotConnected,
-          vpn: {
-            type: mojom.VpnType.kExtension,
-            providerId: 'extension_id1',
-            providerName: 'MyExntensionVPN1',
+          typeState: {
+            vpn: {
+              type: mojom.VpnType.kExtension,
+              providerId: 'extension_id1',
+              providerName: 'MyExntensionVPN1',
+            }
           }
         },
         {
@@ -195,10 +199,12 @@
           name: 'vpn5',
           type: mojom.NetworkType.kVPN,
           connectionState: mojom.ConnectionStateType.kNotConnected,
-          vpn: {
-            type: mojom.VpnType.kExtension,
-            providerId: 'extension_id2',
-            providerName: 'MyExntensionVPN2',
+          typeState: {
+            vpn: {
+              type: mojom.VpnType.kExtension,
+              providerId: 'extension_id2',
+              providerName: 'MyExntensionVPN2',
+            }
           }
         },
         {
@@ -206,10 +212,12 @@
           name: 'vpn6',
           type: mojom.NetworkType.kVPN,
           connectionState: mojom.ConnectionStateType.kConnected,
-          vpn: {
-            type: mojom.VpnType.kArc,
-            providerId: 'vpn.app.package1',
-            providerName: 'MyArcVPN1',
+          typeState: {
+            vpn: {
+              type: mojom.VpnType.kArc,
+              providerId: 'vpn.app.package1',
+              providerName: 'MyArcVPN1',
+            }
           }
         },
         {
@@ -217,10 +225,12 @@
           name: 'vpn7',
           type: mojom.NetworkType.kVPN,
           connectionState: mojom.ConnectionStateType.kNotConnected,
-          vpn: {
-            type: mojom.VpnType.kArc,
-            providerId: 'vpn.app.package1',
-            providerName: 'MyArcVPN1',
+          typeState: {
+            vpn: {
+              type: mojom.VpnType.kArc,
+              providerId: 'vpn.app.package1',
+              providerName: 'MyArcVPN1',
+            }
           }
         },
       ]);
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index 4046c4e..1d004b2 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -112,8 +112,7 @@
   }
 };
 
-// Disabled due to flakiness on linux-chromeos-rel. https://crbug.com/992116
-TEST_F('OSSettingsPageTest', 'DISABLED_AllJsTests', () => {
+TEST_F('OSSettingsPageTest', 'AllJsTests', () => {
   // Run all registered tests.
   mocha.run();
 });
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js b/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js
index f0d1a57..9fae323 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js
@@ -2,18 +2,24 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/** @fileoverview Suite of tests for the OS Settings advanced page. */
+/** @fileoverview Suite of tests for the OS Settings main page. */
 
 suite('OSSettingsPage', function() {
   /** @type {?OsSettingsMainElement} */
   let settingsMain = null;
 
-  setup(async () => {
+  suiteSetup(async function() {
+    await CrSettingsPrefs.initialized;
+
     settingsMain =
         document.querySelector('os-settings-ui').$$('os-settings-main');
     assert(!!settingsMain);
-    settingsMain.advancedToggleExpanded = !settingsMain.advancedToggleExpanded;
-    await test_util.flushTasks();
+
+    const idleRender =
+        settingsMain.$$('os-settings-page').$$('settings-idle-load');
+    assert(!!idleRender);
+    await idleRender.get();
+    Polymer.dom.flush();
   });
 
   function getSection(page, section) {
@@ -67,7 +73,12 @@
     }
   }
 
-  test('AdvancedSections', function() {
+  test('AdvancedSections', async function() {
+    // Open the Advanced section.
+    settingsMain.advancedToggleExpanded = true;
+    Polymer.dom.flush();
+    await test_util.flushTasks();
+
     const page = settingsMain.$$('os-settings-page');
     assertTrue(!!page);
     let sections =
@@ -75,7 +86,7 @@
 
     for (let i = 0; i < sections.length; i++) {
       const section = getSection(page, sections[i]);
-      assertTrue(!!section);
+      assertTrue(!!section, 'Did not find ' + sections[i]);
       verifySubpagesHidden(section);
     }
   });
diff --git a/chrome/test/data/webui/settings/fake_settings_private.js b/chrome/test/data/webui/settings/fake_settings_private.js
index d5bc7cf5..c6fad47 100644
--- a/chrome/test/data/webui/settings/fake_settings_private.js
+++ b/chrome/test/data/webui/settings/fake_settings_private.js
@@ -17,26 +17,29 @@
    * Fake of chrome.settingsPrivate API. Use by setting
    * CrSettingsPrefs.deferInitialization to true, then passing a
    * FakeSettingsPrivate to settings-prefs#initialize().
-   * @constructor
-   * @param {Array<!settings.FakeSettingsPrivate.Pref>=} opt_initialPrefs
    * @implements {SettingsPrivate}
    */
-  function FakeSettingsPrivate(opt_initialPrefs) {
-    this.prefs = {};
+  class FakeSettingsPrivate {
+    /** @param {Array<!settings.FakeSettingsPrivate.Pref>=} opt_initialPrefs */
+    constructor(opt_initialPrefs) {
+      this.disallowSetPref_ = false;
+      this.failNextSetPref_ = false;
 
-    if (!opt_initialPrefs) {
-      return;
-    }
-    for (const pref of opt_initialPrefs) {
-      this.addPref_(pref.type, pref.key, pref.value);
-    }
-  }
+      this.prefs = {};
 
-  FakeSettingsPrivate.prototype = {
+      if (!opt_initialPrefs) {
+        return;
+      }
+      for (const pref of opt_initialPrefs) {
+        this.addPref_(pref.type, pref.key, pref.value);
+      }
+
+      // chrome.settingsPrivate override.
+      this.onPrefsChanged = new FakeChromeEvent();
+    }
+
     // chrome.settingsPrivate overrides.
-    onPrefsChanged: new FakeChromeEvent(),
-
-    getAllPrefs: function(callback) {
+    getAllPrefs(callback) {
       // Send a copy of prefs to keep our internal state private.
       const prefs = [];
       for (const key in this.prefs) {
@@ -46,9 +49,9 @@
       // Run the callback asynchronously to test that the prefs aren't actually
       // used before they become available.
       setTimeout(callback.bind(null, prefs));
-    },
+    }
 
-    setPref: function(key, value, pageId, callback) {
+    setPref(key, value, pageId, callback) {
       const pref = this.prefs[key];
       assertNotEquals(undefined, pref);
       assertEquals(typeof value, typeof pref.value);
@@ -69,35 +72,35 @@
       if (changed) {
         this.sendPrefChanges([{key: key, value: deepCopy(value)}]);
       }
-    },
+    }
 
-    getPref: function(key, callback) {
+    getPref(key, callback) {
       const pref = this.prefs[key];
       assertNotEquals(undefined, pref);
       callback(deepCopy(pref));
-    },
+    }
 
     // Functions used by tests.
 
     /** Instructs the API to return a failure when setPref is next called. */
-    failNextSetPref: function() {
+    failNextSetPref() {
       this.failNextSetPref_ = true;
-    },
+    }
 
     /** Instructs the API to assert (fail the test) if setPref is called. */
-    disallowSetPref: function() {
+    disallowSetPref() {
       this.disallowSetPref_ = true;
-    },
+    }
 
-    allowSetPref: function() {
+    allowSetPref() {
       this.disallowSetPref_ = false;
-    },
+    }
 
     /**
      * Notifies the listeners of pref changes.
      * @param {!Object<{key: string, value: *}>} changes
      */
-    sendPrefChanges: function(changes) {
+    sendPrefChanges(changes) {
       const prefs = [];
       for (const change of changes) {
         const pref = this.prefs[change.key];
@@ -106,7 +109,7 @@
         prefs.push(deepCopy(pref));
       }
       this.onPrefsChanged.callListeners(prefs);
-    },
+    }
 
     // Private methods for use by the fake API.
 
@@ -114,15 +117,16 @@
      * @param {!chrome.settingsPrivate.PrefType} type
      * @param {string} key
      * @param {*} value
+     * @private
      */
-    addPref_: function(type, key, value) {
+    addPref_(type, key, value) {
       this.prefs[key] = {
         type: type,
         key: key,
         value: value,
       };
-    },
-  };
+    }
+  }
 
   return {FakeSettingsPrivate: FakeSettingsPrivate};
 });
diff --git a/chrome/test/data/webui/welcome/app_chooser_test.js b/chrome/test/data/webui/welcome/app_chooser_test.js
index c82fb79..3bef6e0 100644
--- a/chrome/test/data/webui/welcome/app_chooser_test.js
+++ b/chrome/test/data/webui/welcome/app_chooser_test.js
@@ -71,16 +71,8 @@
 
     PolymerTest.clearBody();
 
-    // Add <base> so that images in nux-google-apps will be loaded from the
-    // correct data source.
-    const base = document.createElement('base');
-    base.href = 'chrome://welcome/google_apps/';
-    document.head.appendChild(base);
     testElement = document.createElement('nux-google-apps');
     document.body.appendChild(testElement);
-    // Remove <base> so that routing happens from a base of chrome://test, to
-    // prevent a security error.
-    document.head.removeChild(base);
 
     // Simulate nux-app's onRouteEnter call.
     testElement.onRouteEnter();
diff --git a/chrome/test/data/webui/welcome/nux_ntp_background_test.js b/chrome/test/data/webui/welcome/nux_ntp_background_test.js
index beda88e..5815b86 100644
--- a/chrome/test/data/webui/welcome/nux_ntp_background_test.js
+++ b/chrome/test/data/webui/welcome/nux_ntp_background_test.js
@@ -17,13 +17,13 @@
       title: 'Art',
       /* Image URLs are set to actual static images to prevent requesting
        * an external image. */
-      imageUrl: 'chrome://welcome/images/ntp_thumbnails/art.jpg',
+      imageUrl: './images/ntp_thumbnails/art.jpg',
       thumbnailClass: 'art',
     },
     {
       id: 1,
       title: 'Cityscape',
-      imageUrl: 'chrome://welcome/images/ntp_thumbnails/cityscape.jpg',
+      imageUrl: './images/ntp_thumbnails/cityscape.jpg',
       thumbnailClass: 'cityscape',
     },
   ];
@@ -49,16 +49,8 @@
     testNtpBackgroundProxy.setBackgroundsList(backgrounds);
 
     PolymerTest.clearBody();
-    // Add <base> so that images in nux-google-apps will be loaded from the
-    // correct data source.
-    const base = document.createElement('base');
-    base.href = 'chrome://welcome/google_apps/';
-    document.head.appendChild(base);
     testElement = document.createElement('nux-ntp-background');
     document.body.appendChild(testElement);
-    // Remove <base> so that routing happens from a base of chrome://test, to
-    // prevent a security error.
-    document.head.removeChild(base);
 
     testElement.onRouteEnter();
     return Promise.all([
diff --git a/chrome/test/data/webui/welcome/nux_set_as_default_test.js b/chrome/test/data/webui/welcome/nux_set_as_default_test.js
index 59de895..ae425a5 100644
--- a/chrome/test/data/webui/welcome/nux_set_as_default_test.js
+++ b/chrome/test/data/webui/welcome/nux_set_as_default_test.js
@@ -24,9 +24,6 @@
     NuxSetAsDefaultProxyImpl.instance_ = testSetAsDefaultProxy;
 
     PolymerTest.clearBody();
-    const base = document.createElement('base');
-    base.href = 'chrome://welcome/set_as_default/';
-    document.head.appendChild(base);
     testElement = document.createElement('nux-set-as-default');
     document.body.appendChild(testElement);
     let navigateToNextStep;
@@ -35,7 +32,6 @@
       navigateToNextStep = () => resolve();
     });
     testElement.navigateToNextStep_ = navigateToNextStep;
-    document.head.removeChild(base);
   });
 
   teardown(function() {
diff --git a/chrome/test/data/webui/welcome/signin_view_test.js b/chrome/test/data/webui/welcome/signin_view_test.js
index a3e06639..7b11f4a 100644
--- a/chrome/test/data/webui/welcome/signin_view_test.js
+++ b/chrome/test/data/webui/welcome/signin_view_test.js
@@ -25,16 +25,8 @@
     SigninViewProxyImpl.instance_ = new TestSigninViewProxy();
 
     PolymerTest.clearBody();
-    // Add <base> so that images in nux-google-apps will be loaded from the
-    // correct data source.
-    const base = document.createElement('base');
-    base.href = 'chrome://welcome/google_apps/';
-    document.head.appendChild(base);
     testElement = document.createElement('signin-view');
     document.body.appendChild(testElement);
-    // Remove <base> so that routing happens from a base of chrome://test, to
-    // prevent a security error.
-    document.head.removeChild(base);
   });
 
   teardown(function() {
diff --git a/chrome/test/data/webui/welcome/welcome_app_test.js b/chrome/test/data/webui/welcome/welcome_app_test.js
index 00f51d8..3a38e6d 100644
--- a/chrome/test/data/webui/welcome/welcome_app_test.js
+++ b/chrome/test/data/webui/welcome/welcome_app_test.js
@@ -27,25 +27,13 @@
   /** @type {NuxSetAsDefaultProxy} */
   let testSetAsDefaultProxy;
 
-  let base;
-
   function resetTestElement() {
     PolymerTest.clearBody();
-    navigateToForTest(Routes.LANDING, 'landing');
+    navigateTo(Routes.LANDING, 'landing');
     testElement = document.createElement('welcome-app');
     document.body.appendChild(testElement);
   }
 
-  function navigateToForTest(route, name) {
-    if (base) {
-      document.head.removeChild(base);
-    }
-    navigateTo(route, name);
-    base = document.createElement('base');
-    base.href = 'chrome://welcome/';
-    document.head.appendChild(base);
-  }
-
   function simulateCanSetDefault() {
     testSetAsDefaultProxy.setDefaultStatus({
       isDefault: false,
@@ -97,7 +85,7 @@
 
   test('new user route (can set default)', function() {
     simulateCanSetDefault();
-    navigateToForTest(Routes.NEW_USER, 1);
+    navigateTo(Routes.NEW_USER, 1);
     return waitBeforeNextRender(testElement).then(() => {
       const views = testElement.shadowRoot.querySelectorAll('[slot=view]');
       assertEquals(views.length, 5);
@@ -114,7 +102,7 @@
 
   test('new user route (cannot set default)', function() {
     simulateCannotSetDefault();
-    navigateToForTest(Routes.NEW_USER, 1);
+    navigateTo(Routes.NEW_USER, 1);
     return waitBeforeNextRender(testElement).then(() => {
       const views = testElement.shadowRoot.querySelectorAll('[slot=view]');
       assertEquals(views.length, 4);
@@ -130,7 +118,7 @@
 
   test('returning user route (can set default)', function() {
     simulateCanSetDefault();
-    navigateToForTest(Routes.RETURNING_USER, 1);
+    navigateTo(Routes.RETURNING_USER, 1);
     return waitBeforeNextRender(testElement).then(() => {
       const views = testElement.shadowRoot.querySelectorAll('[slot=view]');
       assertEquals(views.length, 2);
@@ -141,7 +129,7 @@
 
   test('returning user route (cannot set default)', function() {
     simulateCannotSetDefault();
-    navigateToForTest(Routes.RETURNING_USER, 1);
+    navigateTo(Routes.RETURNING_USER, 1);
 
     // At this point, there should be no steps in the returning user, so
     // welcome_app should try to go to NTP.
@@ -160,7 +148,7 @@
 
       // Use the new-user route to test if nux-set-as-default module gets
       // initialized.
-      navigateToForTest(Routes.NEW_USER, 1);
+      navigateTo(Routes.NEW_USER, 1);
       return waitBeforeNextRender(testElement).then(() => {
         // Use the existence of the nux-set-as-default as indication of
         // whether or not the promise is resolved with the expected result.
diff --git a/chrome/test/data/webui/welcome/welcome_browsertest.js b/chrome/test/data/webui/welcome/welcome_browsertest.js
index 15fec502..9d3843b 100644
--- a/chrome/test/data/webui/welcome/welcome_browsertest.js
+++ b/chrome/test/data/webui/welcome/welcome_browsertest.js
@@ -41,7 +41,7 @@
 var WelcomeAppChooserTest = class extends WelcomeBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=welcome/app_chooser_test.js';
+    return 'chrome://welcome/test_loader.html?module=welcome/app_chooser_test.js';
   }
 };
 
@@ -53,7 +53,7 @@
 var WelcomeWelcomeAppTest = class extends WelcomeBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=welcome/welcome_app_test.js';
+    return 'chrome://welcome/test_loader.html?module=welcome/welcome_app_test.js';
   }
 };
 
@@ -65,7 +65,7 @@
 var WelcomeSigninViewTest = class extends WelcomeBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=welcome/signin_view_test.js';
+    return 'chrome://welcome/test_loader.html?module=welcome/signin_view_test.js';
   }
 };
 
@@ -77,7 +77,7 @@
 var WelcomeNavigationBehaviorTest = class extends WelcomeBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=welcome/navigation_behavior_test.js';
+    return 'chrome://welcome/test_loader.html?module=welcome/navigation_behavior_test.js';
   }
 };
 
@@ -89,7 +89,7 @@
 var WelcomeModuleMetricsTest = class extends WelcomeBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=welcome/module_metrics_test.js';
+    return 'chrome://welcome/test_loader.html?module=welcome/module_metrics_test.js';
   }
 };
 
@@ -101,7 +101,7 @@
 var WelcomeSetAsDefaultTest = class extends WelcomeBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=welcome/nux_set_as_default_test.js';
+    return 'chrome://welcome/test_loader.html?module=welcome/nux_set_as_default_test.js';
   }
 };
 
@@ -113,7 +113,7 @@
 var WelcomeNtpBackgroundTest = class extends WelcomeBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test?module=welcome/nux_ntp_background_test.js';
+    return 'chrome://welcome/test_loader.html?module=welcome/nux_ntp_background_test.js';
   }
 };
 
diff --git a/chromecast/browser/webview/webview_layout_manager.cc b/chromecast/browser/webview/webview_layout_manager.cc
index 24d2319..d781b86 100644
--- a/chromecast/browser/webview/webview_layout_manager.cc
+++ b/chromecast/browser/webview/webview_layout_manager.cc
@@ -14,7 +14,8 @@
 WebviewLayoutManager::~WebviewLayoutManager() {}
 
 void WebviewLayoutManager::OnWindowResized() {
-  web_contents_window_->SetBounds(web_contents_window_->parent()->bounds());
+  web_contents_window_->SetBounds(
+      gfx::Rect(web_contents_window_->parent()->bounds().size()));
 }
 
 void WebviewLayoutManager::OnWindowAddedToLayout(aura::Window* child) {}
diff --git a/chromeos/components/media_app_ui/resources/app.html b/chromeos/components/media_app_ui/resources/app.html
index 7207aa88..df0f4e0 100644
--- a/chromeos/components/media_app_ui/resources/app.html
+++ b/chromeos/components/media_app_ui/resources/app.html
@@ -8,7 +8,6 @@
     margin: 0;
   }
 </style>
-<backlight-app></backlight-app>
 <script src="/js/app_main.js"></script>
 <!-- TODO(crbug/996088): Include scripts to run in the <webview> sandbox that
      communicate via postMessage to scripts running in index.html -->
diff --git a/chromeos/dbus/fake_update_engine_client.cc b/chromeos/dbus/fake_update_engine_client.cc
index cc63ce1..c411c53 100644
--- a/chromeos/dbus/fake_update_engine_client.cc
+++ b/chromeos/dbus/fake_update_engine_client.cc
@@ -9,13 +9,7 @@
 
 namespace chromeos {
 
-FakeUpdateEngineClient::FakeUpdateEngineClient()
-    : update_check_result_(UpdateEngineClient::UPDATE_RESULT_SUCCESS),
-      can_rollback_stub_result_(false),
-      reboot_after_update_call_count_(0),
-      request_update_check_call_count_(0),
-      rollback_call_count_(0),
-      can_rollback_call_count_(0) {}
+FakeUpdateEngineClient::FakeUpdateEngineClient() {}
 
 FakeUpdateEngineClient::~FakeUpdateEngineClient() = default;
 
@@ -94,6 +88,7 @@
 void FakeUpdateEngineClient::SetUpdateOverCellularPermission(
     bool allowed,
     const base::Closure& callback) {
+  update_over_cellular_permission_count_++;
   base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
 }
 
@@ -101,6 +96,7 @@
     const std::string& target_version,
     int64_t target_size,
     const UpdateOverCellularOneTimePermissionCallback& callback) {
+  update_over_cellular_one_time_permission_count_++;
   callback.Run(true);
 }
 
diff --git a/chromeos/dbus/fake_update_engine_client.h b/chromeos/dbus/fake_update_engine_client.h
index 40a379d..957b07d4 100644
--- a/chromeos/dbus/fake_update_engine_client.h
+++ b/chromeos/dbus/fake_update_engine_client.h
@@ -84,19 +84,32 @@
   // Returns how many times Rollback() is called.
   int rollback_call_count() const { return rollback_call_count_; }
 
-  // Returns how many times Rollback() is called.
+  // Returns how many times CanRollback() is called.
   int can_rollback_call_count() const { return can_rollback_call_count_; }
 
+  // Returns how many times |SetUpdateOverCellularPermission()| is called.
+  int update_over_cellular_permission_count() const {
+    return update_over_cellular_permission_count_;
+  }
+
+  // Returns how many times |SetUpdateOverCellularOneTimePermission()| is
+  // called.
+  int update_over_cellular_one_time_permission_count() const {
+    return update_over_cellular_one_time_permission_count_;
+  }
+
  private:
   base::ObserverList<Observer>::Unchecked observers_;
   base::queue<update_engine::StatusResult> status_queue_;
   update_engine::StatusResult default_status_;
-  UpdateEngineClient::UpdateCheckResult update_check_result_;
-  bool can_rollback_stub_result_;
-  int reboot_after_update_call_count_;
-  int request_update_check_call_count_;
-  int rollback_call_count_;
-  int can_rollback_call_count_;
+  UpdateCheckResult update_check_result_ = UPDATE_RESULT_SUCCESS;
+  bool can_rollback_stub_result_ = false;
+  int reboot_after_update_call_count_ = 0;
+  int request_update_check_call_count_ = 0;
+  int rollback_call_count_ = 0;
+  int can_rollback_call_count_ = 0;
+  int update_over_cellular_permission_count_ = 0;
+  int update_over_cellular_one_time_permission_count_ = 0;
 };
 
 }  // namespace chromeos
diff --git a/chromeos/network/network_state_test_helper.cc b/chromeos/network/network_state_test_helper.cc
index eace2220..a00693e 100644
--- a/chromeos/network/network_state_test_helper.cc
+++ b/chromeos/network/network_state_test_helper.cc
@@ -159,37 +159,42 @@
     network_config::mojom::NetworkType type,
     network_config::mojom::ConnectionStateType connection_state,
     int signal_strength) {
+  using network_config::mojom::NetworkType;
+  using network_config::mojom::NetworkTypeStateProperties;
   auto network = network_config::mojom::NetworkStateProperties::New();
   network->guid = id;
   network->name = id;
   network->type = type;
   network->connection_state = connection_state;
   switch (type) {
-    case network_config::mojom::NetworkType::kAll:
-    case network_config::mojom::NetworkType::kMobile:
-    case network_config::mojom::NetworkType::kWireless:
+    case NetworkType::kAll:
+    case NetworkType::kMobile:
+    case NetworkType::kWireless:
       NOTREACHED();
       break;
-    case network_config::mojom::NetworkType::kCellular: {
+    case NetworkType::kCellular: {
       auto cellular = network_config::mojom::CellularStateProperties::New();
       cellular->signal_strength = signal_strength;
-      network->cellular = std::move(cellular);
+      network->type_state =
+          NetworkTypeStateProperties::NewCellular(std::move(cellular));
       break;
     }
-    case network_config::mojom::NetworkType::kEthernet:
+    case NetworkType::kEthernet:
       break;
-    case network_config::mojom::NetworkType::kTether: {
+    case NetworkType::kTether: {
       auto tether = network_config::mojom::TetherStateProperties::New();
       tether->signal_strength = signal_strength;
-      network->tether = std::move(tether);
+      network->type_state =
+          NetworkTypeStateProperties::NewTether(std::move(tether));
       break;
     }
-    case network_config::mojom::NetworkType::kVPN:
+    case NetworkType::kVPN:
       break;
-    case network_config::mojom::NetworkType::kWiFi: {
+    case NetworkType::kWiFi: {
       auto wifi = network_config::mojom::WiFiStateProperties::New();
       wifi->signal_strength = signal_strength;
-      network->wifi = std::move(wifi);
+      network->type_state =
+          NetworkTypeStateProperties::NewWifi(std::move(wifi));
       break;
     }
   }
diff --git a/chromeos/profiles/orderfile.newest.txt b/chromeos/profiles/orderfile.newest.txt
index d64caf44..e7667a6e 100644
--- a/chromeos/profiles/orderfile.newest.txt
+++ b/chromeos/profiles/orderfile.newest.txt
@@ -1 +1 @@
-chromeos-chrome-orderfile-field-79-3903.0-1568628088-benchmark-78.0.3902.0-r1.orderfile.xz
\ No newline at end of file
+chromeos-chrome-orderfile-field-79-3904.12-1569231291-benchmark-78.0.3902.0-r1.orderfile.xz
\ No newline at end of file
diff --git a/chromeos/services/assistant/platform/network_provider_impl.cc b/chromeos/services/assistant/platform/network_provider_impl.cc
index 6dc6d199..7cf6b68 100644
--- a/chromeos/services/assistant/platform/network_provider_impl.cc
+++ b/chromeos/services/assistant/platform/network_provider_impl.cc
@@ -25,9 +25,8 @@
     return;
   client->RequestNetworkConfig(
       cros_network_config_remote_.BindNewPipeAndPassReceiver());
-  network_config::mojom::CrosNetworkConfigObserverPtr observer_ptr;
-  binding_.Bind(mojo::MakeRequest(&observer_ptr));
-  cros_network_config_remote_->AddObserver(std::move(observer_ptr));
+  cros_network_config_remote_->AddObserver(
+      receiver_.BindNewPipeAndPassRemote());
   cros_network_config_remote_->GetNetworkStateList(
       network_config::mojom::NetworkFilter::New(
           network_config::mojom::FilterType::kActive,
diff --git a/chromeos/services/assistant/platform/network_provider_impl.h b/chromeos/services/assistant/platform/network_provider_impl.h
index a463fc94..a3b3482 100644
--- a/chromeos/services/assistant/platform/network_provider_impl.h
+++ b/chromeos/services/assistant/platform/network_provider_impl.h
@@ -9,7 +9,7 @@
 #include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
 #include "libassistant/shared/public/platform_net.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
 namespace chromeos {
@@ -40,7 +40,7 @@
 
  private:
   ConnectionStatus connection_status_;
-  mojo::Binding<network_config::mojom::CrosNetworkConfigObserver> binding_{
+  mojo::Receiver<network_config::mojom::CrosNetworkConfigObserver> receiver_{
       this};
   mojo::Remote<network_config::mojom::CrosNetworkConfig>
       cros_network_config_remote_;
diff --git a/chromeos/services/device_sync/BUILD.gn b/chromeos/services/device_sync/BUILD.gn
index d71d27c..119d03c 100644
--- a/chromeos/services/device_sync/BUILD.gn
+++ b/chromeos/services/device_sync/BUILD.gn
@@ -17,6 +17,10 @@
     "cryptauth_client_impl.h",
     "cryptauth_device.cc",
     "cryptauth_device.h",
+    "cryptauth_device_activity_getter.cc",
+    "cryptauth_device_activity_getter.h",
+    "cryptauth_device_activity_getter_impl.cc",
+    "cryptauth_device_activity_getter_impl.h",
     "cryptauth_device_manager.cc",
     "cryptauth_device_manager.h",
     "cryptauth_device_manager_impl.cc",
@@ -215,6 +219,7 @@
     "//chromeos/services/device_sync/proto:test_support",
     "//chromeos/services/device_sync/public/cpp",
     "//chromeos/services/device_sync/public/mojom",
+    "//chromeos/services/device_sync/public/mojom:unit_tests",
     "//testing/gmock",
     "//testing/gtest",
   ]
@@ -226,6 +231,7 @@
   sources = [
     "cryptauth_api_call_flow_unittest.cc",
     "cryptauth_client_impl_unittest.cc",
+    "cryptauth_device_activity_getter_impl_unittest.cc",
     "cryptauth_device_manager_impl_unittest.cc",
     "cryptauth_device_registry_impl_unittest.cc",
     "cryptauth_device_syncer_impl_unittest.cc",
diff --git a/chromeos/services/device_sync/cryptauth_client_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_client_impl_unittest.cc
index f645422..62becdf 100644
--- a/chromeos/services/device_sync/cryptauth_client_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_client_impl_unittest.cc
@@ -60,10 +60,10 @@
 const char kClientMetadataSessionId[] = "session_id";
 const int kLastActivityTimeSecs1 = 111;
 const int kLastActivityTimeSecs2 = 222;
-const cryptauthv2::OnlineStatus kOnlineStatus1 =
-    cryptauthv2::OnlineStatus::ONLINE;
-const cryptauthv2::OnlineStatus kOnlineStatus2 =
-    cryptauthv2::OnlineStatus::OFFLINE;
+const cryptauthv2::ConnectivityStatus kConnectivityStatus1 =
+    cryptauthv2::ConnectivityStatus::ONLINE;
+const cryptauthv2::ConnectivityStatus kConnectivityStatus2 =
+    cryptauthv2::ConnectivityStatus::OFFLINE;
 
 // Values for the DeviceClassifier field.
 const int kDeviceOsVersionCode = 100;
@@ -939,10 +939,10 @@
     cryptauthv2::GetDevicesActivityStatusResponse response;
     response.add_device_activity_statuses()->CopyFrom(
         cryptauthv2::BuildDeviceActivityStatus(
-            kDeviceId1, kLastActivityTimeSecs1, kOnlineStatus1));
+            kDeviceId1, kLastActivityTimeSecs1, kConnectivityStatus1));
     response.add_device_activity_statuses()->CopyFrom(
         cryptauthv2::BuildDeviceActivityStatus(
-            kDeviceId2, kLastActivityTimeSecs2, kOnlineStatus2));
+            kDeviceId2, kLastActivityTimeSecs2, kConnectivityStatus2));
 
     FinishApiCallFlow(&response);
   }
@@ -952,11 +952,13 @@
   EXPECT_EQ(kDeviceId1, result.device_activity_statuses(0).device_id());
   ASSERT_EQ(kLastActivityTimeSecs1,
             result.device_activity_statuses(0).last_activity_time_sec());
-  EXPECT_EQ(kOnlineStatus1, result.device_activity_statuses(0).online_status());
+  EXPECT_EQ(kConnectivityStatus1,
+            result.device_activity_statuses(0).connectivity_status());
   EXPECT_EQ(kDeviceId2, result.device_activity_statuses(1).device_id());
   ASSERT_EQ(kLastActivityTimeSecs2,
             result.device_activity_statuses(1).last_activity_time_sec());
-  EXPECT_EQ(kOnlineStatus2, result.device_activity_statuses(1).online_status());
+  EXPECT_EQ(kConnectivityStatus2,
+            result.device_activity_statuses(1).connectivity_status());
 }
 
 TEST_F(DeviceSyncCryptAuthClientTest, FetchAccessTokenFailure) {
diff --git a/chromeos/services/device_sync/cryptauth_device_activity_getter.cc b/chromeos/services/device_sync/cryptauth_device_activity_getter.cc
new file mode 100644
index 0000000..63b3035b
--- /dev/null
+++ b/chromeos/services/device_sync/cryptauth_device_activity_getter.cc
@@ -0,0 +1,44 @@
+// Copyright 2019 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 "chromeos/services/device_sync/cryptauth_device_activity_getter.h"
+
+#include <utility>
+
+namespace chromeos {
+
+namespace device_sync {
+
+CryptAuthDeviceActivityGetter::CryptAuthDeviceActivityGetter() = default;
+
+CryptAuthDeviceActivityGetter::~CryptAuthDeviceActivityGetter() = default;
+
+void CryptAuthDeviceActivityGetter::GetDevicesActivityStatus(
+    GetDeviceActivityStatusAttemptFinishedCallback success_callback,
+    GetDeviceActivityStatusAttemptErrorCallback error_callback) {
+  // Enforce that GetDevicesActivityStatus() can only be called once.
+  DCHECK(!was_get_device_activity_getter_called_);
+  was_get_device_activity_getter_called_ = true;
+
+  success_callback_ = std::move(success_callback);
+  error_callback_ = std::move(error_callback);
+
+  OnAttemptStarted();
+}
+
+void CryptAuthDeviceActivityGetter::FinishAttemptSuccessfully(
+    DeviceActivityStatusResult device_activity_status) {
+  DCHECK(success_callback_);
+  std::move(success_callback_).Run(std::move(device_activity_status));
+}
+
+void CryptAuthDeviceActivityGetter::FinishAttemptWithError(
+    NetworkRequestError network_request_error) {
+  DCHECK(error_callback_);
+  std::move(error_callback_).Run(network_request_error);
+}
+
+}  // namespace device_sync
+
+}  // namespace chromeos
diff --git a/chromeos/services/device_sync/cryptauth_device_activity_getter.h b/chromeos/services/device_sync/cryptauth_device_activity_getter.h
new file mode 100644
index 0000000..11c6d59c
--- /dev/null
+++ b/chromeos/services/device_sync/cryptauth_device_activity_getter.h
@@ -0,0 +1,63 @@
+// Copyright 2019 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 CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_DEVICE_ACTIVITY_GETTER_H_
+#define CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_DEVICE_ACTIVITY_GETTER_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "chromeos/services/device_sync/network_request_error.h"
+#include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+// Handles the GetDevicesActivityStatus call of the CryptAuth v2 DeviceSync
+// protocol. Returns the device activity statuses for the user's devices.
+//
+// A CryptAuthDeviceActivityGetter object is designed to be used for only one
+// GetDevicesActivityStatus() call. For a new attempt, a new object should be
+// created.
+class CryptAuthDeviceActivityGetter {
+ public:
+  using DeviceActivityStatusResult =
+      std::vector<mojom::DeviceActivityStatusPtr>;
+  using GetDeviceActivityStatusAttemptFinishedCallback =
+      base::OnceCallback<void(DeviceActivityStatusResult)>;
+  using GetDeviceActivityStatusAttemptErrorCallback =
+      base::OnceCallback<void(NetworkRequestError)>;
+
+  virtual ~CryptAuthDeviceActivityGetter();
+
+  // Starts the GetDevicesActivityStatus portion of the CryptAuth v2 DeviceSync
+  // flow, retrieving the user's device activity status.
+  void GetDevicesActivityStatus(
+      GetDeviceActivityStatusAttemptFinishedCallback success_callback,
+      GetDeviceActivityStatusAttemptErrorCallback error_callback);
+
+ protected:
+  CryptAuthDeviceActivityGetter();
+
+  virtual void OnAttemptStarted() = 0;
+
+  void FinishAttemptSuccessfully(
+      DeviceActivityStatusResult device_activity_status);
+  void FinishAttemptWithError(NetworkRequestError network_request_error);
+
+ private:
+  GetDeviceActivityStatusAttemptFinishedCallback success_callback_;
+  GetDeviceActivityStatusAttemptErrorCallback error_callback_;
+  bool was_get_device_activity_getter_called_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(CryptAuthDeviceActivityGetter);
+};
+
+}  // namespace device_sync
+
+}  // namespace chromeos
+
+#endif  //  CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_DEVICE_ACTIVITY_GETTER_H_
diff --git a/chromeos/services/device_sync/cryptauth_device_activity_getter_impl.cc b/chromeos/services/device_sync/cryptauth_device_activity_getter_impl.cc
new file mode 100644
index 0000000..22adc14
--- /dev/null
+++ b/chromeos/services/device_sync/cryptauth_device_activity_getter_impl.cc
@@ -0,0 +1,227 @@
+// Copyright 2019 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 "chromeos/services/device_sync/cryptauth_device_activity_getter_impl.h"
+
+#include <array>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/no_destructor.h"
+#include "base/stl_util.h"
+#include "base/time/time.h"
+#include "chromeos/components/multidevice/logging/logging.h"
+#include "chromeos/components/multidevice/software_feature.h"
+#include "chromeos/components/multidevice/software_feature_state.h"
+#include "chromeos/services/device_sync/cryptauth_better_together_feature_types.h"
+#include "chromeos/services/device_sync/cryptauth_client.h"
+#include "chromeos/services/device_sync/cryptauth_key_bundle.h"
+#include "chromeos/services/device_sync/device_sync_type_converters.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+namespace {
+
+// Timeout values for the asynchronous operations of fetching client app
+// metadata and making the network request.
+// TODO(https://crbug.com/933656): Tune this value.
+constexpr base::TimeDelta kWaitingForClientAppMetadataTimeout =
+    base::TimeDelta::FromSeconds(60);
+constexpr base::TimeDelta kWaitingForGetDevicesActivityStatusResponseTimeout =
+    base::TimeDelta::FromSeconds(10);
+
+}  // namespace
+
+// static
+CryptAuthDeviceActivityGetterImpl::Factory*
+    CryptAuthDeviceActivityGetterImpl::Factory::test_factory_ = nullptr;
+
+// static
+void CryptAuthDeviceActivityGetterImpl::Factory::SetFactoryForTesting(
+    Factory* test_factory) {
+  test_factory_ = test_factory;
+}
+
+// static
+base::Optional<base::TimeDelta>
+CryptAuthDeviceActivityGetterImpl::GetTimeoutForState(State state) {
+  switch (state) {
+    case State::kWaitingForClientAppMetadata:
+      return kWaitingForClientAppMetadataTimeout;
+    case State::kWaitingForGetDevicesActivityStatusResponse:
+      return kWaitingForGetDevicesActivityStatusResponseTimeout;
+    default:
+      // Signifies that there should not be a timeout.
+      return base::nullopt;
+  }
+}
+
+CryptAuthDeviceActivityGetterImpl::Factory::~Factory() = default;
+
+std::unique_ptr<CryptAuthDeviceActivityGetter>
+CryptAuthDeviceActivityGetterImpl::Factory::Create(
+    CryptAuthClientFactory* client_factory,
+    ClientAppMetadataProvider* client_app_metadata_provider,
+    CryptAuthGCMManager* gcm_manager,
+    std::unique_ptr<base::OneShotTimer> timer) {
+  if (test_factory_)
+    return test_factory_->BuildInstance(client_factory,
+                                        client_app_metadata_provider,
+                                        gcm_manager, std::move(timer));
+
+  return base::WrapUnique(new CryptAuthDeviceActivityGetterImpl(
+      client_factory, client_app_metadata_provider, gcm_manager,
+      std::move(timer)));
+}
+
+CryptAuthDeviceActivityGetterImpl::CryptAuthDeviceActivityGetterImpl(
+    CryptAuthClientFactory* client_factory,
+    ClientAppMetadataProvider* client_app_metadata_provider,
+    CryptAuthGCMManager* gcm_manager,
+    std::unique_ptr<base::OneShotTimer> timer)
+    : client_factory_(client_factory),
+      client_app_metadata_provider_(client_app_metadata_provider),
+      gcm_manager_(gcm_manager),
+      timer_(std::move(timer)) {
+  DCHECK(client_factory);
+}
+
+CryptAuthDeviceActivityGetterImpl::~CryptAuthDeviceActivityGetterImpl() =
+    default;
+
+void CryptAuthDeviceActivityGetterImpl::SetState(State state) {
+  timer_->Stop();
+
+  PA_LOG(INFO) << "Transitioning from " << state_ << " to " << state;
+  state_ = state;
+
+  base::Optional<base::TimeDelta> timeout_for_state = GetTimeoutForState(state);
+  if (!timeout_for_state)
+    return;
+
+  // TODO(https://crbug.com/936273): Add metrics to track failure rates due to
+  // async timeouts.
+  timer_->Start(FROM_HERE, *timeout_for_state,
+                base::BindOnce(&CryptAuthDeviceActivityGetterImpl::OnTimeout,
+                               base::Unretained(this)));
+}
+
+void CryptAuthDeviceActivityGetterImpl::OnAttemptStarted() {
+  // GCM registration is expected to be completed before the first enrollment.
+  DCHECK(!gcm_manager_->GetRegistrationId().empty())
+      << "Device activity status requested before GCM registration complete.";
+  SetState(State::kWaitingForClientAppMetadata);
+
+  client_app_metadata_provider_->GetClientAppMetadata(
+      gcm_manager_->GetRegistrationId(),
+      base::BindOnce(
+          &CryptAuthDeviceActivityGetterImpl::OnClientAppMetadataFetched,
+          callback_weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CryptAuthDeviceActivityGetterImpl::OnClientAppMetadataFetched(
+    const base::Optional<cryptauthv2::ClientAppMetadata>& client_app_metadata) {
+  DCHECK(state_ == State::kWaitingForClientAppMetadata);
+
+  if (!client_app_metadata) {
+    OnAttemptError(NetworkRequestError::kUnknown);
+    return;
+  }
+
+  cryptauthv2::GetDevicesActivityStatusRequest request;
+
+  request.mutable_context()->mutable_client_metadata()->set_retry_count(0);
+  request.mutable_context()->mutable_client_metadata()->set_invocation_reason(
+      cryptauthv2::ClientMetadata::INVOCATION_REASON_UNSPECIFIED);
+
+  request.mutable_context()->set_group(
+      CryptAuthKeyBundle::KeyBundleNameEnumToString(
+          CryptAuthKeyBundle::Name::kDeviceSyncBetterTogether));
+  request.mutable_context()->set_device_id(
+      client_app_metadata.value().instance_id());
+
+  // TODO(https://crbug.com/990430): This is the IID token for the v2 Enrollment
+  // service. A different IID token for the v2 DeviceSync service will be
+  // necessary.
+  request.mutable_context()->set_device_id_token(
+      client_app_metadata.value().instance_id_token());
+
+  SetState(State::kWaitingForGetDevicesActivityStatusResponse);
+
+  cryptauth_client_ = client_factory_->CreateInstance();
+  cryptauth_client_->GetDevicesActivityStatus(
+      request,
+      base::Bind(
+          &CryptAuthDeviceActivityGetterImpl::OnGetDevicesActivityStatusSuccess,
+          base::Unretained(this)),
+      base::Bind(
+          &CryptAuthDeviceActivityGetterImpl::OnGetDevicesActivityStatusFailure,
+          base::Unretained(this)));
+}
+
+void CryptAuthDeviceActivityGetterImpl::OnGetDevicesActivityStatusSuccess(
+    const cryptauthv2::GetDevicesActivityStatusResponse& response) {
+  DCHECK(state_ == State::kWaitingForGetDevicesActivityStatusResponse);
+
+  DeviceActivityStatusResult device_activity_statuses;
+
+  for (const cryptauthv2::DeviceActivityStatus& device_activity_status :
+       response.device_activity_statuses()) {
+    device_activity_statuses.emplace_back(mojom::DeviceActivityStatus::New(
+        device_activity_status.device_id(),
+        base::Time::FromTimeT(device_activity_status.last_activity_time_sec()),
+        std::move(device_activity_status.connectivity_status())));
+  }
+
+  cryptauth_client_.reset();
+  SetState(State::kFinished);
+  FinishAttemptSuccessfully(std::move(device_activity_statuses));
+}
+
+void CryptAuthDeviceActivityGetterImpl::OnGetDevicesActivityStatusFailure(
+    NetworkRequestError error) {
+  DCHECK(state_ == State::kWaitingForGetDevicesActivityStatusResponse);
+  OnAttemptError(error);
+}
+
+void CryptAuthDeviceActivityGetterImpl::OnTimeout() {
+  OnAttemptError(NetworkRequestError::kUnknown);
+}
+
+void CryptAuthDeviceActivityGetterImpl::OnAttemptError(
+    NetworkRequestError error) {
+  cryptauth_client_.reset();
+  SetState(State::kFinished);
+  FinishAttemptWithError(error);
+}
+
+std::ostream& operator<<(
+    std::ostream& stream,
+    const CryptAuthDeviceActivityGetterImpl::State& state) {
+  switch (state) {
+    case CryptAuthDeviceActivityGetterImpl::State::kNotStarted:
+      stream << "[DeviceActivityGetter state: Not started]";
+      break;
+    case CryptAuthDeviceActivityGetterImpl::State::kWaitingForClientAppMetadata:
+      stream << "[DeviceActivityGetter state: Waiting for client app metadata]";
+      break;
+    case CryptAuthDeviceActivityGetterImpl::State::
+        kWaitingForGetDevicesActivityStatusResponse:
+      stream << "[DeviceActivityGetter state: Waiting for "
+             << "GetDevicesActivityStatus response]";
+      break;
+    case CryptAuthDeviceActivityGetterImpl::State::kFinished:
+      stream << "[DeviceActivityGetter state: Finished]";
+      break;
+  }
+
+  return stream;
+}
+
+}  // namespace device_sync
+
+}  // namespace chromeos
diff --git a/chromeos/services/device_sync/cryptauth_device_activity_getter_impl.h b/chromeos/services/device_sync/cryptauth_device_activity_getter_impl.h
new file mode 100644
index 0000000..d064e22f
--- /dev/null
+++ b/chromeos/services/device_sync/cryptauth_device_activity_getter_impl.h
@@ -0,0 +1,110 @@
+// Copyright 2019 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 CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_DEVICE_ACTIVITY_GETTER_IMPL_H_
+#define CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_DEVICE_ACTIVITY_GETTER_IMPL_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/timer/timer.h"
+#include "chromeos/services/device_sync/cryptauth_device_activity_getter.h"
+#include "chromeos/services/device_sync/cryptauth_gcm_manager.h"
+#include "chromeos/services/device_sync/network_request_error.h"
+#include "chromeos/services/device_sync/proto/cryptauth_client_app_metadata.pb.h"
+#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "chromeos/services/device_sync/public/cpp/client_app_metadata_provider.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+class CryptAuthClient;
+class CryptAuthClientFactory;
+
+// An implementation of CryptAuthDeviceActivityGetter, using instances of
+// CryptAuthClient to make the GetDevicesActivityStatus API calls to CryptAuth.
+// This implementation handles timeouts internally, so the callback passed to
+// CryptAuthDeviceActivityGetter::GetDevicesActivityStatus() is always
+// guaranteed to be invoked.
+class CryptAuthDeviceActivityGetterImpl : public CryptAuthDeviceActivityGetter {
+ public:
+  class Factory {
+   public:
+    static std::unique_ptr<CryptAuthDeviceActivityGetter> Create(
+        CryptAuthClientFactory* client_factory,
+        ClientAppMetadataProvider* client_app_metadata_provider,
+        CryptAuthGCMManager* gcm_manager,
+        std::unique_ptr<base::OneShotTimer> timer =
+            std::make_unique<base::OneShotTimer>());
+    static void SetFactoryForTesting(Factory* test_factory);
+    virtual ~Factory();
+    virtual std::unique_ptr<CryptAuthDeviceActivityGetter> BuildInstance(
+        CryptAuthClientFactory* client_factory,
+        ClientAppMetadataProvider* client_app_metadata_provider,
+        CryptAuthGCMManager* gcm_manager,
+        std::unique_ptr<base::OneShotTimer> timer) = 0;
+
+   private:
+    static Factory* test_factory_;
+  };
+
+  ~CryptAuthDeviceActivityGetterImpl() override;
+
+ private:
+  enum class State {
+    kNotStarted,
+    kWaitingForClientAppMetadata,
+    kWaitingForGetDevicesActivityStatusResponse,
+    kFinished
+  };
+
+  friend std::ostream& operator<<(std::ostream& stream, const State& state);
+
+  CryptAuthDeviceActivityGetterImpl(
+      CryptAuthClientFactory* client_factory,
+      ClientAppMetadataProvider* client_app_metadata_provider,
+      CryptAuthGCMManager* gcm_manager,
+      std::unique_ptr<base::OneShotTimer> timer);
+
+  // CryptAuthDeviceActivityGetter:
+  void OnAttemptStarted() override;
+  void OnClientAppMetadataFetched(
+      const base::Optional<cryptauthv2::ClientAppMetadata>&
+          client_app_metadata);
+
+  static base::Optional<base::TimeDelta> GetTimeoutForState(State state);
+  void SetState(State state);
+  void OnTimeout();
+
+  void OnGetDevicesActivityStatusSuccess(
+      const cryptauthv2::GetDevicesActivityStatusResponse& response);
+  void OnGetDevicesActivityStatusFailure(NetworkRequestError error);
+
+  void OnAttemptError(NetworkRequestError error);
+
+  // The CryptAuthClient for the latest CryptAuth request. The client can only
+  // be used for one call; therefore, for each API call, a new client needs to
+  // be generated from |client_factory_|.
+  std::unique_ptr<CryptAuthClient> cryptauth_client_;
+
+  State state_ = State::kNotStarted;
+  CryptAuthClientFactory* client_factory_ = nullptr;
+  ClientAppMetadataProvider* client_app_metadata_provider_ = nullptr;
+  CryptAuthGCMManager* gcm_manager_ = nullptr;
+  std::unique_ptr<base::OneShotTimer> timer_;
+  base::WeakPtrFactory<CryptAuthDeviceActivityGetterImpl>
+      callback_weak_ptr_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(CryptAuthDeviceActivityGetterImpl);
+};
+
+}  // namespace device_sync
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_DEVICE_ACTIVITY_GETTER_IMPL_H_
diff --git a/chromeos/services/device_sync/cryptauth_device_activity_getter_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_device_activity_getter_impl_unittest.cc
new file mode 100644
index 0000000..7fa75ee
--- /dev/null
+++ b/chromeos/services/device_sync/cryptauth_device_activity_getter_impl_unittest.cc
@@ -0,0 +1,281 @@
+// Copyright 2019 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 "chromeos/services/device_sync/cryptauth_device_activity_getter_impl.h"
+#include "chromeos/services/device_sync/cryptauth_feature_status_getter_impl.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/containers/flat_set.h"
+#include "base/macros.h"
+#include "base/no_destructor.h"
+#include "base/optional.h"
+#include "base/timer/mock_timer.h"
+#include "chromeos/services/device_sync/cryptauth_client.h"
+#include "chromeos/services/device_sync/cryptauth_device.h"
+#include "chromeos/services/device_sync/cryptauth_device_sync_result.h"
+#include "chromeos/services/device_sync/cryptauth_gcm_manager_impl.h"
+#include "chromeos/services/device_sync/cryptauth_key.h"
+#include "chromeos/services/device_sync/cryptauth_key_bundle.h"
+#include "chromeos/services/device_sync/cryptauth_v2_device_sync_test_devices.h"
+#include "chromeos/services/device_sync/device_sync_type_converters.h"
+#include "chromeos/services/device_sync/fake_cryptauth_gcm_manager.h"
+#include "chromeos/services/device_sync/mock_cryptauth_client.h"
+#include "chromeos/services/device_sync/network_request_error.h"
+#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
+#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "chromeos/services/device_sync/proto/cryptauth_v2_test_util.h"
+#include "chromeos/services/device_sync/public/cpp/fake_client_app_metadata_provider.h"
+#include "components/gcm_driver/fake_gcm_driver.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+namespace {
+
+const char kAccessTokenUsed[] = "access token used by CryptAuthClient";
+const char kTestCryptAuthGCMRegistrationId[] = "cryptAuthRegistrationId";
+const char kDeviceId[] = "device_id1";
+const int kLastActivityTimeSecs = 111;
+const cryptauthv2::ConnectivityStatus kConnectivityStatus =
+    cryptauthv2::ConnectivityStatus::ONLINE;
+
+const cryptauthv2::ClientMetadata& GetClientMetadata() {
+  static const base::NoDestructor<cryptauthv2::ClientMetadata> client_metadata(
+      cryptauthv2::BuildClientMetadata(
+          0 /* retry_count */,
+          cryptauthv2::ClientMetadata::INVOCATION_REASON_UNSPECIFIED));
+  return *client_metadata;
+}
+
+const cryptauthv2::RequestContext& GetRequestContext() {
+  static const base::NoDestructor<cryptauthv2::RequestContext> request_context(
+      [] {
+        return cryptauthv2::BuildRequestContext(
+            CryptAuthKeyBundle::KeyBundleNameEnumToString(
+                CryptAuthKeyBundle::Name::kDeviceSyncBetterTogether),
+            GetClientMetadata(),
+            cryptauthv2::GetClientAppMetadataForTest().instance_id(),
+            cryptauthv2::GetClientAppMetadataForTest().instance_id_token());
+      }());
+  return *request_context;
+}
+
+const cryptauthv2::GetDevicesActivityStatusResponse& GetResponse() {
+  static const base::NoDestructor<cryptauthv2::GetDevicesActivityStatusResponse>
+      activity_response([] {
+        cryptauthv2::GetDevicesActivityStatusResponse response;
+        response.add_device_activity_statuses()->CopyFrom(
+            cryptauthv2::BuildDeviceActivityStatus(
+                kDeviceId, kLastActivityTimeSecs, kConnectivityStatus));
+        return response;
+      }());
+
+  return *activity_response;
+}
+
+}  // namespace
+
+class DeviceSyncCryptAuthDeviceActivityGetterImplTest
+    : public testing::Test,
+      public MockCryptAuthClientFactory::Observer {
+ protected:
+  DeviceSyncCryptAuthDeviceActivityGetterImplTest()
+      : client_factory_(std::make_unique<MockCryptAuthClientFactory>(
+            MockCryptAuthClientFactory::MockType::MAKE_NICE_MOCKS)) {
+    client_factory_->AddObserver(this);
+  }
+
+  ~DeviceSyncCryptAuthDeviceActivityGetterImplTest() override {
+    client_factory_->RemoveObserver(this);
+  }
+
+  // testing::Test:
+  void SetUp() override {
+    auto mock_timer = std::make_unique<base::MockOneShotTimer>();
+    timer_ = mock_timer.get();
+
+    fake_cryptauth_gcm_manager_ = std::make_unique<FakeCryptAuthGCMManager>(
+        kTestCryptAuthGCMRegistrationId);
+
+    fake_client_app_metadata_provider_ =
+        std::make_unique<FakeClientAppMetadataProvider>();
+
+    device_activity_getter_ =
+        CryptAuthDeviceActivityGetterImpl::Factory::Create(
+            client_factory_.get(), fake_client_app_metadata_provider_.get(),
+            fake_cryptauth_gcm_manager_.get(), std::move(mock_timer));
+  }
+
+  // MockCryptAuthClientFactory::Observer:
+  void OnCryptAuthClientCreated(MockCryptAuthClient* client) override {
+    ON_CALL(*client,
+            GetDevicesActivityStatus(testing::_, testing::_, testing::_))
+        .WillByDefault(
+            Invoke(this, &DeviceSyncCryptAuthDeviceActivityGetterImplTest::
+                             OnGetDevicesActivityStatus));
+
+    ON_CALL(*client, GetAccessTokenUsed())
+        .WillByDefault(testing::Return(kAccessTokenUsed));
+  }
+
+  void GetDeviceActivityStatus() {
+    device_activity_getter_->GetDevicesActivityStatus(
+        base::BindOnce(&DeviceSyncCryptAuthDeviceActivityGetterImplTest::
+                           OnGetDevicesActivityStatusFinished,
+                       base::Unretained(this)),
+        base::BindOnce(&DeviceSyncCryptAuthDeviceActivityGetterImplTest::
+                           OnGetDevicesActivityStatusError,
+                       base::Unretained(this)));
+  }
+
+  void RetrieveClientAppMetadata(
+      const base::Optional<cryptauthv2::ClientAppMetadata>&
+          client_app_metadata) {
+    std::move(
+        fake_client_app_metadata_provider_->metadata_requests().back().callback)
+        .Run(client_app_metadata);
+  }
+
+  void VerifyGetDevicesActivityStatusRequest() {
+    ASSERT_TRUE(get_device_activity_status_request_);
+    EXPECT_TRUE(get_device_activity_status_success_callback_);
+    EXPECT_TRUE(get_device_activity_status_failure_callback_);
+
+    EXPECT_EQ(
+        GetRequestContext().SerializeAsString(),
+        get_device_activity_status_request_->context().SerializeAsString());
+  }
+
+  void SendGetDevicesActivityStatusResponse() {
+    ASSERT_TRUE(get_device_activity_status_success_callback_);
+    std::move(get_device_activity_status_success_callback_).Run(GetResponse());
+  }
+
+  void FailGetDevicesActivityStatusRequest(
+      const NetworkRequestError& network_request_error) {
+    ASSERT_TRUE(get_device_activity_status_failure_callback_);
+    std::move(get_device_activity_status_failure_callback_)
+        .Run(network_request_error);
+  }
+
+  void VerifyNetworkRequestResult(
+      mojom::NetworkRequestResult expected_network_result) {
+    EXPECT_EQ(expected_network_result, network_request_result_);
+  }
+
+  void VerifyDevicesActivityStatusResult(
+      const std::vector<mojom::DeviceActivityStatusPtr>&
+          expected_device_activity_status_result) {
+    EXPECT_EQ(expected_device_activity_status_result,
+              device_activity_status_result_);
+  }
+
+  base::MockOneShotTimer* timer() { return timer_; }
+
+ private:
+  void OnGetDevicesActivityStatus(
+      const cryptauthv2::GetDevicesActivityStatusRequest& request,
+      const CryptAuthClient::GetDevicesActivityStatusCallback& callback,
+      const CryptAuthClient::ErrorCallback& error_callback) {
+    EXPECT_FALSE(get_device_activity_status_request_);
+    EXPECT_FALSE(get_device_activity_status_success_callback_);
+    EXPECT_FALSE(get_device_activity_status_failure_callback_);
+
+    get_device_activity_status_request_ = request;
+    get_device_activity_status_success_callback_ = callback;
+    get_device_activity_status_failure_callback_ = error_callback;
+  }
+
+  void OnGetDevicesActivityStatusFinished(
+      std::vector<mojom::DeviceActivityStatusPtr>
+          device_activity_status_result) {
+    device_activity_status_result_ = std::move(device_activity_status_result);
+    network_request_result_ = mojom::NetworkRequestResult::kSuccess;
+  }
+
+  void OnGetDevicesActivityStatusError(NetworkRequestError error) {
+    network_request_result_ =
+        mojo::ConvertTo<mojom::NetworkRequestResult>(error);
+  }
+
+  base::Optional<cryptauthv2::GetDevicesActivityStatusRequest>
+      get_device_activity_status_request_;
+  CryptAuthClient::GetDevicesActivityStatusCallback
+      get_device_activity_status_success_callback_;
+  CryptAuthClient::ErrorCallback get_device_activity_status_failure_callback_;
+
+  std::vector<mojom::DeviceActivityStatusPtr> device_activity_status_result_;
+  mojom::NetworkRequestResult network_request_result_;
+
+  std::unique_ptr<MockCryptAuthClientFactory> client_factory_;
+  base::MockOneShotTimer* timer_;
+
+  std::unique_ptr<FakeCryptAuthGCMManager> fake_cryptauth_gcm_manager_;
+  std::unique_ptr<FakeClientAppMetadataProvider>
+      fake_client_app_metadata_provider_;
+
+  std::unique_ptr<CryptAuthDeviceActivityGetter> device_activity_getter_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceSyncCryptAuthDeviceActivityGetterImplTest);
+};
+
+TEST_F(DeviceSyncCryptAuthDeviceActivityGetterImplTest, Success) {
+  GetDeviceActivityStatus();
+  RetrieveClientAppMetadata(cryptauthv2::GetClientAppMetadataForTest());
+  VerifyGetDevicesActivityStatusRequest();
+  SendGetDevicesActivityStatusResponse();
+  VerifyNetworkRequestResult(mojom::NetworkRequestResult::kSuccess);
+  mojom::DeviceActivityStatusPtr expected_device_activity_status =
+      mojom::DeviceActivityStatus::New(
+          kDeviceId, base::Time::FromTimeT(kLastActivityTimeSecs),
+          kConnectivityStatus);
+  std::vector<mojom::DeviceActivityStatusPtr>
+      expected_device_activity_status_result;
+  expected_device_activity_status_result.emplace_back(
+      std::move(expected_device_activity_status));
+  VerifyDevicesActivityStatusResult(expected_device_activity_status_result);
+}
+
+TEST_F(DeviceSyncCryptAuthDeviceActivityGetterImplTest,
+       NullMetadata_GetClientAppMetadata) {
+  GetDeviceActivityStatus();
+  RetrieveClientAppMetadata(base::nullopt);
+  VerifyNetworkRequestResult(mojom::NetworkRequestResult::kUnknown);
+}
+
+TEST_F(DeviceSyncCryptAuthDeviceActivityGetterImplTest,
+       Failure_Timeout_GetClientAppMetadata) {
+  GetDeviceActivityStatus();
+  timer()->Fire();
+  VerifyNetworkRequestResult(mojom::NetworkRequestResult::kUnknown);
+}
+
+TEST_F(DeviceSyncCryptAuthDeviceActivityGetterImplTest,
+       Failure_Timeout_GetDevicesActivityStatusResponse) {
+  GetDeviceActivityStatus();
+  RetrieveClientAppMetadata(cryptauthv2::GetClientAppMetadataForTest());
+  VerifyGetDevicesActivityStatusRequest();
+  timer()->Fire();
+  VerifyNetworkRequestResult(mojom::NetworkRequestResult::kUnknown);
+}
+
+TEST_F(DeviceSyncCryptAuthDeviceActivityGetterImplTest,
+       Failure_ApiCall_GetDevicesActivityStatus) {
+  GetDeviceActivityStatus();
+  RetrieveClientAppMetadata(cryptauthv2::GetClientAppMetadataForTest());
+  VerifyGetDevicesActivityStatusRequest();
+  FailGetDevicesActivityStatusRequest(NetworkRequestError::kBadRequest);
+  VerifyNetworkRequestResult(mojom::NetworkRequestResult::kBadRequest);
+}
+
+}  // namespace device_sync
+
+}  // namespace chromeos
diff --git a/chromeos/services/device_sync/device_sync_impl.cc b/chromeos/services/device_sync/device_sync_impl.cc
index f291280..83c356264 100644
--- a/chromeos/services/device_sync/device_sync_impl.cc
+++ b/chromeos/services/device_sync/device_sync_impl.cc
@@ -16,6 +16,7 @@
 #include "chromeos/components/multidevice/secure_message_delegate_impl.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/services/device_sync/cryptauth_client_impl.h"
+#include "chromeos/services/device_sync/cryptauth_device_activity_getter_impl.h"
 #include "chromeos/services/device_sync/cryptauth_device_manager_impl.h"
 #include "chromeos/services/device_sync/cryptauth_enroller_factory_impl.h"
 #include "chromeos/services/device_sync/cryptauth_enrollment_manager_impl.h"
@@ -446,6 +447,56 @@
                  weak_ptr_factory_.GetWeakPtr(), callback_holder));
 }
 
+void DeviceSyncImpl::GetDevicesActivityStatus(
+    GetDevicesActivityStatusCallback callback) {
+  if (status_ != Status::READY) {
+    PA_LOG(WARNING)
+        << "DeviceSyncImpl::GetDevicesActivityStatus() invoked before "
+        << "initialization was complete. Cannot get activity statuses.";
+    std::move(callback).Run(
+        mojom::NetworkRequestResult::kServiceNotYetInitialized,
+        base::nullopt /* device_activity_statuses */);
+    return;
+  }
+
+  auto request_id = base::UnguessableToken::Create();
+  get_devices_activity_status_callbacks_.emplace(request_id,
+                                                 std::move(callback));
+
+  cryptauth_device_activity_getter_ =
+      CryptAuthDeviceActivityGetterImpl::Factory::Create(
+          cryptauth_client_factory_.get(), client_app_metadata_provider_,
+          cryptauth_gcm_manager_.get());
+
+  cryptauth_device_activity_getter_->GetDevicesActivityStatus(
+      base::Bind(&DeviceSyncImpl::OnGetDevicesActivityStatusFinished,
+                 weak_ptr_factory_.GetWeakPtr(), request_id),
+      base::Bind(&DeviceSyncImpl::OnGetDevicesActivityStatusError,
+                 weak_ptr_factory_.GetWeakPtr(), request_id));
+}
+
+void DeviceSyncImpl::OnGetDevicesActivityStatusFinished(
+    const base::UnguessableToken& request_id,
+    CryptAuthDeviceActivityGetter::DeviceActivityStatusResult
+        device_activity_status_result) {
+  auto iter = get_devices_activity_status_callbacks_.find(request_id);
+  DCHECK(iter != get_devices_activity_status_callbacks_.end());
+  std::move(iter->second)
+      .Run(mojom::NetworkRequestResult::kSuccess,
+           base::make_optional(std::move(device_activity_status_result)));
+  get_devices_activity_status_callbacks_.erase(iter);
+}
+
+void DeviceSyncImpl::OnGetDevicesActivityStatusError(
+    const base::UnguessableToken& request_id,
+    NetworkRequestError error) {
+  auto iter = get_devices_activity_status_callbacks_.find(request_id);
+  DCHECK(iter != get_devices_activity_status_callbacks_.end());
+  std::move(iter->second)
+      .Run(mojo::ConvertTo<mojom::NetworkRequestResult>(error), base::nullopt);
+  get_devices_activity_status_callbacks_.erase(iter);
+}
+
 void DeviceSyncImpl::GetDebugInfo(GetDebugInfoCallback callback) {
   if (status_ != Status::READY) {
     PA_LOG(WARNING) << "DeviceSyncImpl::GetDebugInfo() invoked before "
diff --git a/chromeos/services/device_sync/device_sync_impl.h b/chromeos/services/device_sync/device_sync_impl.h
index 6f1dd3ea..88e7398 100644
--- a/chromeos/services/device_sync/device_sync_impl.h
+++ b/chromeos/services/device_sync/device_sync_impl.h
@@ -10,6 +10,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/unguessable_token.h"
+#include "chromeos/services/device_sync/cryptauth_device_activity_getter.h"
 #include "chromeos/services/device_sync/cryptauth_enrollment_manager.h"
 #include "chromeos/services/device_sync/cryptauth_gcm_manager.h"
 #include "chromeos/services/device_sync/device_sync_base.h"
@@ -103,6 +104,8 @@
       SetSoftwareFeatureStateCallback callback) override;
   void FindEligibleDevices(multidevice::SoftwareFeature software_feature,
                            FindEligibleDevicesCallback callback) override;
+  void GetDevicesActivityStatus(
+      GetDevicesActivityStatusCallback callback) override;
   void GetDebugInfo(GetDebugInfoCallback callback) override;
 
   // CryptAuthEnrollmentManager::Observer:
@@ -205,6 +208,14 @@
                mojom::FindEligibleDevicesResponsePtr)>& callback,
       NetworkRequestError error);
 
+  void OnGetDevicesActivityStatusFinished(
+      const base::UnguessableToken& request_id,
+      CryptAuthDeviceActivityGetter::DeviceActivityStatusResult
+          device_activity_status_result);
+  void OnGetDevicesActivityStatusError(
+      const base::UnguessableToken& request_id,
+      NetworkRequestError network_request_error);
+
   // Note: If the timer is already running, StartSetSoftwareFeatureTimer()
   // restarts it.
   void StartSetSoftwareFeatureTimer();
@@ -229,6 +240,8 @@
   base::flat_map<base::UnguessableToken,
                  std::unique_ptr<PendingSetSoftwareFeatureRequest>>
       id_to_pending_set_software_feature_request_map_;
+  base::flat_map<base::UnguessableToken, GetDevicesActivityStatusCallback>
+      get_devices_activity_status_callbacks_;
 
   std::unique_ptr<CryptAuthGCMManager> cryptauth_gcm_manager_;
   std::unique_ptr<CryptAuthClientFactory> cryptauth_client_factory_;
@@ -241,6 +254,8 @@
   std::unique_ptr<CryptAuthDeviceManager> cryptauth_device_manager_;
   std::unique_ptr<RemoteDeviceProvider> remote_device_provider_;
   std::unique_ptr<SoftwareFeatureManager> software_feature_manager_;
+  std::unique_ptr<CryptAuthDeviceActivityGetter>
+      cryptauth_device_activity_getter_;
 
   base::WeakPtrFactory<DeviceSyncImpl> weak_ptr_factory_{this};
 
diff --git a/chromeos/services/device_sync/fake_device_sync.cc b/chromeos/services/device_sync/fake_device_sync.cc
index cdbf43c..b79f44be 100644
--- a/chromeos/services/device_sync/fake_device_sync.cc
+++ b/chromeos/services/device_sync/fake_device_sync.cc
@@ -47,6 +47,15 @@
   find_eligible_devices_callback_queue_.pop();
 }
 
+void FakeDeviceSync::InvokePendingGetDevicesActivityStatusCallback(
+    mojom::NetworkRequestResult result_code,
+    std::vector<mojom::DeviceActivityStatusPtr>
+        get_devices_activity_status_response) {
+  std::move(get_devices_activity_status_callback_queue_.front())
+      .Run(result_code, std::move(get_devices_activity_status_response));
+  get_devices_activity_status_callback_queue_.pop();
+}
+
 void FakeDeviceSync::InvokePendingGetDebugInfoCallback(
     mojom::DebugInfoPtr debug_info_ptr) {
   std::move(get_debug_info_callback_queue_.front())
@@ -90,6 +99,11 @@
   get_debug_info_callback_queue_.push(std::move(callback));
 }
 
+void FakeDeviceSync::GetDevicesActivityStatus(
+    GetDevicesActivityStatusCallback callback) {
+  get_devices_activity_status_callback_queue_.push(std::move(callback));
+}
+
 }  // namespace device_sync
 
 }  // namespace chromeos
diff --git a/chromeos/services/device_sync/fake_device_sync.h b/chromeos/services/device_sync/fake_device_sync.h
index d990ed6e..436705f 100644
--- a/chromeos/services/device_sync/fake_device_sync.h
+++ b/chromeos/services/device_sync/fake_device_sync.h
@@ -44,6 +44,10 @@
   void InvokePendingFindEligibleDevicesCallback(
       mojom::NetworkRequestResult result_code,
       mojom::FindEligibleDevicesResponsePtr find_eligible_devices_response_ptr);
+  void InvokePendingGetDevicesActivityStatusCallback(
+      mojom::NetworkRequestResult result_code,
+      std::vector<mojom::DeviceActivityStatusPtr>
+          get_devices_activity_status_response);
   void InvokePendingGetDebugInfoCallback(mojom::DebugInfoPtr debug_info_ptr);
 
  protected:
@@ -61,6 +65,8 @@
   void FindEligibleDevices(multidevice::SoftwareFeature software_feature,
                            FindEligibleDevicesCallback callback) override;
   void GetDebugInfo(GetDebugInfoCallback callback) override;
+  void GetDevicesActivityStatus(
+      GetDevicesActivityStatusCallback callback) override;
 
  private:
   bool force_enrollment_now_completed_success_ = true;
@@ -72,6 +78,8 @@
   std::queue<SetSoftwareFeatureStateCallback>
       set_software_feature_state_callback_queue_;
   std::queue<FindEligibleDevicesCallback> find_eligible_devices_callback_queue_;
+  std::queue<GetDevicesActivityStatusCallback>
+      get_devices_activity_status_callback_queue_;
   std::queue<GetDebugInfoCallback> get_debug_info_callback_queue_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeDeviceSync);
diff --git a/chromeos/services/device_sync/proto/cryptauth_devicesync.proto b/chromeos/services/device_sync/proto/cryptauth_devicesync.proto
index 44c6375..c5c2fd6 100644
--- a/chromeos/services/device_sync/proto/cryptauth_devicesync.proto
+++ b/chromeos/services/device_sync/proto/cryptauth_devicesync.proto
@@ -270,7 +270,7 @@
 }
 
 // Describes the device's network reachability.
-enum OnlineStatus {
+enum ConnectivityStatus {
   // Default value.
   UNKNOWN_CONNECTIVITY = 0;
   // The device appears to be unreachable.
@@ -289,7 +289,7 @@
   int64 last_activity_time_sec = 2;
 
   // Online status of the device as inferred by reachability via FCM.
-  OnlineStatus online_status = 3;
+  ConnectivityStatus connectivity_status = 3;
 }
 
 // Response for GetDevicesActivityStatus.
diff --git a/chromeos/services/device_sync/proto/cryptauth_v2_test_util.cc b/chromeos/services/device_sync/proto/cryptauth_v2_test_util.cc
index 1f2cfc7c..43f472a 100644
--- a/chromeos/services/device_sync/proto/cryptauth_v2_test_util.cc
+++ b/chromeos/services/device_sync/proto/cryptauth_v2_test_util.cc
@@ -90,11 +90,11 @@
 DeviceActivityStatus BuildDeviceActivityStatus(
     const std::string& device_id,
     int64_t last_activity_time_sec,
-    const OnlineStatus online_status) {
+    const ConnectivityStatus connectivity_status) {
   DeviceActivityStatus device_activity_status;
   device_activity_status.set_device_id(device_id);
   device_activity_status.set_last_activity_time_sec(last_activity_time_sec);
-  device_activity_status.set_online_status(online_status);
+  device_activity_status.set_connectivity_status(connectivity_status);
   return device_activity_status;
 }
 
diff --git a/chromeos/services/device_sync/proto/cryptauth_v2_test_util.h b/chromeos/services/device_sync/proto/cryptauth_v2_test_util.h
index a35f34011..1f137443 100644
--- a/chromeos/services/device_sync/proto/cryptauth_v2_test_util.h
+++ b/chromeos/services/device_sync/proto/cryptauth_v2_test_util.h
@@ -58,7 +58,7 @@
 DeviceActivityStatus BuildDeviceActivityStatus(
     const std::string& device_id,
     int64_t last_activity_time_sec,
-    const OnlineStatus online_status);
+    const ConnectivityStatus online_status);
 
 // The data field is set to "start_|start_time_millis|_end_|end_time_millis|".
 BeaconSeed BuildBeaconSeedForTest(int64_t start_time_millis,
diff --git a/chromeos/services/device_sync/public/mojom/BUILD.gn b/chromeos/services/device_sync/public/mojom/BUILD.gn
index 8014769..f3e8fb2 100644
--- a/chromeos/services/device_sync/public/mojom/BUILD.gn
+++ b/chromeos/services/device_sync/public/mojom/BUILD.gn
@@ -15,3 +15,19 @@
     "//services/preferences/public/mojom",
   ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = [
+    "device_sync_mojom_traits_unittest.cc",
+  ]
+
+  deps = [
+    ":mojom",
+    "//base",
+    "//base/test:test_support",
+    "//mojo/public/cpp/test_support:test_utils",
+    "//testing/gtest",
+  ]
+}
diff --git a/chromeos/services/device_sync/public/mojom/OWNERS b/chromeos/services/device_sync/public/mojom/OWNERS
index 08850f4..3e6cfccf 100644
--- a/chromeos/services/device_sync/public/mojom/OWNERS
+++ b/chromeos/services/device_sync/public/mojom/OWNERS
@@ -1,2 +1,6 @@
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
\ No newline at end of file
diff --git a/chromeos/services/device_sync/public/mojom/device_sync.mojom b/chromeos/services/device_sync/public/mojom/device_sync.mojom
index 4f72d887..2dbbecc 100644
--- a/chromeos/services/device_sync/public/mojom/device_sync.mojom
+++ b/chromeos/services/device_sync/public/mojom/device_sync.mojom
@@ -48,6 +48,17 @@
   array<chromeos.multidevice.mojom.RemoteDevice> ineligible_devices;
 };
 
+enum ConnectivityStatus {
+  kOnline,
+  kOffline
+};
+
+struct DeviceActivityStatus {
+  string device_id;
+  mojo_base.mojom.Time last_activity_time;
+  ConnectivityStatus connectivity_status;
+};
+
 struct DebugInfo {
   // Enrollment stats:
   mojo_base.mojom.Time last_enrollment_time;
@@ -76,6 +87,7 @@
 };
 
 // TODO(khorimoto): Flesh out API.
+// Calls to this API run in the browser process.
 interface DeviceSync {
   // Adds an Observer of this API.
   AddObserver(DeviceSyncObserver observer) => ();
@@ -141,6 +153,20 @@
           (NetworkRequestResult result_code,
            FindEligibleDevicesResponse? response);
 
+  // Retrieves the activity status of the user's devices. When this function
+  // is invoked, a network request will be sent to the CryptAuth server to
+  // retrieve the last time a device was used.
+  //
+  // On success, this function returns a null error_code with the activity
+  // statuses to the callback; on error, it returns a valid error_code string
+  // indicating the reason for failure along with a null activity statuses.
+  // 
+  // This method is expected to be used by multidevice_setup service running 
+  // in the browser process.
+  GetDevicesActivityStatus() =>
+          (NetworkRequestResult result_code,
+           array<DeviceActivityStatus>? device_activity_statuses);
+
   // Functions below are implemented for chrome://proximity-auth page, which is
   // intended for debugging purposes only.
   // TODO(khorimoto): Determine whether a new, debug-only interface should be
diff --git a/chromeos/services/device_sync/public/mojom/device_sync.typemap b/chromeos/services/device_sync/public/mojom/device_sync.typemap
new file mode 100644
index 0000000..0796284
--- /dev/null
+++ b/chromeos/services/device_sync/public/mojom/device_sync.typemap
@@ -0,0 +1,19 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//chromeos/services/device_sync/public/mojom/device_sync.mojom"
+
+public_headers =
+    [ "//chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h" ]
+
+traits_headers = [
+  "//chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.h",
+]
+
+sources = [
+  "//chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.cc",
+  "//chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.h",
+]
+
+type_mappings = [ "chromeos.device_sync.mojom.ConnectivityStatus=cryptauthv2::ConnectivityStatus" ]
diff --git a/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.cc b/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.cc
new file mode 100644
index 0000000..df4dc71
--- /dev/null
+++ b/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.cc
@@ -0,0 +1,46 @@
+// Copyright 2019 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 "chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.h"
+
+namespace mojo {
+
+chromeos::device_sync::mojom::ConnectivityStatus EnumTraits<
+    chromeos::device_sync::mojom::ConnectivityStatus,
+    cryptauthv2::ConnectivityStatus>::ToMojom(cryptauthv2::ConnectivityStatus
+                                                  input) {
+  switch (input) {
+    case cryptauthv2::ConnectivityStatus::ONLINE:
+      return chromeos::device_sync::mojom::ConnectivityStatus::kOnline;
+    case cryptauthv2::ConnectivityStatus::UNKNOWN_CONNECTIVITY:
+      FALLTHROUGH;
+    case cryptauthv2::ConnectivityStatus::OFFLINE:
+      return chromeos::device_sync::mojom::ConnectivityStatus::kOffline;
+    case cryptauthv2::ConnectivityStatus::
+        ConnectivityStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
+    case cryptauthv2::ConnectivityStatus::
+        ConnectivityStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
+      NOTREACHED();
+      return chromeos::device_sync::mojom::ConnectivityStatus::kOffline;
+  }
+}
+
+bool EnumTraits<chromeos::device_sync::mojom::ConnectivityStatus,
+                cryptauthv2::ConnectivityStatus>::
+    FromMojom(chromeos::device_sync::mojom::ConnectivityStatus input,
+              cryptauthv2::ConnectivityStatus* out) {
+  switch (input) {
+    case chromeos::device_sync::mojom::ConnectivityStatus::kOnline:
+      *out = cryptauthv2::ConnectivityStatus::ONLINE;
+      return true;
+    case chromeos::device_sync::mojom::ConnectivityStatus::kOffline:
+      *out = cryptauthv2::ConnectivityStatus::OFFLINE;
+      return true;
+  }
+
+  NOTREACHED();
+  return false;
+}
+
+}  // namespace mojo
diff --git a/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.h b/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.h
new file mode 100644
index 0000000..4dd9d46
--- /dev/null
+++ b/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.h
@@ -0,0 +1,25 @@
+// Copyright 2019 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 CHROMEOS_SERVICES_DEVICE_SYNC_PUBLIC_MOJOM_DEVICE_SYNC_MOJOM_TRAITS_H_
+#define CHROMEOS_SERVICES_DEVICE_SYNC_PUBLIC_MOJOM_DEVICE_SYNC_MOJOM_TRAITS_H_
+
+#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
+#include "mojo/public/cpp/bindings/enum_traits.h"
+
+namespace mojo {
+template <>
+class EnumTraits<chromeos::device_sync::mojom::ConnectivityStatus,
+                 cryptauthv2::ConnectivityStatus> {
+ public:
+  static chromeos::device_sync::mojom::ConnectivityStatus ToMojom(
+      cryptauthv2::ConnectivityStatus input);
+  static bool FromMojom(chromeos::device_sync::mojom::ConnectivityStatus input,
+                        cryptauthv2::ConnectivityStatus* out);
+};
+
+}  // namespace mojo
+
+#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_PUBLIC_MOJOM_DEVICE_SYNC_MOJOM_TRAITS_H_
\ No newline at end of file
diff --git a/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits_unittest.cc b/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits_unittest.cc
new file mode 100644
index 0000000..2fa4983
--- /dev/null
+++ b/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits_unittest.cc
@@ -0,0 +1,29 @@
+// Copyright 2019 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 "chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.h"
+
+#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
+#include "chromeos/services/device_sync/public/mojom/device_sync.mojom.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(DeviceSyncMojomTraitsTest, ConnectivityStatus) {
+  static constexpr cryptauthv2::ConnectivityStatus kTestConnectivityStatuses[] =
+      {cryptauthv2::ConnectivityStatus::ONLINE,
+       cryptauthv2::ConnectivityStatus::OFFLINE};
+
+  for (auto status_in : kTestConnectivityStatuses) {
+    cryptauthv2::ConnectivityStatus status_out;
+
+    chromeos::device_sync::mojom::ConnectivityStatus serialized_status =
+        mojo::EnumTraits<chromeos::device_sync::mojom::ConnectivityStatus,
+                         cryptauthv2::ConnectivityStatus>::ToMojom(status_in);
+    ASSERT_TRUE((mojo::EnumTraits<
+                 chromeos::device_sync::mojom::ConnectivityStatus,
+                 cryptauthv2::ConnectivityStatus>::FromMojom(serialized_status,
+                                                             &status_out)));
+    EXPECT_EQ(status_in, status_out);
+  }
+}
diff --git a/chromeos/services/device_sync/public/mojom/typemaps.gni b/chromeos/services/device_sync/public/mojom/typemaps.gni
new file mode 100644
index 0000000..c105182
--- /dev/null
+++ b/chromeos/services/device_sync/public/mojom/typemaps.gni
@@ -0,0 +1,6 @@
+# Copyright 2019 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.
+
+typemaps =
+    [ "//chromeos/services/device_sync/public/mojom/device_sync.typemap" ]
diff --git a/chromeos/services/network_config/cros_network_config.cc b/chromeos/services/network_config/cros_network_config.cc
index c3100e1..a35f5f7 100644
--- a/chromeos/services/network_config/cros_network_config.cc
+++ b/chromeos/services/network_config/cros_network_config.cc
@@ -311,7 +311,8 @@
           network_state_handler->GetDeviceState(network->device_path());
       cellular->sim_locked = !cellular_device->IsSimAbsent() &&
                              cellular_device->sim_lock_enabled();
-      result->cellular = std::move(cellular);
+      result->type_state =
+          mojom::NetworkTypeStateProperties::NewCellular(std::move(cellular));
       break;
     }
     case mojom::NetworkType::kEthernet: {
@@ -319,7 +320,8 @@
       ethernet->authentication = network->type() == shill::kTypeEthernetEap
                                      ? mojom::AuthenticationType::k8021x
                                      : mojom::AuthenticationType::kNone;
-      result->ethernet = std::move(ethernet);
+      result->type_state =
+          mojom::NetworkTypeStateProperties::NewEthernet(std::move(ethernet));
       break;
     }
     case mojom::NetworkType::kTether: {
@@ -328,7 +330,8 @@
       tether->carrier = network->tether_carrier();
       tether->has_connected_to_host = network->tether_has_connected_to_host();
       tether->signal_strength = network->signal_strength();
-      result->tether = std::move(tether);
+      result->type_state =
+          mojom::NetworkTypeStateProperties::NewTether(std::move(tether));
       break;
     }
     case mojom::NetworkType::kVPN: {
@@ -342,7 +345,8 @@
         vpn->provider_name =
             GetVpnProviderName(vpn_providers, vpn_provider->id);
       }
-      result->vpn = std::move(vpn);
+      result->type_state =
+          mojom::NetworkTypeStateProperties::NewVpn(std::move(vpn));
       break;
     }
     case mojom::NetworkType::kWiFi: {
@@ -353,7 +357,8 @@
       wifi->security = network->GetMojoSecurity();
       wifi->signal_strength = network->signal_strength();
       wifi->ssid = network->name();
-      result->wifi = std::move(wifi);
+      result->type_state =
+          mojom::NetworkTypeStateProperties::NewWifi(std::move(wifi));
       break;
     }
     case mojom::NetworkType::kAll:
@@ -1673,14 +1678,14 @@
 }
 
 void CrosNetworkConfig::AddObserver(
-    mojom::CrosNetworkConfigObserverPtr observer) {
+    mojo::PendingRemote<mojom::CrosNetworkConfigObserver> observer) {
   if (!network_state_handler_->HasObserver(this))
     network_state_handler_->AddObserver(this, FROM_HERE);
   if (network_certificate_handler_ &&
       !network_certificate_handler_->HasObserver(this)) {
     network_certificate_handler_->AddObserver(this);
   }
-  observers_.AddPtr(std::move(observer));
+  observers_.Add(std::move(observer));
 }
 
 void CrosNetworkConfig::GetNetworkState(const std::string& guid,
@@ -2413,9 +2418,8 @@
 void CrosNetworkConfig::SetVpnProviders(
     std::vector<mojom::VpnProviderPtr> providers) {
   vpn_providers_ = std::move(providers);
-  observers_.ForAllPtrs([](mojom::CrosNetworkConfigObserver* observer) {
+  for (auto& observer : observers_)
     observer->OnVpnProvidersChanged();
-  });
 }
 
 void CrosNetworkConfig::GetVpnProviders(GetVpnProvidersCallback callback) {
@@ -2442,15 +2446,13 @@
 
 // NetworkStateHandlerObserver
 void CrosNetworkConfig::NetworkListChanged() {
-  observers_.ForAllPtrs([](mojom::CrosNetworkConfigObserver* observer) {
+  for (auto& observer : observers_)
     observer->OnNetworkStateListChanged();
-  });
 }
 
 void CrosNetworkConfig::DeviceListChanged() {
-  observers_.ForAllPtrs([](mojom::CrosNetworkConfigObserver* observer) {
+  for (auto& observer : observers_)
     observer->OnDeviceStateListChanged();
-  });
 }
 
 void CrosNetworkConfig::ActiveNetworksChanged(
@@ -2462,9 +2464,8 @@
     if (mojo_network)
       result.emplace_back(std::move(mojo_network));
   }
-  observers_.ForAllPtrs([&result](mojom::CrosNetworkConfigObserver* observer) {
+  for (auto& observer : observers_)
     observer->OnActiveNetworksChanged(mojo::Clone(result));
-  });
 }
 
 void CrosNetworkConfig::NetworkPropertiesUpdated(const NetworkState* network) {
@@ -2474,10 +2475,8 @@
       NetworkStateToMojo(network_state_handler_, vpn_providers_, network);
   if (!mojo_network)
     return;
-  observers_.ForAllPtrs(
-      [&mojo_network](mojom::CrosNetworkConfigObserver* observer) {
-        observer->OnNetworkStateChanged(mojo_network.Clone());
-      });
+  for (auto& observer : observers_)
+    observer->OnNetworkStateChanged(mojo_network.Clone());
 }
 
 void CrosNetworkConfig::DevicePropertiesUpdated(const DeviceState* device) {
@@ -2491,9 +2490,8 @@
 }
 
 void CrosNetworkConfig::OnCertificatesChanged() {
-  observers_.ForAllPtrs([](mojom::CrosNetworkConfigObserver* observer) {
+  for (auto& observer : observers_)
     observer->OnNetworkCertificatesChanged();
-  });
 }
 
 const std::string& CrosNetworkConfig::GetServicePathFromGuid(
diff --git a/chromeos/services/network_config/cros_network_config.h b/chromeos/services/network_config/cros_network_config.h
index a36d943..f62e6e4 100644
--- a/chromeos/services/network_config/cros_network_config.h
+++ b/chromeos/services/network_config/cros_network_config.h
@@ -10,9 +10,10 @@
 #include "chromeos/network/network_certificate_handler.h"
 #include "chromeos/network/network_state_handler_observer.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
-#include "mojo/public/cpp/bindings/interface_ptr_set.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
+#include "mojo/public/cpp/bindings/remote_set.h"
 
 namespace base {
 class DictionaryValue;
@@ -48,7 +49,8 @@
   void BindReceiver(mojo::PendingReceiver<mojom::CrosNetworkConfig> receiver);
 
   // mojom::CrosNetworkConfig
-  void AddObserver(mojom::CrosNetworkConfigObserverPtr observer) override;
+  void AddObserver(
+      mojo::PendingRemote<mojom::CrosNetworkConfigObserver> observer) override;
   void GetNetworkState(const std::string& guid,
                        GetNetworkStateCallback callback) override;
   void GetNetworkStateList(mojom::NetworkFilterPtr filter,
@@ -163,7 +165,7 @@
   NetworkConnectionHandler* network_connection_handler_;    // Unowned
   NetworkCertificateHandler* network_certificate_handler_;  // Unowned
 
-  mojo::InterfacePtrSet<mojom::CrosNetworkConfigObserver> observers_;
+  mojo::RemoteSet<mojom::CrosNetworkConfigObserver> observers_;
   mojo::ReceiverSet<mojom::CrosNetworkConfig> receivers_;
 
   int callback_id_ = 1;
diff --git a/chromeos/services/network_config/cros_network_config_unittest.cc b/chromeos/services/network_config/cros_network_config_unittest.cc
index f0b63bc0..6c3bf925 100644
--- a/chromeos/services/network_config/cros_network_config_unittest.cc
+++ b/chromeos/services/network_config/cros_network_config_unittest.cc
@@ -169,7 +169,7 @@
 
   void SetupObserver() {
     observer_ = std::make_unique<CrosNetworkConfigTestObserver>();
-    cros_network_config_->AddObserver(observer_->GenerateInterfacePtr());
+    cros_network_config_->AddObserver(observer_->GenerateRemote());
   }
 
   mojom::NetworkStatePropertiesPtr GetNetworkState(const std::string& guid) {
@@ -455,9 +455,11 @@
   EXPECT_EQ("wifi1_guid", network->guid);
   EXPECT_EQ(mojom::NetworkType::kWiFi, network->type);
   EXPECT_EQ(mojom::ConnectionStateType::kConnected, network->connection_state);
-  ASSERT_TRUE(network->wifi);
-  EXPECT_EQ(mojom::SecurityType::kNone, network->wifi->security);
-  EXPECT_EQ(50, network->wifi->signal_strength);
+  ASSERT_TRUE(network->type_state);
+  ASSERT_TRUE(network->type_state->is_wifi());
+  EXPECT_EQ(mojom::SecurityType::kNone,
+            network->type_state->get_wifi()->security);
+  EXPECT_EQ(50, network->type_state->get_wifi()->signal_strength);
   EXPECT_EQ(mojom::OncSource::kNone, network->source);
 
   network = GetNetworkState("wifi2_guid");
@@ -466,9 +468,11 @@
   EXPECT_EQ(mojom::NetworkType::kWiFi, network->type);
   EXPECT_EQ(mojom::ConnectionStateType::kNotConnected,
             network->connection_state);
-  ASSERT_TRUE(network->wifi);
-  EXPECT_EQ(mojom::SecurityType::kWpaPsk, network->wifi->security);
-  EXPECT_EQ(100, network->wifi->signal_strength);
+  ASSERT_TRUE(network->type_state);
+  ASSERT_TRUE(network->type_state->is_wifi());
+  EXPECT_EQ(mojom::SecurityType::kWpaPsk,
+            network->type_state->get_wifi()->security);
+  EXPECT_EQ(100, network->type_state->get_wifi()->signal_strength);
   EXPECT_EQ(mojom::OncSource::kUserPolicy, network->source);
 
   network = GetNetworkState("wifi3_guid");
@@ -477,9 +481,11 @@
   EXPECT_EQ(mojom::NetworkType::kWiFi, network->type);
   EXPECT_EQ(mojom::ConnectionStateType::kNotConnected,
             network->connection_state);
-  ASSERT_TRUE(network->wifi);
-  EXPECT_EQ(mojom::SecurityType::kWpaPsk, network->wifi->security);
-  EXPECT_EQ(0, network->wifi->signal_strength);
+  ASSERT_TRUE(network->type_state);
+  ASSERT_TRUE(network->type_state->is_wifi());
+  EXPECT_EQ(mojom::SecurityType::kWpaPsk,
+            network->type_state->get_wifi()->security);
+  EXPECT_EQ(0, network->type_state->get_wifi()->signal_strength);
   EXPECT_EQ(mojom::OncSource::kDevice, network->source);
 
   network = GetNetworkState("cellular_guid");
@@ -488,21 +494,24 @@
   EXPECT_EQ(mojom::NetworkType::kCellular, network->type);
   EXPECT_EQ(mojom::ConnectionStateType::kNotConnected,
             network->connection_state);
-  ASSERT_TRUE(network->cellular);
-  EXPECT_EQ(0, network->cellular->signal_strength);
-  EXPECT_EQ("LTE", network->cellular->network_technology);
-  EXPECT_EQ(mojom::ActivationStateType::kActivated,
-            network->cellular->activation_state);
+  ASSERT_TRUE(network->type_state);
+  ASSERT_TRUE(network->type_state->is_cellular());
+  mojom::CellularStatePropertiesPtr& cellular =
+      network->type_state->get_cellular();
+  EXPECT_EQ(0, cellular->signal_strength);
+  EXPECT_EQ("LTE", cellular->network_technology);
+  EXPECT_EQ(mojom::ActivationStateType::kActivated, cellular->activation_state);
   EXPECT_EQ(mojom::OncSource::kNone, network->source);
-  EXPECT_TRUE(network->cellular->sim_locked);
+  EXPECT_TRUE(cellular->sim_locked);
 
   network = GetNetworkState("vpn_guid");
   ASSERT_TRUE(network);
   EXPECT_EQ("vpn_guid", network->guid);
   EXPECT_EQ(mojom::NetworkType::kVPN, network->type);
   EXPECT_EQ(mojom::ConnectionStateType::kConnecting, network->connection_state);
-  ASSERT_TRUE(network->vpn);
-  EXPECT_EQ(mojom::VpnType::kL2TPIPsec, network->vpn->type);
+  ASSERT_TRUE(network->type_state);
+  ASSERT_TRUE(network->type_state->is_vpn());
+  EXPECT_EQ(mojom::VpnType::kL2TPIPsec, network->type_state->get_vpn()->type);
   EXPECT_EQ(mojom::OncSource::kNone, network->source);
 
   // TODO(919691): Test ProxyMode once UIProxyConfigService logic is improved.
@@ -760,8 +769,9 @@
   EXPECT_EQ(guid, network->guid);
   EXPECT_EQ(mojom::NetworkType::kWiFi, network->type);
   EXPECT_EQ(mojom::OncSource::kDevice, network->source);
-  ASSERT_TRUE(network->wifi);
-  EXPECT_EQ(ssid, network->wifi->ssid);
+  ASSERT_TRUE(network->type_state);
+  ASSERT_TRUE(network->type_state->is_wifi());
+  EXPECT_EQ(ssid, network->type_state->get_wifi()->ssid);
 }
 
 TEST_F(CrosNetworkConfigTest, ForgetNetwork) {
@@ -955,7 +965,7 @@
     bool wifi_scanning_ = false;
   };
   ScanningObserver observer(cros_network_config());
-  cros_network_config()->AddObserver(observer.GenerateInterfacePtr());
+  cros_network_config()->AddObserver(observer.GenerateRemote());
   base::RunLoop().RunUntilIdle();
 
   cros_network_config()->RequestNetworkScan(mojom::NetworkType::kWiFi);
diff --git a/chromeos/services/network_config/public/cpp/cros_network_config_test_observer.cc b/chromeos/services/network_config/public/cpp/cros_network_config_test_observer.cc
index fa79c7a..e5dc7874 100644
--- a/chromeos/services/network_config/public/cpp/cros_network_config_test_observer.cc
+++ b/chromeos/services/network_config/public/cpp/cros_network_config_test_observer.cc
@@ -13,11 +13,9 @@
 CrosNetworkConfigTestObserver::CrosNetworkConfigTestObserver() = default;
 CrosNetworkConfigTestObserver::~CrosNetworkConfigTestObserver() = default;
 
-mojom::CrosNetworkConfigObserverPtr
-CrosNetworkConfigTestObserver::GenerateInterfacePtr() {
-  mojom::CrosNetworkConfigObserverPtr observer_ptr;
-  binding().Bind(mojo::MakeRequest(&observer_ptr));
-  return observer_ptr;
+mojo::PendingRemote<mojom::CrosNetworkConfigObserver>
+CrosNetworkConfigTestObserver::GenerateRemote() {
+  return receiver().BindNewPipeAndPassRemote();
 }
 
 int CrosNetworkConfigTestObserver::GetNetworkChangedCount(
@@ -64,7 +62,7 @@
 }
 
 void CrosNetworkConfigTestObserver::FlushForTesting() {
-  binding_.FlushForTesting();
+  receiver_.FlushForTesting();
 }
 
 }  // namespace network_config
diff --git a/chromeos/services/network_config/public/cpp/cros_network_config_test_observer.h b/chromeos/services/network_config/public/cpp/cros_network_config_test_observer.h
index 5f95827..d182941 100644
--- a/chromeos/services/network_config/public/cpp/cros_network_config_test_observer.h
+++ b/chromeos/services/network_config/public/cpp/cros_network_config_test_observer.h
@@ -10,7 +10,8 @@
 
 #include "base/macros.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 
 namespace chromeos {
 namespace network_config {
@@ -20,7 +21,7 @@
   CrosNetworkConfigTestObserver();
   ~CrosNetworkConfigTestObserver() override;
 
-  mojom::CrosNetworkConfigObserverPtr GenerateInterfacePtr();
+  mojo::PendingRemote<mojom::CrosNetworkConfigObserver> GenerateRemote();
 
   // mojom::CrosNetworkConfigObserver:
   void OnActiveNetworksChanged(
@@ -44,14 +45,14 @@
   int GetNetworkChangedCount(const std::string& guid) const;
   void ResetNetworkChanges();
 
-  mojo::Binding<mojom::CrosNetworkConfigObserver>& binding() {
-    return binding_;
+  mojo::Receiver<mojom::CrosNetworkConfigObserver>& receiver() {
+    return receiver_;
   }
 
   void FlushForTesting();
 
  private:
-  mojo::Binding<mojom::CrosNetworkConfigObserver> binding_{this};
+  mojo::Receiver<mojom::CrosNetworkConfigObserver> receiver_{this};
   int active_networks_changed_ = 0;
   std::map<std::string, int> networks_changed_;
   int network_state_list_changed_ = 0;
diff --git a/chromeos/services/network_config/public/cpp/cros_network_config_util.cc b/chromeos/services/network_config/public/cpp/cros_network_config_util.cc
index d731be0..61e6fd9 100644
--- a/chromeos/services/network_config/public/cpp/cros_network_config_util.cc
+++ b/chromeos/services/network_config/public/cpp/cros_network_config_util.cc
@@ -53,15 +53,15 @@
 int GetWirelessSignalStrength(const mojom::NetworkStateProperties* network) {
   switch (network->type) {
     case mojom::NetworkType::kCellular:
-      return network->cellular->signal_strength;
+      return network->type_state->get_cellular()->signal_strength;
     case mojom::NetworkType::kEthernet:
       return 0;
     case mojom::NetworkType::kTether:
-      return network->tether->signal_strength;
+      return network->type_state->get_tether()->signal_strength;
     case mojom::NetworkType::kVPN:
       return 0;
     case mojom::NetworkType::kWiFi:
-      return network->wifi->signal_strength;
+      return network->type_state->get_wifi()->signal_strength;
     case mojom::NetworkType::kAll:
     case mojom::NetworkType::kMobile:
     case mojom::NetworkType::kWireless:
diff --git a/chromeos/services/network_config/public/mojom/cros_network_config.mojom b/chromeos/services/network_config/public/mojom/cros_network_config.mojom
index a9548c3..dd48eb74 100644
--- a/chromeos/services/network_config/public/mojom/cros_network_config.mojom
+++ b/chromeos/services/network_config/public/mojom/cros_network_config.mojom
@@ -239,16 +239,22 @@
   string ssid;
 };
 
+union NetworkTypeStateProperties {
+  CellularStateProperties cellular;
+  EthernetStateProperties ethernet;
+  TetherStateProperties tether;
+  VPNStateProperties vpn;
+  WiFiStateProperties wifi;
+};
+
 struct NetworkStateProperties {
   CaptivePortalProvider? captive_portal_provider;
-  CellularStateProperties? cellular;
   // True if the network is configured and may be connectable.
   bool connectable = false;
   // True if an explicit connect was requested. The network state may not be
   // Connecting yet.
   bool connect_requested = false;
   ConnectionStateType connection_state;
-  EthernetStateProperties? ethernet;
   // A string describing the network error state. TODO(stevenjb): Use an enum.
   string? error_state;
   // The unique identifier for the network.
@@ -263,10 +269,8 @@
   // True for visible networks that are blocked / disallowed by policy.
   bool prohibited_by_policy = false;
   OncSource source;
-  TetherStateProperties? tether;
   NetworkType type;
-  VPNStateProperties? vpn;
-  WiFiStateProperties? wifi;
+  NetworkTypeStateProperties type_state;
 };
 
 struct DeviceStateProperties {
@@ -841,7 +845,7 @@
 // Interface for fetching and setting network configuration properties, e.g.
 // from Settings WebUI or the SystemTray.
 interface CrosNetworkConfig {
-  AddObserver(CrosNetworkConfigObserver observer);
+  AddObserver(pending_remote<CrosNetworkConfigObserver> observer);
 
   // Returns the state properties of the network matching |guid|. If the network
   // is not available returns null.
diff --git a/components/arc/intent_helper/arc_intent_helper_bridge.cc b/components/arc/intent_helper/arc_intent_helper_bridge.cc
index bc1a2d34..ebce3e3 100644
--- a/components/arc/intent_helper/arc_intent_helper_bridge.cc
+++ b/components/arc/intent_helper/arc_intent_helper_bridge.cc
@@ -274,11 +274,15 @@
     return true;
   }
 
-  for (const IntentFilter& filter : intent_filters_) {
+  for (auto& package_filters : intent_filters_) {
     // The intent helper package is used by ARC to send URLs to Chrome, so it
     // does not count as a candidate.
-    if (filter.Match(url) && !IsIntentHelperPackage(filter.package_name()))
-      return false;
+    if (IsIntentHelperPackage(package_filters.first))
+      continue;
+    for (auto& filter : package_filters.second) {
+      if (filter.Match(url))
+        return false;
+    }
   }
 
   // Didn't find any matches for Android so let Chrome handle it.
@@ -320,10 +324,18 @@
 void ArcIntentHelperBridge::OnIntentFiltersUpdated(
     std::vector<IntentFilter> filters) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  intent_filters_ = std::move(filters);
+
+  intent_filters_.clear();
+  for (auto& filter : filters)
+    intent_filters_[filter.package_name()].push_back(std::move(filter));
 
   for (auto& observer : observer_list_)
     observer.OnIntentFiltersUpdated();
 }
 
+const std::vector<IntentFilter>&
+ArcIntentHelperBridge::GetIntentFilterForPackage(
+    const std::string& package_name) {
+  return intent_filters_[package_name];
+}
 }  // namespace arc
diff --git a/components/arc/intent_helper/arc_intent_helper_bridge.h b/components/arc/intent_helper/arc_intent_helper_bridge.h
index f665aea..02c0760 100644
--- a/components/arc/intent_helper/arc_intent_helper_bridge.h
+++ b/components/arc/intent_helper/arc_intent_helper_bridge.h
@@ -119,6 +119,9 @@
 
   static const char kArcIntentHelperPackageName[];
 
+  const std::vector<IntentFilter>& GetIntentFilterForPackage(
+      const std::string& package_name);
+
  private:
   THREAD_CHECKER(thread_checker_);
 
@@ -127,9 +130,13 @@
 
   internal::ActivityIconLoader icon_loader_;
 
-  // List of intent filters from Android. Used to determine if Chrome should
-  // handle a URL without handing off to Android.
-  std::vector<IntentFilter> intent_filters_;
+  // A map of each package name to the intent filters for that package.
+  // Used to determine if Chrome should handle a URL without handing off to
+  // Android.
+  // TODO(crbug.com/853604): Now the package name exists in the map key as well
+  // as the IntentFilter struct, it is a duplication. Should update the ARC
+  // mojom type to optimise the structure.
+  std::map<std::string, std::vector<IntentFilter>> intent_filters_;
 
   base::ObserverList<ArcIntentHelperObserver>::Unchecked observer_list_;
 
diff --git a/components/arc/intent_helper/arc_intent_helper_bridge_unittest.cc b/components/arc/intent_helper/arc_intent_helper_bridge_unittest.cc
index 0ce6e72..1e51d718 100644
--- a/components/arc/intent_helper/arc_intent_helper_bridge_unittest.cc
+++ b/components/arc/intent_helper/arc_intent_helper_bridge_unittest.cc
@@ -297,7 +297,11 @@
       "www.google.com", ArcIntentHelperBridge::kArcIntentHelperPackageName));
   array.emplace_back(GetIntentFilter(
       "www.android.com", ArcIntentHelperBridge::kArcIntentHelperPackageName));
-  array.emplace_back(GetIntentFilter("dev.chromium.org", kPackageName));
+  // Let the package name start with "z" to ensure the intent helper package
+  // is not always the last package checked in the ShouldChromeHandleUrl
+  // filter matching logic. This is to ensure this unit test tests the package
+  // name checking logic properly.
+  array.emplace_back(GetIntentFilter("dev.chromium.org", "z.package.name"));
   instance_->OnIntentFiltersUpdated(std::move(array));
 
   EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("http://www.google.com")));
diff --git a/components/dom_distiller/content/browser/distillable_page_utils_browsertest.cc b/components/dom_distiller/content/browser/distillable_page_utils_browsertest.cc
index 82e34ae..9da0ace 100644
--- a/components/dom_distiller/content/browser/distillable_page_utils_browsertest.cc
+++ b/components/dom_distiller/content/browser/distillable_page_utils_browsertest.cc
@@ -76,8 +76,7 @@
     ASSERT_TRUE(embedded_test_server()->Start());
   }
 
-  void DocumentLoadedInFrame(
-      content::RenderFrameHost* render_frame_host) override {
+  void DOMContentLoaded(content::RenderFrameHost* render_frame_host) override {
     if (!render_frame_host->GetParent())
       main_frame_loaded_callback_.Run();
   }
diff --git a/components/dom_distiller/content/browser/distiller_page_web_contents.cc b/components/dom_distiller/content/browser/distiller_page_web_contents.cc
index f6f70ae..c78381a 100644
--- a/components/dom_distiller/content/browser/distiller_page_web_contents.cc
+++ b/components/dom_distiller/content/browser/distiller_page_web_contents.cc
@@ -100,7 +100,7 @@
         ExecuteJavaScript();
       } else {
         // Main frame document has not loaded yet, so wait until it has before
-        // executing JavaScript. It will trigger after DocumentLoadedInFrame is
+        // executing JavaScript. It will trigger after DOMContentLoaded is
         // called for the main frame.
         content::WebContentsObserver::Observe(
             source_page_handle_->web_contents());
@@ -149,7 +149,7 @@
   return size;
 }
 
-void DistillerPageWebContents::DocumentLoadedInFrame(
+void DistillerPageWebContents::DOMContentLoaded(
     content::RenderFrameHost* render_frame_host) {
   if (render_frame_host ==
       source_page_handle_->web_contents()->GetMainFrame()) {
diff --git a/components/dom_distiller/content/browser/distiller_page_web_contents.h b/components/dom_distiller/content/browser/distiller_page_web_contents.h
index 37857d2a..08a7df2 100644
--- a/components/dom_distiller/content/browser/distiller_page_web_contents.h
+++ b/components/dom_distiller/content/browser/distiller_page_web_contents.h
@@ -63,8 +63,7 @@
       content::WebContents* web_contents) override;
 
   // content::WebContentsObserver implementation.
-  void DocumentLoadedInFrame(
-      content::RenderFrameHost* render_frame_host) override;
+  void DOMContentLoaded(content::RenderFrameHost* render_frame_host) override;
 
   void DidFailLoad(content::RenderFrameHost* render_frame_host,
                    const GURL& validated_url,
diff --git a/components/dom_distiller/content/browser/distiller_page_web_contents_browsertest.cc b/components/dom_distiller/content/browser/distiller_page_web_contents_browsertest.cc
index 0a407662..23a3e23 100644
--- a/components/dom_distiller/content/browser/distiller_page_web_contents_browsertest.cc
+++ b/components/dom_distiller/content/browser/distiller_page_web_contents_browsertest.cc
@@ -46,7 +46,7 @@
 
 // Helper class to know how far in the loading process the current WebContents
 // has come. It will call the callback either after
-// DidCommitProvisionalLoadForFrame or DocumentLoadedInFrame is called for the
+// DidCommitProvisionalLoadForFrame or DOMContentLoaded is called for the
 // main frame, based on the value of |wait_for_document_loaded|.
 class WebContentsMainFrameHelper : public content::WebContentsObserver {
  public:
@@ -65,8 +65,7 @@
       callback_.Run();
   }
 
-  void DocumentLoadedInFrame(
-      content::RenderFrameHost* render_frame_host) override {
+  void DOMContentLoaded(content::RenderFrameHost* render_frame_host) override {
     if (wait_for_document_loaded_) {
       if (!render_frame_host->GetParent())
         callback_.Run();
diff --git a/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc b/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc
index 98f6568..5ef694e 100644
--- a/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc
+++ b/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc
@@ -65,8 +65,7 @@
       content::NavigationHandle* navigation_handle) override;
   void RenderProcessGone(base::TerminationStatus status) override;
   void WebContentsDestroyed() override;
-  void DocumentLoadedInFrame(
-      content::RenderFrameHost* render_frame_host) override;
+  void DOMContentLoaded(content::RenderFrameHost* render_frame_host) override;
   void OnInterfaceRequestFromFrame(
       content::RenderFrameHost* render_frame_host,
       const std::string& interface_name,
@@ -179,15 +178,15 @@
   base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
 }
 
-void DomDistillerViewerSource::RequestViewerHandle::DocumentLoadedInFrame(
+void DomDistillerViewerSource::RequestViewerHandle::DOMContentLoaded(
     content::RenderFrameHost* render_frame_host) {
-  // DocumentLoadedInFrame() is late enough to execute JavaScript, and is early
+  // DOMContentLoaded() is late enough to execute JavaScript, and is early
   // enough so that it's more likely that the title and content can be picked up
   // by TalkBack instead of the placeholder. If distillation is finished by
-  // DocumentLoadedInFrame(), onload() event would also be delayed, so that the
+  // DOMContentLoaded(), onload() event would also be delayed, so that the
   // accessibility focus is more likely to be on the web content. Otherwise, the
   // focus is usually on the close button of the CustomTab (CCT), or nowhere. If
-  // distillation finishes later than DocumentLoadedInFrame(), or if for some
+  // distillation finishes later than DOMContentLoaded(), or if for some
   // reason the accessibility focus is on the close button of the CCT, the title
   // could go unannounced.
   // See http://crbug.com/811417.
diff --git a/components/dom_distiller/content/browser/test/dom_distiller_js_browsertest.cc b/components/dom_distiller/content/browser/test/dom_distiller_js_browsertest.cc
index dde43cfd..a9cd463 100644
--- a/components/dom_distiller/content/browser/test/dom_distiller_js_browsertest.cc
+++ b/components/dom_distiller/content/browser/test/dom_distiller_js_browsertest.cc
@@ -30,7 +30,7 @@
 namespace {
 
 // Helper class to know how far in the loading process the current WebContents
-// has come. It will call the callback after DocumentLoadedInFrame is called for
+// has come. It will call the callback after DOMContentLoaded is called for
 // the main frame.
 class WebContentsMainFrameHelper : public content::WebContentsObserver {
  public:
@@ -38,8 +38,7 @@
                              const base::Closure& callback)
       : WebContentsObserver(web_contents), callback_(callback) {}
 
-  void DocumentLoadedInFrame(
-      content::RenderFrameHost* render_frame_host) override {
+  void DOMContentLoaded(content::RenderFrameHost* render_frame_host) override {
     if (!render_frame_host->GetParent())
       callback_.Run();
   }
diff --git a/components/dom_distiller/content/browser/web_contents_main_frame_observer.cc b/components/dom_distiller/content/browser/web_contents_main_frame_observer.cc
index ba4ad13..5e817cf 100644
--- a/components/dom_distiller/content/browser/web_contents_main_frame_observer.cc
+++ b/components/dom_distiller/content/browser/web_contents_main_frame_observer.cc
@@ -22,7 +22,7 @@
   CleanUp();
 }
 
-void WebContentsMainFrameObserver::DocumentLoadedInFrame(
+void WebContentsMainFrameObserver::DOMContentLoaded(
     content::RenderFrameHost* render_frame_host) {
   if (!render_frame_host->GetParent()) {
     is_document_loaded_in_main_frame_ = true;
diff --git a/components/dom_distiller/content/browser/web_contents_main_frame_observer.h b/components/dom_distiller/content/browser/web_contents_main_frame_observer.h
index 2a9af47..e7bef1a7 100644
--- a/components/dom_distiller/content/browser/web_contents_main_frame_observer.h
+++ b/components/dom_distiller/content/browser/web_contents_main_frame_observer.h
@@ -12,7 +12,7 @@
 
 namespace dom_distiller {
 
-// Tracks whether DocumentLoadedInFrame has been called for the main frame for
+// Tracks whether DOMContentLoaded has been called for the main frame for
 // the current main frame for the given WebContents. It removes itself as an
 // observer if the WebContents is destroyed or the render process is gone.
 class WebContentsMainFrameObserver
@@ -28,8 +28,7 @@
   bool is_initialized() const { return is_initialized_; }
 
   // content::WebContentsObserver implementation.
-  void DocumentLoadedInFrame(
-      content::RenderFrameHost* render_frame_host) override;
+  void DOMContentLoaded(content::RenderFrameHost* render_frame_host) override;
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
   void RenderProcessGone(base::TerminationStatus status) override;
@@ -41,9 +40,9 @@
   // Removes the observer and clears the WebContents member.
   void CleanUp();
 
-  // Whether DocumentLoadedInFrame has been called for the tracked WebContents
+  // Whether DOMContentLoaded has been called for the tracked WebContents
   // for the current main frame. This is cleared when the main frame navigates,
-  // and set again when DocumentLoadedInFrame is called for the main frame.
+  // and set again when DOMContentLoaded is called for the main frame.
   bool is_document_loaded_in_main_frame_;
 
   // Whether this object has been correctly initialized. This is set as soon as
diff --git a/components/dom_distiller/content/browser/web_contents_main_frame_observer_unittest.cc b/components/dom_distiller/content/browser/web_contents_main_frame_observer_unittest.cc
index d8a9b64..58b0674 100644
--- a/components/dom_distiller/content/browser/web_contents_main_frame_observer_unittest.cc
+++ b/components/dom_distiller/content/browser/web_contents_main_frame_observer_unittest.cc
@@ -55,7 +55,7 @@
   EXPECT_TRUE(main_frame_observer_->is_initialized());
   EXPECT_FALSE(main_frame_observer_->is_document_loaded_in_main_frame());
 
-  main_frame_observer_->DocumentLoadedInFrame(main_rfh());
+  main_frame_observer_->DOMContentLoaded(main_rfh());
   EXPECT_TRUE(main_frame_observer_->is_document_loaded_in_main_frame());
 }
 
@@ -79,13 +79,13 @@
 
   // Even if we didn't acknowledge a same-document navigation, if the main
   // frame loads, consider a load complete.
-  main_frame_observer_->DocumentLoadedInFrame(main_rfh());
+  main_frame_observer_->DOMContentLoaded(main_rfh());
   EXPECT_TRUE(main_frame_observer_->is_document_loaded_in_main_frame());
 }
 
 TEST_F(WebContentsMainFrameObserverTest, ResetOnPageNavigation) {
   Navigate(kMainFrame, kDifferentDocument);
-  main_frame_observer_->DocumentLoadedInFrame(main_rfh());
+  main_frame_observer_->DOMContentLoaded(main_rfh());
   EXPECT_TRUE(main_frame_observer_->is_document_loaded_in_main_frame());
 
   // Another navigation should result in waiting for a page load.
@@ -96,7 +96,7 @@
 
 TEST_F(WebContentsMainFrameObserverTest, DoesNotResetOnInPageNavigation) {
   Navigate(kMainFrame, kDifferentDocument);
-  main_frame_observer_->DocumentLoadedInFrame(main_rfh());
+  main_frame_observer_->DOMContentLoaded(main_rfh());
   EXPECT_TRUE(main_frame_observer_->is_document_loaded_in_main_frame());
 
   // Navigating withing the page should not result in waiting for a page load.
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index 58cb319..3951a32 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -126,6 +126,8 @@
     "keyword_extensions_delegate.h",
     "keyword_provider.cc",
     "keyword_provider.h",
+    "local_history_zero_suggest_provider.cc",
+    "local_history_zero_suggest_provider.h",
     "location_bar_model.h",
     "location_bar_model_delegate.cc",
     "location_bar_model_delegate.h",
diff --git a/components/omnibox/browser/DEPS b/components/omnibox/browser/DEPS
index aaf6678..e060145 100644
--- a/components/omnibox/browser/DEPS
+++ b/components/omnibox/browser/DEPS
@@ -4,6 +4,7 @@
   "+components/component_updater",
   "+components/favicon/core",
   "+components/favicon_base",
+  "+components/google/core/common/google_util.h",
   "+components/grit",
   "+components/history/core/browser",
   "+components/history/core/test",
diff --git a/components/omnibox/browser/autocomplete_classifier.cc b/components/omnibox/browser/autocomplete_classifier.cc
index b81537d..b42700d 100644
--- a/components/omnibox/browser/autocomplete_classifier.cc
+++ b/components/omnibox/browser/autocomplete_classifier.cc
@@ -44,6 +44,7 @@
       AutocompleteProvider::TYPE_CLIPBOARD |
 #endif
       AutocompleteProvider::TYPE_ZERO_SUGGEST |
+      AutocompleteProvider::TYPE_ZERO_SUGGEST_LOCAL_HISTORY |
       (base::FeatureList::IsEnabled(omnibox::kDocumentProvider)
            ? AutocompleteProvider::TYPE_DOCUMENT
            : 0) |
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc
index 4702bdff..25df87d 100644
--- a/components/omnibox/browser/autocomplete_controller.cc
+++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -37,6 +37,7 @@
 #include "components/omnibox/browser/history_quick_provider.h"
 #include "components/omnibox/browser/history_url_provider.h"
 #include "components/omnibox/browser/keyword_provider.h"
+#include "components/omnibox/browser/local_history_zero_suggest_provider.h"
 #include "components/omnibox/browser/on_device_head_provider.h"
 #include "components/omnibox/browser/search_provider.h"
 #include "components/omnibox/browser/shortcuts_provider.h"
@@ -270,6 +271,10 @@
     if (zero_suggest_provider_)
       providers_.push_back(zero_suggest_provider_);
   }
+  if (provider_types & AutocompleteProvider::TYPE_ZERO_SUGGEST_LOCAL_HISTORY) {
+    providers_.push_back(
+        LocalHistoryZeroSuggestProvider::Create(provider_client_.get(), this));
+  }
   if (provider_types & AutocompleteProvider::TYPE_DOCUMENT) {
     document_provider_ = DocumentProvider::Create(provider_client_.get(), this);
     providers_.push_back(document_provider_);
diff --git a/components/omnibox/browser/autocomplete_provider.cc b/components/omnibox/browser/autocomplete_provider.cc
index 38d1fac8c..3b8ce83 100644
--- a/components/omnibox/browser/autocomplete_provider.cc
+++ b/components/omnibox/browser/autocomplete_provider.cc
@@ -58,6 +58,8 @@
       return "Clipboard";
     case TYPE_ON_DEVICE_HEAD:
       return "OnDeviceHead";
+    case TYPE_ZERO_SUGGEST_LOCAL_HISTORY:
+      return "LocalHistoryZeroSuggest";
     default:
       NOTREACHED() << "Unhandled AutocompleteProvider::Type " << type;
       return "Unknown";
@@ -124,6 +126,8 @@
       return metrics::OmniboxEventProto::ZERO_SUGGEST;
     case TYPE_CLIPBOARD:
       return metrics::OmniboxEventProto::CLIPBOARD;
+    case TYPE_ZERO_SUGGEST_LOCAL_HISTORY:
+      return metrics::OmniboxEventProto::ZERO_SUGGEST_LOCAL_HISTORY;
     default:
       NOTREACHED() << "Unhandled AutocompleteProvider::Type " << type_;
       return metrics::OmniboxEventProto::UNKNOWN_PROVIDER;
diff --git a/components/omnibox/browser/autocomplete_provider.h b/components/omnibox/browser/autocomplete_provider.h
index 1c4e291..6bdc226 100644
--- a/components/omnibox/browser/autocomplete_provider.h
+++ b/components/omnibox/browser/autocomplete_provider.h
@@ -44,6 +44,7 @@
 // Clipboard URL                                                       |  800
 // Zero Suggest (most visited, Android only)                           |  600--
 // Zero Suggest (default, may be overridden by server)                 |  100
+// Local History Zero Suggest                                          |  500--
 //
 // UNKNOWN input type:
 // --------------------------------------------------------------------|-----
@@ -149,6 +150,7 @@
     TYPE_CLIPBOARD = 1 << 8,
     TYPE_DOCUMENT = 1 << 9,
     TYPE_ON_DEVICE_HEAD = 1 << 10,
+    TYPE_ZERO_SUGGEST_LOCAL_HISTORY = 1 << 11,
   };
 
   explicit AutocompleteProvider(Type type);
diff --git a/components/omnibox/browser/base_search_provider.h b/components/omnibox/browser/base_search_provider.h
index 55f1630..4c4a318 100644
--- a/components/omnibox/browser/base_search_provider.h
+++ b/components/omnibox/browser/base_search_provider.h
@@ -48,8 +48,7 @@
   // Returns a simpler AutocompleteMatch suitable for persistence like in
   // ShortcutsDatabase.  This wrapper function uses a number of default values
   // that may or may not be appropriate for your needs.
-  // NOTE: Use with care. Most likely you want the other CreateSearchSuggestion
-  // with protected access.
+  // NOTE: Use with care. Most likely you want the other CreateSearchSuggestion.
   static AutocompleteMatch CreateSearchSuggestion(
       const base::string16& suggestion,
       AutocompleteMatchType::Type type,
@@ -57,6 +56,32 @@
       const TemplateURL* template_url,
       const SearchTermsData& search_terms_data);
 
+  // Returns an AutocompleteMatch with the given |autocomplete_provider|
+  // for the search |suggestion|, which represents a search via |template_url|.
+  // If |template_url| is NULL, returns a match with an invalid destination URL.
+  //
+  // |input| is the original user input. Text in the input is used to highlight
+  // portions of the match contents to distinguish locally-typed text from
+  // suggested text.
+  //
+  // |input| is also necessary for various other details, like whether we should
+  // allow inline autocompletion and what the transition type should be.
+  // |in_keyword_mode| helps guarantee a non-keyword suggestion does not
+  // appear as the default match when the user is in keyword mode.
+  // |accepted_suggestion| is used to generate Assisted Query Stats.
+  // |append_extra_query_params_from_command_line| should be set if
+  // |template_url| is the default search engine, so the destination URL will
+  // contain any command-line-specified query params.
+  static AutocompleteMatch CreateSearchSuggestion(
+      AutocompleteProvider* autocomplete_provider,
+      const AutocompleteInput& input,
+      const bool in_keyword_mode,
+      const SearchSuggestionParser::SuggestResult& suggestion,
+      const TemplateURL* template_url,
+      const SearchTermsData& search_terms_data,
+      int accepted_suggestion,
+      bool append_extra_query_params_from_command_line);
+
   // A helper function to convert result from on device providers to
   // AutocompleteMatch instance.
   static AutocompleteMatch CreateOnDeviceSearchSuggestion(
@@ -117,32 +142,6 @@
   typedef std::vector<std::unique_ptr<SuggestionDeletionHandler>>
       SuggestionDeletionHandlers;
 
-  // Returns an AutocompleteMatch with the given |autocomplete_provider|
-  // for the search |suggestion|, which represents a search via |template_url|.
-  // If |template_url| is NULL, returns a match with an invalid destination URL.
-  //
-  // |input| is the original user input. Text in the input is used to highlight
-  // portions of the match contents to distinguish locally-typed text from
-  // suggested text.
-  //
-  // |input| is also necessary for various other details, like whether we should
-  // allow inline autocompletion and what the transition type should be.
-  // |in_keyword_mode| helps guarantee a non-keyword suggestion does not
-  // appear as the default match when the user is in keyword mode.
-  // |accepted_suggestion| is used to generate Assisted Query Stats.
-  // |append_extra_query_params_from_command_line| should be set if
-  // |template_url| is the default search engine, so the destination URL will
-  // contain any command-line-specified query params.
-  static AutocompleteMatch CreateSearchSuggestion(
-      AutocompleteProvider* autocomplete_provider,
-      const AutocompleteInput& input,
-      const bool in_keyword_mode,
-      const SearchSuggestionParser::SuggestResult& suggestion,
-      const TemplateURL* template_url,
-      const SearchTermsData& search_terms_data,
-      int accepted_suggestion,
-      bool append_extra_query_params_from_command_line);
-
   // Returns the appropriate value for the fill_into_edit field of an
   // AutcompleteMatch. The result consists of the suggestion text from
   // |suggest_result|, optionally prepended by the keyword from |template_url|
diff --git a/components/omnibox/browser/local_history_zero_suggest_provider.cc b/components/omnibox/browser/local_history_zero_suggest_provider.cc
new file mode 100644
index 0000000..4ab879c
--- /dev/null
+++ b/components/omnibox/browser/local_history_zero_suggest_provider.cc
@@ -0,0 +1,332 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/omnibox/browser/local_history_zero_suggest_provider.h"
+
+#include <set>
+#include <string>
+
+#include "base/bind.h"
+#include "base/i18n/case_conversion.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/trace_event/trace_event.h"
+#include "components/google/core/common/google_util.h"
+#include "components/history/core/browser/history_database.h"
+#include "components/history/core/browser/history_db_task.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/history/core/browser/url_database.h"
+#include "components/omnibox/browser/autocomplete_input.h"
+#include "components/omnibox/browser/autocomplete_match.h"
+#include "components/omnibox/browser/autocomplete_match_classification.h"
+#include "components/omnibox/browser/autocomplete_provider_client.h"
+#include "components/omnibox/browser/autocomplete_provider_listener.h"
+#include "components/omnibox/browser/autocomplete_result.h"
+#include "components/omnibox/browser/base_search_provider.h"
+#include "components/omnibox/browser/omnibox_field_trial.h"
+#include "components/omnibox/browser/omnibox_pref_names.h"
+#include "components/omnibox/common/omnibox_features.h"
+#include "components/search_engines/template_url_service.h"
+#include "url/gurl.h"
+
+namespace {
+
+// Default relevance for the LocalHistoryZeroSuggestProvider query suggestions.
+const int kLocalHistoryZeroSuggestRelevance = 500;
+
+// ZeroSuggestVariant field trial param value for the local history query
+// suggestions.
+const char kZeroSuggestLocalVariant[] = "Local";
+
+// ProviderHistoryDBTask wraps two non-null callbacks into a HistoryDBTask to
+// passed to HistoryService::ScheduleDBTask. |request_callback| gets invoked on
+// the history backend thread when the on-disk history database becomes
+// available in order to query the database and populate |url_matches_|.
+// |result_callback_| is then invoked with |url_matches_| back on the main
+// thread.
+class ProviderHistoryDBTask : public history::HistoryDBTask {
+ public:
+  using RequestCallback =
+      base::OnceCallback<history::URLRows(history::URLDatabase* url_db)>;
+  using ResultCallback = base::OnceCallback<void(const history::URLRows&)>;
+
+  ProviderHistoryDBTask(RequestCallback request_callback,
+                        ResultCallback result_callback)
+      : request_callback_(std::move(request_callback)),
+        result_callback_(std::move(result_callback)) {
+    DCHECK(!request_callback_.is_null());
+    DCHECK(!result_callback_.is_null());
+  }
+
+  ~ProviderHistoryDBTask() override {}
+
+ private:
+  // history::HistoryDBTask
+  bool RunOnDBThread(history::HistoryBackend* history_backend,
+                     history::HistoryDatabase* db) override {
+    // Query the database.
+    if (db)
+      url_matches_ = std::move(request_callback_).Run(db);
+    return true;
+  }
+
+  void DoneRunOnMainThread() override {
+    // Return the query results to the originating thread.
+    std::move(result_callback_).Run(url_matches_);
+  }
+
+  RequestCallback request_callback_;
+  ResultCallback result_callback_;
+  history::URLRows url_matches_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProviderHistoryDBTask);
+};
+
+// Queries |url_db| for URLs matching |google_search_url| prefix and returns
+// the results. |url_db| must not be null. May be called on the history backend
+// thread.
+history::URLRows QueryURLDatabaseOnHistoryThread(
+    const std::string& google_search_url,
+    const size_t provider_max_matches,
+    history::URLDatabase* url_db) {
+  DCHECK(url_db);
+
+  // Request 5x more URLs than the number of matches the provider intends to
+  // return hoping to have enough URLs to work with once duplicates are filtered
+  // out.
+  history::URLRows url_matches;
+  url_db->AutocompleteForPrefix(google_search_url, 5 * provider_max_matches,
+                                false, &url_matches);
+  return url_matches;
+}
+
+// Extracts the search terms from |url|. Collapses whitespaces, converts them to
+// lowercase and returns them. |template_url_service| must not be null.
+base::string16 GetSearchTermsFromURL(const GURL& url,
+                                     TemplateURLService* template_url_service) {
+  DCHECK(template_url_service);
+  base::string16 search_terms;
+  template_url_service->GetDefaultSearchProvider()->ExtractSearchTermsFromURL(
+      url, template_url_service->search_terms_data(), &search_terms);
+  return base::i18n::ToLower(base::CollapseWhitespace(search_terms, false));
+}
+
+}  // namespace
+
+// static
+LocalHistoryZeroSuggestProvider* LocalHistoryZeroSuggestProvider::Create(
+    AutocompleteProviderClient* client,
+    AutocompleteProviderListener* listener) {
+  return new LocalHistoryZeroSuggestProvider(client, listener);
+}
+
+void LocalHistoryZeroSuggestProvider::Start(const AutocompleteInput& input,
+                                            bool minimal_changes) {
+  TRACE_EVENT0("omnibox", "LocalHistoryZeroSuggestProvider::Start");
+
+  history_task_tracker_.TryCancel(history_db_task_id_);
+  matches_.clear();
+  Stop(true, false);
+
+  // Allow local history query suggestions only when the user is unauthenticated
+  // and is not in an off-the-record context.
+  if (client_->IsAuthenticated() || client_->IsOffTheRecord())
+    return;
+
+  // Allow local history query suggestions only when the omnibox is empty and is
+  // focused from the NTP.
+  if (!input.from_omnibox_focus() ||
+      input.type() != metrics::OmniboxInputType::EMPTY ||
+      !BaseSearchProvider::IsNTPPage(input.current_page_classification())) {
+    return;
+  }
+
+  // Allow local history query suggestions only when the user has set up Google
+  // as their default search engine.
+  TemplateURLService* template_url_service = client_->GetTemplateURLService();
+  if (!template_url_service ||
+      !template_url_service->GetDefaultSearchProvider() ||
+      template_url_service->GetDefaultSearchProvider()->GetEngineType(
+          template_url_service->search_terms_data()) != SEARCH_ENGINE_GOOGLE) {
+    return;
+  }
+
+  if (!base::Contains(OmniboxFieldTrial::GetZeroSuggestVariants(
+                          input.current_page_classification()),
+                      kZeroSuggestLocalVariant)) {
+    return;
+  }
+
+  history::HistoryService* const history_service = client_->GetHistoryService();
+  if (!history_service)
+    return;
+
+  GURL google_base_url(
+      template_url_service->search_terms_data().GoogleBaseURLValue());
+  std::string google_search_url =
+      google_util::GetGoogleSearchURL(google_base_url).spec();
+
+  // Query the in-memory URL database, if available. Otherwise, schedule a
+  // task to query the on-disk history database on the history backend thread.
+  history::URLDatabase* url_db = history_service->InMemoryDatabase();
+  if (url_db) {
+    const auto url_matches = QueryURLDatabaseOnHistoryThread(
+        google_search_url, max_matches_, url_db);
+    OnQueryURLDatabaseComplete(input, std::move(url_matches));
+  } else {
+    history_db_task_id_ = history_service->ScheduleDBTask(
+        FROM_HERE,
+        std::make_unique<ProviderHistoryDBTask>(
+            base::BindOnce(QueryURLDatabaseOnHistoryThread, google_search_url,
+                           max_matches_),
+            base::BindOnce(
+                &LocalHistoryZeroSuggestProvider::OnQueryURLDatabaseComplete,
+                weak_ptr_factory_.GetWeakPtr(), input)),
+        &history_task_tracker_);
+  }
+}
+
+void LocalHistoryZeroSuggestProvider::DeleteMatch(
+    const AutocompleteMatch& match) {
+  history::HistoryService* history_service = client_->GetHistoryService();
+  if (!history_service)
+    return;
+
+  TemplateURLService* template_url_service = client_->GetTemplateURLService();
+  if (!template_url_service ||
+      !template_url_service->GetDefaultSearchProvider()) {
+    return;
+  }
+
+  // Generate a Google search URL for the suggestion by replacing the search
+  // terms in the Google search URL template. Note that the suggestion's
+  // original search URL cannot be used here as it is unique due to the assisted
+  // query stats (aka AQS) query param which contains impressions of
+  // autocomplete matches shown at query submission time.
+  GURL google_search_url;
+  TemplateURLRef::SearchTermsArgs search_terms_args(match.contents);
+  const auto* default_search_provider =
+      template_url_service->GetDefaultSearchProvider();
+  const auto& search_terms_data = template_url_service->search_terms_data();
+  default_search_provider->ReplaceSearchTermsInURL(
+      default_search_provider->GenerateSearchURL(search_terms_data),
+      search_terms_args, search_terms_data, &google_search_url);
+
+  // Query the HistoryService for URLs matching the Google search URL. Note that
+  // this step is necessary here because 1) there may be matching fresh URLs
+  // that were not encountered in the suggestion creation phase due to looking
+  // up a maximum number of URLs in that phase. and 2) the performance overhead
+  // of requerying the HistoryService cannot be tolerated in the suggestion
+  // creation phase. Here on the other hand it can due to the small percentage
+  // of suggestions getting deleted relative to the number of suggestions shown.
+  history::QueryOptions opts;
+  opts.duplicate_policy = history::QueryOptions::KEEP_ALL_DUPLICATES;
+  opts.begin_time = history::AutocompleteAgeThreshold();
+  history_service->QueryHistory(
+      base::ASCIIToUTF16(google_search_url.spec()), opts,
+      base::BindOnce(&LocalHistoryZeroSuggestProvider::OnHistoryQueryResults,
+                     weak_ptr_factory_.GetWeakPtr(), match.contents),
+      &history_task_tracker_);
+
+  deleted_suggestions_set_.insert(match.contents);
+}
+
+LocalHistoryZeroSuggestProvider::LocalHistoryZeroSuggestProvider(
+    AutocompleteProviderClient* client,
+    AutocompleteProviderListener* listener)
+    : AutocompleteProvider(
+          AutocompleteProvider::TYPE_ZERO_SUGGEST_LOCAL_HISTORY),
+      max_matches_(AutocompleteResult::GetMaxMatches(/*is_zero_suggest=*/true)),
+      client_(client),
+      listener_(listener) {}
+
+LocalHistoryZeroSuggestProvider::~LocalHistoryZeroSuggestProvider() {}
+
+void LocalHistoryZeroSuggestProvider::OnQueryURLDatabaseComplete(
+    const AutocompleteInput& input,
+    const history::URLRows& url_matches) {
+  done_ = true;
+  matches_.clear();
+
+  // Fail if we can't extract the search terms from the URL matches.
+  TemplateURLService* template_url_service = client_->GetTemplateURLService();
+  if (!template_url_service ||
+      !template_url_service->GetDefaultSearchProvider()) {
+    return;
+  }
+
+  // Used to filter out duplicate query suggestions.
+  std::set<base::string16> seen_suggestions_set;
+
+  int relevance = kLocalHistoryZeroSuggestRelevance;
+  for (size_t i = 0; i < url_matches.size(); i++) {
+    const history::URLRow& url_row = url_matches[i];
+    const GURL& url = url_row.url();
+
+    // Discard the URL if it is not valid or fresh enough.
+    if (!url.is_valid() ||
+        url_row.last_visit() < history::AutocompleteAgeThreshold()) {
+      continue;
+    }
+
+    base::string16 search_terms =
+        GetSearchTermsFromURL(url, template_url_service);
+    if (search_terms.empty())
+      continue;
+
+    // Filter out duplicate query suggestions.
+    if (seen_suggestions_set.count(search_terms))
+      continue;
+    seen_suggestions_set.insert(search_terms);
+
+    // Filter out deleted query suggestions.
+    if (deleted_suggestions_set_.count(search_terms))
+      continue;
+
+    SearchSuggestionParser::SuggestResult suggestion(
+        /*suggestion=*/search_terms, AutocompleteMatchType::SEARCH_HISTORY,
+        /*subtype_identifier=*/0, /*from_keyword=*/false, relevance--,
+        /*relevance_from_server=*/0,
+        /*input_text=*/base::ASCIIToUTF16(std::string()));
+
+    AutocompleteMatch match = BaseSearchProvider::CreateSearchSuggestion(
+        this, input, /*in_keyword_mode=*/false, suggestion,
+        template_url_service->GetDefaultSearchProvider(),
+        template_url_service->search_terms_data(),
+        TemplateURLRef::NO_SUGGESTIONS_AVAILABLE,
+        /*append_extra_query_params_from_command_line*/ true);
+    match.deletable = true;
+
+    matches_.push_back(match);
+    if (matches_.size() >= max_matches_)
+      break;
+  }
+
+  listener_->OnProviderUpdate(true);
+}
+
+void LocalHistoryZeroSuggestProvider::OnHistoryQueryResults(
+    const base::string16& suggestion,
+    history::QueryResults results) {
+  history::HistoryService* history_service = client_->GetHistoryService();
+  if (!history_service)
+    return;
+
+  TemplateURLService* template_url_service = client_->GetTemplateURLService();
+  if (!template_url_service ||
+      !template_url_service->GetDefaultSearchProvider()) {
+    return;
+  }
+
+  // Delete the matching URLs that would generate |suggestion|.
+  std::vector<GURL> urls_to_delete;
+  for (const auto& result : results) {
+    base::string16 search_terms =
+        GetSearchTermsFromURL(result.url(), template_url_service);
+    if (search_terms == suggestion)
+      urls_to_delete.push_back(result.url());
+  }
+  history_service->DeleteURLs(urls_to_delete);
+}
diff --git a/components/omnibox/browser/local_history_zero_suggest_provider.h b/components/omnibox/browser/local_history_zero_suggest_provider.h
new file mode 100644
index 0000000..82a7808
--- /dev/null
+++ b/components/omnibox/browser/local_history_zero_suggest_provider.h
@@ -0,0 +1,73 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OMNIBOX_BROWSER_LOCAL_HISTORY_ZERO_SUGGEST_PROVIDER_H_
+#define COMPONENTS_OMNIBOX_BROWSER_LOCAL_HISTORY_ZERO_SUGGEST_PROVIDER_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "components/omnibox/browser/autocomplete_provider.h"
+
+class AutocompleteProviderClient;
+class AutocompleteProviderListener;
+
+namespace history {
+class HistoryDBTask;
+class QueryResults;
+}  // namespace history
+
+// Autocomplete provider for on-focus zero-prefix query suggestions from local
+// history when Google is the default search engine.
+class LocalHistoryZeroSuggestProvider : public AutocompleteProvider {
+ public:
+  // Creates and returns an instance of this provider.
+  static LocalHistoryZeroSuggestProvider* Create(
+      AutocompleteProviderClient* client,
+      AutocompleteProviderListener* listener);
+
+  // AutocompleteProvider:
+  void Start(const AutocompleteInput& input, bool minimal_changes) override;
+  void DeleteMatch(const AutocompleteMatch& match) override;
+
+ private:
+  LocalHistoryZeroSuggestProvider(AutocompleteProviderClient* client,
+                                  AutocompleteProviderListener* listener);
+  ~LocalHistoryZeroSuggestProvider() override;
+
+  // Creates autocomplete matches from |url_matches| and notifies |listener_|.
+  void OnQueryURLDatabaseComplete(const AutocompleteInput& input,
+                                  const history::URLRows& url_matches);
+
+  // Called when the query results from HistoryService::QueryHistory are ready.
+  // Deletes URLs in |results| that would generate |suggestion|.
+  void OnHistoryQueryResults(const base::string16& suggestion,
+                             history::QueryResults results);
+
+  // The maximum number of matches to return.
+  const size_t max_matches_;
+
+  // Used to filter out deleted query suggestions in order to prevent them from
+  // reappearing before the corresponding URLs are asynchronously deleted.
+  std::set<base::string16> deleted_suggestions_set_;
+
+  // Client for accessing TemplateUrlService, prefs, etc.
+  AutocompleteProviderClient* const client_;
+
+  // Listener to notify when matches are available.
+  AutocompleteProviderListener* const listener_;
+
+  // Used for the async tasks querying the HistoryService.
+  base::CancelableTaskTracker history_task_tracker_;
+
+  // Task ID for the history::HistoryDBTask running on history backend thread.
+  base::CancelableTaskTracker::TaskId history_db_task_id_;
+
+  base::WeakPtrFactory<LocalHistoryZeroSuggestProvider> weak_ptr_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(LocalHistoryZeroSuggestProvider);
+};
+
+#endif  // COMPONENTS_OMNIBOX_BROWSER_LOCAL_HISTORY_ZERO_SUGGEST_PROVIDER_H_
diff --git a/components/optimization_guide/hints_fetcher.cc b/components/optimization_guide/hints_fetcher.cc
index 689db95..8321502 100644
--- a/components/optimization_guide/hints_fetcher.cc
+++ b/components/optimization_guide/hints_fetcher.cc
@@ -40,7 +40,7 @@
 
 HintsFetcher::HintsFetcher(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    GURL optimization_guide_service_url,
+    const GURL& optimization_guide_service_url,
     PrefService* pref_service)
     : optimization_guide_service_url_(net::AppendOrReplaceQueryParameter(
           optimization_guide_service_url,
@@ -96,17 +96,20 @@
 
 bool HintsFetcher::FetchOptimizationGuideServiceHints(
     const std::vector<std::string>& hosts,
+    optimization_guide::proto::RequestContext request_context,
     HintsFetchedCallback hints_fetched_callback) {
   SEQUENCE_CHECKER(sequence_checker_);
 
   if (content::GetNetworkConnectionTracker()->IsOffline()) {
-    std::move(hints_fetched_callback).Run(base::nullopt);
+    std::move(hints_fetched_callback).Run(request_context, base::nullopt);
     return false;
   }
 
   if (url_loader_)
     return false;
 
+  request_context_ = request_context;
+
   get_hints_request_ = std::make_unique<proto::GetHintsRequest>();
 
   // Add all the optimizations supported by the current version of Chrome,
@@ -118,7 +121,7 @@
   // for clients to specify their supported optimization types or have a static
   // assert on the last OptimizationType.
 
-  get_hints_request_->set_context(proto::CONTEXT_BATCH_UPDATE);
+  get_hints_request_->set_context(request_context_);
 
   for (const auto& host : hosts) {
     proto::HostInfo* host_info = get_hints_request_->add_hosts();
@@ -204,9 +207,10 @@
         "OptimizationGuide.HintsFetcher.GetHintsRequest.HintCount",
         get_hints_response->hints_size());
     UpdateHostsSuccessfullyFetched();
-    std::move(hints_fetched_callback_).Run(std::move(get_hints_response));
+    std::move(hints_fetched_callback_)
+        .Run(request_context_, std::move(get_hints_response));
   } else {
-    std::move(hints_fetched_callback_).Run(base::nullopt);
+    std::move(hints_fetched_callback_).Run(request_context_, base::nullopt);
   }
 }
 
diff --git a/components/optimization_guide/hints_fetcher.h b/components/optimization_guide/hints_fetcher.h
index f0c45916..34568e0 100644
--- a/components/optimization_guide/hints_fetcher.h
+++ b/components/optimization_guide/hints_fetcher.h
@@ -31,6 +31,7 @@
 // to pass back the fetched hints response from the remote Optimization Guide
 // Service.
 using HintsFetchedCallback = base::OnceCallback<void(
+    optimization_guide::proto::RequestContext request_context,
     base::Optional<std::unique_ptr<proto::GetHintsResponse>>)>;
 
 // A class to handle requests for optimization hints from a remote Optimization
@@ -43,7 +44,7 @@
  public:
   HintsFetcher(
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      GURL optimization_guide_service_url,
+      const GURL& optimization_guide_service_url,
       PrefService* pref_service);
   virtual ~HintsFetcher();
 
@@ -54,6 +55,7 @@
   // testing.
   virtual bool FetchOptimizationGuideServiceHints(
       const std::vector<std::string>& hosts,
+      optimization_guide::proto::RequestContext request_context,
       HintsFetchedCallback hints_fetched_callback);
 
   // Set |time_clock_| for testing.
@@ -104,6 +106,10 @@
   // Holds the |URLLoader| for an active hints request.
   std::unique_ptr<network::SimpleURLLoader> url_loader_;
 
+  // Context of the fetch request. Opaque field that's returned back in the
+  // callback and is also included in the requests to the hints server.
+  optimization_guide::proto::RequestContext request_context_;
+
   // A reference to the PrefService for this profile. Not owned.
   PrefService* pref_service_ = nullptr;
 
diff --git a/components/optimization_guide/hints_fetcher_unittest.cc b/components/optimization_guide/hints_fetcher_unittest.cc
index 1fb9799..b5a89f09 100644
--- a/components/optimization_guide/hints_fetcher_unittest.cc
+++ b/components/optimization_guide/hints_fetcher_unittest.cc
@@ -57,7 +57,8 @@
 
   ~HintsFetcherTest() override {}
 
-  void OnHintsFetched(base::Optional<std::unique_ptr<proto::GetHintsResponse>>
+  void OnHintsFetched(optimization_guide::proto::RequestContext request_context,
+                      base::Optional<std::unique_ptr<proto::GetHintsResponse>>
                           get_hints_response) {
     if (get_hints_response)
       hints_fetched_ = true;
@@ -98,8 +99,9 @@
  protected:
   bool FetchHints(const std::vector<std::string>& hosts) {
     bool status = hints_fetcher_->FetchOptimizationGuideServiceHints(
-        hosts, base::BindOnce(&HintsFetcherTest::OnHintsFetched,
-                              base::Unretained(this)));
+        hosts, optimization_guide::proto::CONTEXT_BATCH_UPDATE,
+        base::BindOnce(&HintsFetcherTest::OnHintsFetched,
+                       base::Unretained(this)));
     RunUntilIdle();
     return status;
   }
diff --git a/components/optimization_guide/optimization_guide_service.cc b/components/optimization_guide/optimization_guide_service.cc
index 8bd86f7..2ff069d 100644
--- a/components/optimization_guide/optimization_guide_service.cc
+++ b/components/optimization_guide/optimization_guide_service.cc
@@ -5,6 +5,7 @@
 #include "components/optimization_guide/optimization_guide_service.h"
 
 #include "base/bind.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/task/post_task.h"
 
 namespace optimization_guide {
@@ -55,6 +56,10 @@
     return;
   }
 
+  base::UmaHistogramSparse(
+      "OptimizationGuide.OptimizationHintsComponent.MajorVersion",
+      info.version.components()[0]);
+
   hints_component_info_.emplace(info.version, info.path);
   for (auto& observer : observers_) {
     observer.OnHintsComponentAvailable(*hints_component_info_);
diff --git a/components/optimization_guide/optimization_guide_service_unittest.cc b/components/optimization_guide/optimization_guide_service_unittest.cc
index 6a359b6..e401c22 100644
--- a/components/optimization_guide/optimization_guide_service_unittest.cc
+++ b/components/optimization_guide/optimization_guide_service_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "base/version.h"
 #include "components/optimization_guide/hints_component_info.h"
@@ -99,6 +100,8 @@
 };
 
 TEST_F(OptimizationGuideServiceTest, ProcessHintsIssuesNotification) {
+  base::HistogramTester histogram_tester;
+
   AddObserver();
 
   HintsComponentInfo component_info(base::Version("1.0.0.0"),
@@ -109,9 +112,13 @@
   EXPECT_EQ(observer()->hints_component_notification_count(), 1);
   EXPECT_EQ(component_info.version, observer()->hints_component_version());
   EXPECT_EQ(component_info.path, observer()->hints_component_path());
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.OptimizationHintsComponent.MajorVersion", 1, 1);
 }
 
 TEST_F(OptimizationGuideServiceTest, ProcessHintsNewVersionProcessed) {
+  base::HistogramTester histogram_tester;
+
   AddObserver();
 
   HintsComponentInfo component_info_1(base::Version("1.0.0.0"),
@@ -125,9 +132,18 @@
   EXPECT_EQ(observer()->hints_component_notification_count(), 2);
   EXPECT_EQ(component_info_2.version, observer()->hints_component_version());
   EXPECT_EQ(component_info_2.path, observer()->hints_component_path());
+  // The histogram should be recorded twice - once for each update.
+  histogram_tester.ExpectTotalCount(
+      "OptimizationGuide.OptimizationHintsComponent.MajorVersion", 2);
+  histogram_tester.ExpectBucketCount(
+      "OptimizationGuide.OptimizationHintsComponent.MajorVersion", 1, 1);
+  histogram_tester.ExpectBucketCount(
+      "OptimizationGuide.OptimizationHintsComponent.MajorVersion", 2, 1);
 }
 
 TEST_F(OptimizationGuideServiceTest, ProcessHintsPastVersionIgnored) {
+  base::HistogramTester histogram_tester;
+
   AddObserver();
 
   HintsComponentInfo component_info_1(base::Version("2.0.0.0"),
@@ -141,9 +157,15 @@
   EXPECT_EQ(observer()->hints_component_notification_count(), 1);
   EXPECT_EQ(component_info_1.version, observer()->hints_component_version());
   EXPECT_EQ(component_info_1.path, observer()->hints_component_path());
+  // The histogram should only be recorded once - for the version it actually
+  // updated to.
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.OptimizationHintsComponent.MajorVersion", 2, 1);
 }
 
 TEST_F(OptimizationGuideServiceTest, ProcessHintsSameVersionIgnored) {
+  base::HistogramTester histogram_tester;
+
   AddObserver();
 
   HintsComponentInfo component_info_1(base::Version("2.0.0.0"),
@@ -157,10 +179,14 @@
   EXPECT_EQ(observer()->hints_component_notification_count(), 1);
   EXPECT_EQ(component_info_1.version, observer()->hints_component_version());
   EXPECT_EQ(component_info_1.path, observer()->hints_component_path());
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.OptimizationHintsComponent.MajorVersion", 2, 1);
 }
 
 TEST_F(OptimizationGuideServiceTest,
        UnregisteredObserverDoesNotReceiveNotification) {
+  base::HistogramTester histogram_tester;
+
   // Add and remove observer to ensure that observer properly unregistered.
   AddObserver();
   RemoveObserver();
@@ -171,11 +197,17 @@
   MaybeUpdateHintsComponent(component_info);
 
   EXPECT_EQ(observer()->hints_component_notification_count(), 0);
+  // We should still log the histogram since that is what the component updater
+  // storage has.
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.OptimizationHintsComponent.MajorVersion", 1, 1);
 }
 
 TEST_F(OptimizationGuideServiceTest,
        RegisteredObserverReceivesNotificationForCurrentComponent) {
-  HintsComponentInfo component_info(base::Version("1.0.0.0"),
+  base::HistogramTester histogram_tester;
+
+  HintsComponentInfo component_info(base::Version("172"),
                                     temp_dir().Append(kFileName1));
 
   MaybeUpdateHintsComponent(component_info);
@@ -185,6 +217,8 @@
   EXPECT_EQ(observer()->hints_component_notification_count(), 1);
   EXPECT_EQ(component_info.version, observer()->hints_component_version());
   EXPECT_EQ(component_info.path, observer()->hints_component_path());
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.OptimizationHintsComponent.MajorVersion", 172, 1);
 }
 
 }  // namespace optimization_guide
diff --git a/components/page_info_strings.grdp b/components/page_info_strings.grdp
index 70bd109..98f52005 100644
--- a/components/page_info_strings.grdp
+++ b/components/page_info_strings.grdp
@@ -33,14 +33,23 @@
   </message>
 
   <!-- Safety Tip summary strings -->
+  <message name="IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_TITLE" desc="Message to display in the page info bubble when the page you are on triggered a safety tip.">
+    Suspicious site
+  </message>
   <message name="IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_DESCRIPTION" desc="Body of message to display in the page info bubble when you are viewing a page that triggered a safety tip.">
     This site could be fake or fraudulent. Chrome recommends leaving now.
   </message>
   <message name="IDS_PAGE_INFO_SAFETY_TIP_LEAVE_BUTTON" desc="Text of button to leave a suspicious page shown on the safety tip page info bubble.">
     Leave this site
   </message>
-  <message name="IDS_PAGE_INFO_SAFETY_TIP_SUMMARY" desc="Message to display in the page info bubble when the page you are on triggered a safety tip.">
-    Suspicious site
+  <message name="IDS_PAGE_INFO_SAFETY_TIP_GO_TO_SITE_BUTTON" desc="Text of button to leave a suspicious page shown on the safety tip page info bubble.">
+    Yes. Go there.
+  </message>
+  <message name="IDS_PAGE_INFO_SAFETY_TIP_LOOKALIKE_TITLE" desc="Title of Safety Tip infobar on a domain that looks like another domain.">
+    Did you mean <ph name='LOOKALIKE_DOMAIN'>$1<ex>google.com</ex></ph>?
+  </message>
+  <message name="IDS_PAGE_INFO_SAFETY_TIP_LOOKALIKE_DESCRIPTION" desc="Body of an infobar warning when the user visits a page that triggered a Safety Tip because the domain looked like another domain.">
+    This site's name looks similar to that of another site. Attackers sometimes mimic sites by making small, hard-to-see changes to the domain name.
   </message>
 
   <!-- Viewing file strings -->
diff --git a/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_TITLE.png.sha1 b/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_TITLE.png.sha1
index c3b63e3..f5f4107 100644
--- a/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_TITLE.png.sha1
+++ b/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_TITLE.png.sha1
@@ -1 +1 @@
-b34b7304a8ffb374af8016eb3068dbbb88ba4ede
\ No newline at end of file
+a529530d0ddcd81921d7f17316a5ddb453b3aef6
\ No newline at end of file
diff --git a/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_GO_TO_SITE_BUTTON.png.sha1 b/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_GO_TO_SITE_BUTTON.png.sha1
new file mode 100644
index 0000000..e68090a
--- /dev/null
+++ b/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_GO_TO_SITE_BUTTON.png.sha1
@@ -0,0 +1 @@
+f0b39b6ee075f05cb44cae77812ac7e185a57581
\ No newline at end of file
diff --git a/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_LOOKALIKE_DESCRIPTION.png.sha1 b/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_LOOKALIKE_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..e68090a
--- /dev/null
+++ b/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_LOOKALIKE_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+f0b39b6ee075f05cb44cae77812ac7e185a57581
\ No newline at end of file
diff --git a/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_LOOKALIKE_TITLE.png.sha1 b/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_LOOKALIKE_TITLE.png.sha1
new file mode 100644
index 0000000..e68090a
--- /dev/null
+++ b/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_LOOKALIKE_TITLE.png.sha1
@@ -0,0 +1 @@
+f0b39b6ee075f05cb44cae77812ac7e185a57581
\ No newline at end of file
diff --git a/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_SUMMARY.png.sha1 b/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_SUMMARY.png.sha1
deleted file mode 100644
index f5f4107..0000000
--- a/components/page_info_strings_grdp/IDS_PAGE_INFO_SAFETY_TIP_SUMMARY.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a529530d0ddcd81921d7f17316a5ddb453b3aef6
\ No newline at end of file
diff --git a/components/previews/content/previews_optimization_guide_impl.cc b/components/previews/content/previews_optimization_guide_impl.cc
index 49e7cd1..8909e624 100644
--- a/components/previews/content/previews_optimization_guide_impl.cc
+++ b/components/previews/content/previews_optimization_guide_impl.cc
@@ -280,13 +280,14 @@
 
   if (top_hosts->size() > 0) {
     hints_fetcher_->FetchOptimizationGuideServiceHints(
-        *top_hosts,
+        *top_hosts, optimization_guide::proto::CONTEXT_BATCH_UPDATE,
         base::BindOnce(&PreviewsOptimizationGuideImpl::OnHintsFetched,
                        ui_weak_ptr_factory_.GetWeakPtr()));
   }
 }
 
 void PreviewsOptimizationGuideImpl::OnHintsFetched(
+    optimization_guide::proto::RequestContext request_context,
     base::Optional<std::unique_ptr<optimization_guide::proto::GetHintsResponse>>
         get_hints_response) {
   // TODO(mcrouse): this will be dropped into a backgroundtask as it will likely
diff --git a/components/previews/content/previews_optimization_guide_impl.h b/components/previews/content/previews_optimization_guide_impl.h
index b36b65b..a8ab775 100644
--- a/components/previews/content/previews_optimization_guide_impl.h
+++ b/components/previews/content/previews_optimization_guide_impl.h
@@ -124,6 +124,7 @@
   // is ready to be processed and stored for use. Virtual to be mocked in
   // testing.
   virtual void OnHintsFetched(
+      optimization_guide::proto::RequestContext request_context,
       base::Optional<
           std::unique_ptr<optimization_guide::proto::GetHintsResponse>>
           get_hints_response);
diff --git a/components/previews/content/previews_optimization_guide_impl_unittest.cc b/components/previews/content/previews_optimization_guide_impl_unittest.cc
index 2218829..e3564339 100644
--- a/components/previews/content/previews_optimization_guide_impl_unittest.cc
+++ b/components/previews/content/previews_optimization_guide_impl_unittest.cc
@@ -136,19 +136,22 @@
 
   bool FetchOptimizationGuideServiceHints(
       const std::vector<std::string>& hosts,
+      optimization_guide::proto::RequestContext request_context,
       optimization_guide::HintsFetchedCallback hints_fetched_callback)
       override {
     switch (fetch_state_) {
       case HintsFetcherEndState::kFetchFailed:
-        std::move(hints_fetched_callback).Run(base::nullopt);
+        std::move(hints_fetched_callback).Run(request_context, base::nullopt);
         return false;
       case HintsFetcherEndState::kFetchSuccessWithHints:
         hints_fetched_ = true;
-        std::move(hints_fetched_callback).Run(BuildHintsResponse({"host.com"}));
+        std::move(hints_fetched_callback)
+            .Run(request_context, BuildHintsResponse({"host.com"}));
         return true;
       case HintsFetcherEndState::kFetchSuccessWithNoHints:
         hints_fetched_ = true;
-        std::move(hints_fetched_callback).Run(BuildHintsResponse({}));
+        std::move(hints_fetched_callback)
+            .Run(request_context, BuildHintsResponse({}));
         return true;
     }
     return true;
@@ -189,11 +192,13 @@
 
  private:
   void OnHintsFetched(
+      optimization_guide::proto::RequestContext request_context,
       base::Optional<
           std::unique_ptr<optimization_guide::proto::GetHintsResponse>>
           get_hints_response) override {
     fetched_hints_stored_ = false;
     PreviewsOptimizationGuideImpl::OnHintsFetched(
+        optimization_guide::proto::CONTEXT_BATCH_UPDATE,
         std::move(get_hints_response));
   }
 
diff --git a/components/security_interstitials_strings.grdp b/components/security_interstitials_strings.grdp
index 1689dbf..e79c3a3 100644
--- a/components/security_interstitials_strings.grdp
+++ b/components/security_interstitials_strings.grdp
@@ -455,19 +455,13 @@
   </message>
 
   <!-- Safety Tip summary strings for Android (Desktop is in page_info_strings.grdp) -->
-  <message name="IDS_SAFETY_TIP_ANDROID_NAME" desc="Name of safety tips in general, displayed in bold as part of the title of the safety tip infobar.">
-    Safety tip:
-  </message>
-  <message name="IDS_SAFETY_TIP_ANDROID_BAD_REPUTATION_TITLE" desc="Title of Safety Tip infobar.">
-    <ph name="SAFETY_TIP">$1<ex>Safety tip:</ex></ph> suspicious site
-  </message>
-  <message name="IDS_SAFETY_TIP_ANDROID_BAD_REPUTATION_DESCRIPTION" desc="Body of an infobar warning when the user visits a page that triggered a Safety Tip.">
-    This site could be fake or fraudulent. Chrome recommends leaving now.
-  </message>
   <message name="IDS_SAFETY_TIP_ANDROID_IGNORE_BUTTON" desc="Text of button to leave a suspicious page. Shown on the safety tip infobar.">
-    IGNORE
+    Ignore
   </message>
   <message name="IDS_SAFETY_TIP_ANDROID_LEAVE_BUTTON" desc="Text of button to ignore the warning on a suspicious page. Shown on the safety tip infobar.">
-    LEAVE SITE
+    Leave site
+  </message>
+  <message name="IDS_SAFETY_TIP_ANDROID_GO_TO_SITE_BUTTON" desc="Text of button to ignore the warning on a suspicious page. Shown on the safety tip infobar when we suggest an alternate (safe) destination.">
+    Yes. Go there.
   </message>
 </grit-part>
diff --git a/components/security_interstitials_strings_grdp/IDS_SAFETY_TIP_ANDROID_GO_TO_SITE_BUTTON.png.sha1 b/components/security_interstitials_strings_grdp/IDS_SAFETY_TIP_ANDROID_GO_TO_SITE_BUTTON.png.sha1
new file mode 100644
index 0000000..cc7eaaed
--- /dev/null
+++ b/components/security_interstitials_strings_grdp/IDS_SAFETY_TIP_ANDROID_GO_TO_SITE_BUTTON.png.sha1
@@ -0,0 +1 @@
+18b9a6a28e716bb98d31654a92cccf63416e20f8
\ No newline at end of file
diff --git a/components/security_interstitials_strings_grdp/IDS_SAFETY_TIP_ANDROID_IGNORE_BUTTON.png.sha1 b/components/security_interstitials_strings_grdp/IDS_SAFETY_TIP_ANDROID_IGNORE_BUTTON.png.sha1
index 0f238f8..1dec9de 100644
--- a/components/security_interstitials_strings_grdp/IDS_SAFETY_TIP_ANDROID_IGNORE_BUTTON.png.sha1
+++ b/components/security_interstitials_strings_grdp/IDS_SAFETY_TIP_ANDROID_IGNORE_BUTTON.png.sha1
@@ -1 +1 @@
-fb0016e6f1d75b8d7023df83680103ec528713d4
\ No newline at end of file
+d78ee69ac678f6b20a323feb48b48a38bfe2d848
\ No newline at end of file
diff --git a/components/security_interstitials_strings_grdp/IDS_SAFETY_TIP_ANDROID_LEAVE_BUTTON.png.sha1 b/components/security_interstitials_strings_grdp/IDS_SAFETY_TIP_ANDROID_LEAVE_BUTTON.png.sha1
index 0f238f8..1dec9de 100644
--- a/components/security_interstitials_strings_grdp/IDS_SAFETY_TIP_ANDROID_LEAVE_BUTTON.png.sha1
+++ b/components/security_interstitials_strings_grdp/IDS_SAFETY_TIP_ANDROID_LEAVE_BUTTON.png.sha1
@@ -1 +1 @@
-fb0016e6f1d75b8d7023df83680103ec528713d4
\ No newline at end of file
+d78ee69ac678f6b20a323feb48b48a38bfe2d848
\ No newline at end of file
diff --git a/components/services/patch/public/cpp/patch.cc b/components/services/patch/public/cpp/patch.cc
index 60c3a2f..83fcbc6 100644
--- a/components/services/patch/public/cpp/patch.cc
+++ b/components/services/patch/public/cpp/patch.cc
@@ -38,8 +38,8 @@
 
   ~PatchParams() = default;
 
-  // The FilePatcherPtr is stored so it does not get deleted before the callback
-  // runs.
+  // The mojo::Remote<FilePatcher> is stored so it does not get deleted before
+  // the callback runs.
   mojo::Remote<mojom::FilePatcher> file_patcher_;
 
   PatchCallback callback_;
diff --git a/components/services/unzip/public/cpp/unzip.cc b/components/services/unzip/public/cpp/unzip.cc
index 5e8ffb3..d254d6e 100644
--- a/components/services/unzip/public/cpp/unzip.cc
+++ b/components/services/unzip/public/cpp/unzip.cc
@@ -19,8 +19,9 @@
 #include "components/services/filesystem/directory_impl.h"
 #include "components/services/filesystem/lock_table.h"
 #include "components/services/unzip/public/mojom/unzipper.mojom.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 
 namespace unzip {
 
@@ -28,9 +29,10 @@
 
 class UnzipFilter : public unzip::mojom::UnzipFilter {
  public:
-  UnzipFilter(unzip::mojom::UnzipFilterRequest request,
+  UnzipFilter(mojo::PendingReceiver<unzip::mojom::UnzipFilter> receiver,
               UnzipFilterCallback filter_callback)
-      : binding_(this, std::move(request)), filter_callback_(filter_callback) {}
+      : receiver_(this, std::move(receiver)),
+        filter_callback_(filter_callback) {}
 
  private:
   // unzip::mojom::UnzipFilter implementation:
@@ -39,7 +41,7 @@
     std::move(callback).Run(filter_callback_.Run(path));
   }
 
-  mojo::Binding<unzip::mojom::UnzipFilter> binding_;
+  mojo::Receiver<unzip::mojom::UnzipFilter> receiver_;
   UnzipFilterCallback filter_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(UnzipFilter);
@@ -111,10 +113,10 @@
     return;
   }
 
-  filesystem::mojom::DirectoryPtr directory_ptr;
-  mojo::MakeStrongBinding(
+  mojo::PendingRemote<filesystem::mojom::Directory> directory_remote;
+  mojo::MakeSelfOwnedReceiver(
       std::make_unique<filesystem::DirectoryImpl>(output_dir, nullptr, nullptr),
-      mojo::MakeRequest(&directory_ptr));
+      directory_remote.InitWithNewPipeAndPassReceiver());
 
   // |result_callback| is shared between the connection error handler and the
   // Unzip call using a refcounted UnzipParams object that owns
@@ -128,18 +130,18 @@
 
   if (filter_callback.is_null()) {
     unzip_params->unzipper()->Unzip(std::move(zip_file),
-                                    std::move(directory_ptr),
+                                    std::move(directory_remote),
                                     base::BindOnce(&UnzipDone, unzip_params));
     return;
   }
 
-  unzip::mojom::UnzipFilterPtr unzip_filter_ptr;
+  mojo::PendingRemote<unzip::mojom::UnzipFilter> unzip_filter_remote;
   unzip_params->set_unzip_filter(std::make_unique<UnzipFilter>(
-      mojo::MakeRequest(&unzip_filter_ptr), filter_callback));
+      unzip_filter_remote.InitWithNewPipeAndPassReceiver(), filter_callback));
 
   unzip_params->unzipper()->UnzipWithFilter(
-      std::move(zip_file), std::move(directory_ptr),
-      std::move(unzip_filter_ptr), base::BindOnce(&UnzipDone, unzip_params));
+      std::move(zip_file), std::move(directory_remote),
+      std::move(unzip_filter_remote), base::BindOnce(&UnzipDone, unzip_params));
 }
 
 }  // namespace
diff --git a/components/services/unzip/public/mojom/unzipper.mojom b/components/services/unzip/public/mojom/unzipper.mojom
index ece5f1069..8fd46f5a 100644
--- a/components/services/unzip/public/mojom/unzipper.mojom
+++ b/components/services/unzip/public/mojom/unzipper.mojom
@@ -16,13 +16,14 @@
 interface Unzipper {
   // Unzip |zip_file| into |output_dir|.
   // Returns true on success, false otherwise.
-  Unzip(mojo_base.mojom.File zip_file, filesystem.mojom.Directory output_dir)
-      => (bool result);
+  Unzip(mojo_base.mojom.File zip_file,
+        pending_remote<filesystem.mojom.Directory> output_dir)
+       => (bool result);
 
   // Same as |unzip| but only includes the files for which |filter| returns
   // true. Note that this incurs one IPC for each file of the archive.
   UnzipWithFilter(
       mojo_base.mojom.File zip_file,
-      filesystem.mojom.Directory output_dir,
-      UnzipFilter filter) => (bool result);
+      pending_remote<filesystem.mojom.Directory> output_dir,
+      pending_remote<UnzipFilter> filter) => (bool result);
 };
diff --git a/components/services/unzip/unzipper_impl.cc b/components/services/unzip/unzipper_impl.cc
index 76b8a508..04602bb 100644
--- a/components/services/unzip/unzipper_impl.cc
+++ b/components/services/unzip/unzipper_impl.cc
@@ -12,6 +12,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "components/services/filesystem/public/mojom/directory.mojom.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/zlib/google/zip.h"
 #include "third_party/zlib/google/zip_reader.h"
 
@@ -43,26 +44,24 @@
 }
 
 // Modifies output_dir to point to the final directory.
-bool CreateDirectory(filesystem::mojom::DirectoryPtr* output_dir,
+bool CreateDirectory(filesystem::mojom::Directory* output_dir,
                      const base::FilePath& path) {
   base::File::Error err = base::File::Error::FILE_OK;
-  return (*output_dir)
-             ->OpenDirectory(PathToMojoString(path), mojo::NullReceiver(),
-                             filesystem::mojom::kFlagOpenAlways, &err) &&
+  return output_dir->OpenDirectory(PathToMojoString(path), mojo::NullReceiver(),
+                                   filesystem::mojom::kFlagOpenAlways, &err) &&
          err == base::File::Error::FILE_OK;
 }
 
 std::unique_ptr<zip::WriterDelegate> MakeFileWriterDelegateNoParent(
-    filesystem::mojom::DirectoryPtr* output_dir,
+    filesystem::mojom::Directory* output_dir,
     const base::FilePath& path) {
   auto file = std::make_unique<base::File>();
   base::File::Error err;
-  if (!(*output_dir)
-           ->OpenFileHandle(PathToMojoString(path),
-                            filesystem::mojom::kFlagCreate |
-                                filesystem::mojom::kFlagWrite |
-                                filesystem::mojom::kFlagWriteAttributes,
-                            &err, file.get()) ||
+  if (!output_dir->OpenFileHandle(PathToMojoString(path),
+                                  filesystem::mojom::kFlagCreate |
+                                      filesystem::mojom::kFlagWrite |
+                                      filesystem::mojom::kFlagWriteAttributes,
+                                  &err, file.get()) ||
       err != base::File::Error::FILE_OK) {
     return std::make_unique<DudWriterDelegate>();
   }
@@ -70,30 +69,29 @@
 }
 
 std::unique_ptr<zip::WriterDelegate> MakeFileWriterDelegate(
-    filesystem::mojom::DirectoryPtr* output_dir,
+    filesystem::mojom::Directory* output_dir,
     const base::FilePath& path) {
   if (path == path.BaseName())
     return MakeFileWriterDelegateNoParent(output_dir, path);
-  filesystem::mojom::DirectoryPtr parent;
+  mojo::Remote<filesystem::mojom::Directory> parent;
   base::File::Error err;
-  if (!(*output_dir)
-           ->OpenDirectory(PathToMojoString(path.DirName()),
-                           mojo::MakeRequest(&parent),
-                           filesystem::mojom::kFlagOpenAlways, &err) ||
+  if (!output_dir->OpenDirectory(PathToMojoString(path.DirName()),
+                                 parent.BindNewPipeAndPassReceiver(),
+                                 filesystem::mojom::kFlagOpenAlways, &err) ||
       err != base::File::Error::FILE_OK) {
     return std::make_unique<DudWriterDelegate>();
   }
-  return MakeFileWriterDelegateNoParent(&parent, path.BaseName());
+  return MakeFileWriterDelegateNoParent(parent.get(), path.BaseName());
 }
 
 bool FilterNoFiles(const base::FilePath& unused) {
   return true;
 }
 
-bool FilterWithFilterPtr(mojom::UnzipFilterPtr* filter,
-                         const base::FilePath& path) {
+bool FilterWithFilterRemote(mojom::UnzipFilter* filter,
+                            const base::FilePath& path) {
   bool result = false;
-  (*filter)->ShouldUnzipFile(path, &result);
+  filter->ShouldUnzipFile(path, &result);
   return result;
 }
 
@@ -106,22 +104,29 @@
 
 UnzipperImpl::~UnzipperImpl() = default;
 
-void UnzipperImpl::Unzip(base::File zip_file,
-                         filesystem::mojom::DirectoryPtr output_dir,
-                         UnzipCallback callback) {
+void UnzipperImpl::Unzip(
+    base::File zip_file,
+    mojo::PendingRemote<filesystem::mojom::Directory> output_dir_remote,
+    UnzipCallback callback) {
   DCHECK(zip_file.IsValid());
+  mojo::Remote<filesystem::mojom::Directory> output_dir(
+      std::move(output_dir_remote));
   std::move(callback).Run(zip::UnzipWithFilterAndWriters(
       zip_file.GetPlatformFile(),
-      base::BindRepeating(&MakeFileWriterDelegate, &output_dir),
-      base::BindRepeating(&CreateDirectory, &output_dir),
+      base::BindRepeating(&MakeFileWriterDelegate, output_dir.get()),
+      base::BindRepeating(&CreateDirectory, output_dir.get()),
       base::BindRepeating(&FilterNoFiles), /*log_skipped_files=*/false));
 }
 
-void UnzipperImpl::UnzipWithFilter(base::File zip_file,
-                                   filesystem::mojom::DirectoryPtr output_dir,
-                                   mojom::UnzipFilterPtr filter,
-                                   UnzipCallback callback) {
+void UnzipperImpl::UnzipWithFilter(
+    base::File zip_file,
+    mojo::PendingRemote<filesystem::mojom::Directory> output_dir_remote,
+    mojo::PendingRemote<mojom::UnzipFilter> filter_remote,
+    UnzipCallback callback) {
   DCHECK(zip_file.IsValid());
+  mojo::Remote<filesystem::mojom::Directory> output_dir(
+      std::move(output_dir_remote));
+  mojo::Remote<mojom::UnzipFilter> filter(std::move(filter_remote));
 
   // Note that we pass a pointer to |filter| below, as it is a repeating
   // callback and transferring its value would cause the callback to fail when
@@ -130,9 +135,9 @@
   // the method returns.
   std::move(callback).Run(zip::UnzipWithFilterAndWriters(
       zip_file.GetPlatformFile(),
-      base::BindRepeating(&MakeFileWriterDelegate, &output_dir),
-      base::BindRepeating(&CreateDirectory, &output_dir),
-      base::BindRepeating(&FilterWithFilterPtr, &filter),
+      base::BindRepeating(&MakeFileWriterDelegate, output_dir.get()),
+      base::BindRepeating(&CreateDirectory, output_dir.get()),
+      base::BindRepeating(&FilterWithFilterRemote, filter.get()),
       /*log_skipped_files=*/false));
 }
 
diff --git a/components/services/unzip/unzipper_impl.h b/components/services/unzip/unzipper_impl.h
index d5cc821..9de2a09 100644
--- a/components/services/unzip/unzipper_impl.h
+++ b/components/services/unzip/unzipper_impl.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "components/services/unzip/public/mojom/unzipper.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 
 namespace unzip {
@@ -28,14 +29,16 @@
 
  private:
   // unzip::mojom::Unzipper:
-  void Unzip(base::File zip_file,
-             filesystem::mojom::DirectoryPtr output_dir,
-             UnzipCallback callback) override;
+  void Unzip(
+      base::File zip_file,
+      mojo::PendingRemote<filesystem::mojom::Directory> output_dir_remote,
+      UnzipCallback callback) override;
 
-  void UnzipWithFilter(base::File zip_file,
-                       filesystem::mojom::DirectoryPtr output_dir,
-                       mojom::UnzipFilterPtr filter,
-                       UnzipWithFilterCallback callback) override;
+  void UnzipWithFilter(
+      base::File zip_file,
+      mojo::PendingRemote<filesystem::mojom::Directory> output_dir_remote,
+      mojo::PendingRemote<mojom::UnzipFilter> filter_remote,
+      UnzipWithFilterCallback callback) override;
 
   mojo::Receiver<mojom::Unzipper> receiver_{this};
 
diff --git a/components/sync/driver/resources/sync_log.js b/components/sync/driver/resources/sync_log.js
index bb322aa..25fe452 100644
--- a/components/sync/driver/resources/sync_log.js
+++ b/components/sync/driver/resources/sync_log.js
@@ -96,7 +96,7 @@
       entry.textDetails = JSON.stringify(entry.details, null, 2);
       this.entries.push(entry);
       // Fire append event.
-      const e = cr.doc.createEvent('CustomEvent');
+      const e = document.createEvent('CustomEvent');
       e.initCustomEvent('append', false, false, entry);
       this.dispatchEvent(e);
     }
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_error_resource.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_error_resource.Nexus_5-19.png.sha1
deleted file mode 100644
index 2fc1d59..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_error_resource.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-2ffcb95d98413c66e0a3effc40e7209795b0e83a
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_error_resource_expanded.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_error_resource_expanded.Nexus_5-19.png.sha1
deleted file mode 100644
index 4a76a1b..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_error_resource_expanded.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-e1ddf2d0bc4b70ec45ae1eaa47191af60d2b9588
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_error_string.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_error_string.Nexus_5-19.png.sha1
deleted file mode 100644
index 56f22eb..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_error_string.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a0799e2686eab6cd19fb3bed34c62e1e25a137a8
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_error_string_expanded.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_error_string_expanded.Nexus_5-19.png.sha1
deleted file mode 100644
index a4524ef..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_error_string_expanded.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-362b6166f2477a40d0237ecc170ef3f7f37ace64
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_initial.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_initial.Nexus_5-19.png.sha1
deleted file mode 100644
index 7822493..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_initial.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-f71440a596cf9b0b089402573e0125e2372a170a
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_initial_expanded.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_initial_expanded.Nexus_5-19.png.sha1
deleted file mode 100644
index 75bf03d..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_initial_expanded.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-ce49a8a3419025d7ea50a07d8b70731873ada4b4
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_processing.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_processing.Nexus_5-19.png.sha1
deleted file mode 100644
index b6024ac..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_processing.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-050b33e82d278745080ec9fe87272fb485148a35
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_processing_expanded.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_processing_expanded.Nexus_5-19.png.sha1
deleted file mode 100644
index ef200e4..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_processing_expanded.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-4f97844b18a4bb5de49e9905a483f782855a0512
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_success.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_success.Nexus_5-19.png.sha1
deleted file mode 100644
index 8d21e27a..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_success.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-4ee5d58a05ea1df7e21428d3c071f3533dcb4320
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_success_expanded.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_success_expanded.Nexus_5-19.png.sha1
deleted file mode 100644
index 44807813..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.button_success_expanded.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-e07f3ca6aec1bc7a48ac62df908eb1bbc2f9967e
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_error_resource.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_error_resource.Nexus_5-19.png.sha1
deleted file mode 100644
index eaf0958b..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_error_resource.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-01f5b5212714fea6108aa75cd1a763a5c5d2c5fa
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_error_resource_expanded.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_error_resource_expanded.Nexus_5-19.png.sha1
deleted file mode 100644
index 169cc70..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_error_resource_expanded.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-059a58733a525f89492e1d06541dd595b64e2674
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_error_string.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_error_string.Nexus_5-19.png.sha1
deleted file mode 100644
index 70e1d181..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_error_string.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-597dedb5aa98d1c7985fd815632477848c435412
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_error_string_expanded.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_error_string_expanded.Nexus_5-19.png.sha1
deleted file mode 100644
index 2da2fba0..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_error_string_expanded.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-56d31b38e22da4c2399c5491cccd397c27945196
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_initial.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_initial.Nexus_5-19.png.sha1
deleted file mode 100644
index 3369e0b1..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_initial.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-12c6daa5fbfea11eb7a7c239440b2585eae3d777
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_initial_expanded.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_initial_expanded.Nexus_5-19.png.sha1
deleted file mode 100644
index 37e9d8f..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_initial_expanded.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-cbcc9055beee80114ba66035ba34f473ae168467
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_processing.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_processing.Nexus_5-19.png.sha1
deleted file mode 100644
index e3a6605..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_processing.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-792edcbad644e560aab2e309daf3ea9fe3166ada
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_processing_expanded.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_processing_expanded.Nexus_5-19.png.sha1
deleted file mode 100644
index aee05d4..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_processing_expanded.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-fdd6f1becf73f7f50bd8eec9c530ccb65c95414c
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_success.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_success.Nexus_5-19.png.sha1
deleted file mode 100644
index 342cde3..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_success.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-7b74244d173e0b7540671faab1f8047812b8a8d6
\ No newline at end of file
diff --git a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_success_expanded.Nexus_5-19.png.sha1 b/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_success_expanded.Nexus_5-19.png.sha1
deleted file mode 100644
index d1200af..0000000
--- a/components/test/data/payments/render_tests/MicrotransactionRenderTest.fingerprint_success_expanded.Nexus_5-19.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-c513c5a353ac108a5499454a404f23d491b0a434
\ No newline at end of file
diff --git a/components/translate/translate_internals/translate_internals.js b/components/translate/translate_internals/translate_internals.js
index e83f03f5..474ab5a 100644
--- a/components/translate/translate_internals/translate_internals.js
+++ b/components/translate/translate_internals/translate_internals.js
@@ -625,7 +625,8 @@
  * The entry point of the UI.
  */
 function main() {
-  cr.doc.addEventListener('DOMContentLoaded', cr.translateInternals.initialize);
+  document.addEventListener(
+      'DOMContentLoaded', cr.translateInternals.initialize);
 }
 
 main();
diff --git a/content/browser/android/web_contents_observer_proxy.cc b/content/browser/android/web_contents_observer_proxy.cc
index e3ccd1a..017ebf9 100644
--- a/content/browser/android/web_contents_observer_proxy.cc
+++ b/content/browser/android/web_contents_observer_proxy.cc
@@ -165,7 +165,7 @@
       !render_frame_host->GetParent());
 }
 
-void WebContentsObserverProxy::DocumentLoadedInFrame(
+void WebContentsObserverProxy::DOMContentLoaded(
     RenderFrameHost* render_frame_host) {
   JNIEnv* env = AttachCurrentThread();
   Java_WebContentsObserverProxy_documentLoadedInFrame(
diff --git a/content/browser/android/web_contents_observer_proxy.h b/content/browser/android/web_contents_observer_proxy.h
index c604154e..83302ad 100644
--- a/content/browser/android/web_contents_observer_proxy.h
+++ b/content/browser/android/web_contents_observer_proxy.h
@@ -50,7 +50,7 @@
 
   void DidFinishLoad(RenderFrameHost* render_frame_host,
                      const GURL& validated_url) override;
-  void DocumentLoadedInFrame(RenderFrameHost* render_frame_host) override;
+  void DOMContentLoaded(RenderFrameHost* render_frame_host) override;
   void NavigationEntryCommitted(
       const LoadCommittedDetails& load_details) override;
   void NavigationEntriesDeleted() override;
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index 216da3e..0b77194 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -15,6 +15,7 @@
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/screen_enumeration/screen_enumeration_impl.h"
 #include "content/browser/service_worker/service_worker_provider_host.h"
+#include "content/browser/wake_lock/wake_lock_service_impl.h"
 #include "content/browser/worker_host/dedicated_worker_host.h"
 #include "content/browser/worker_host/shared_worker_host.h"
 #include "content/public/browser/browser_context.h"
@@ -40,6 +41,7 @@
 #include "third_party/blink/public/mojom/picture_in_picture/picture_in_picture.mojom.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom.h"
 #include "third_party/blink/public/mojom/speech/speech_synthesis.mojom.h"
+#include "third_party/blink/public/mojom/wake_lock/wake_lock.mojom.h"
 #include "third_party/blink/public/mojom/webaudio/audio_context_manager.mojom.h"
 #include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
 #include "third_party/blink/public/mojom/webauthn/virtual_authenticator.mojom.h"
@@ -165,6 +167,8 @@
       base::BindRepeating(&KeyboardLockServiceImpl::CreateMojoService));
   map->Add<blink::mojom::PictureInPictureService>(
       base::BindRepeating(&PictureInPictureServiceImpl::Create));
+  map->Add<blink::mojom::WakeLockService>(
+      base::BindRepeating(&WakeLockServiceImpl::Create));
   GetContentClient()->browser()->RegisterBrowserInterfaceBindersForFrame(map);
 }
 
diff --git a/content/browser/devtools/devtools_instrumentation.cc b/content/browser/devtools/devtools_instrumentation.cc
index 5fa8432..9eb2832 100644
--- a/content/browser/devtools/devtools_instrumentation.cc
+++ b/content/browser/devtools/devtools_instrumentation.cc
@@ -199,9 +199,12 @@
                    ->GetFrameTree()
                    ->root();
     } else {
-      return result;
+      parent = frame_tree_node->original_opener();
     }
   }
+  if (!parent)
+    return result;
+
   agent_host = RenderFrameDevToolsAgentHost::GetFor(parent);
   if (agent_host) {
     for (auto* target_handler :
diff --git a/content/browser/devtools/protocol/target_auto_attacher.cc b/content/browser/devtools/protocol/target_auto_attacher.cc
index 8a80f23a..83843a6 100644
--- a/content/browser/devtools/protocol/target_auto_attacher.cc
+++ b/content/browser/devtools/protocol/target_auto_attacher.cc
@@ -223,6 +223,20 @@
   scoped_refptr<DevToolsAgentHost> agent_host =
       RenderFrameDevToolsAgentHost::FindForDangling(frame_tree_node);
 
+  // Process the window.open auto-attaches for new targets.
+  if (frame_tree_node->original_opener()) {
+    if (!agent_host) {
+      agent_host =
+          RenderFrameDevToolsAgentHost::CreateForCrossProcessNavigation(
+              navigation_request);
+    }
+    if (auto_attached_hosts_.find(agent_host) != auto_attached_hosts_.end())
+      return nullptr;
+    attach_callback_.Run(agent_host.get(), wait_for_debugger_on_start_);
+    auto_attached_hosts_.insert(agent_host);
+    return wait_for_debugger_on_start_ ? agent_host.get() : nullptr;
+  }
+
   bool old_cross_process = !!agent_host;
   bool is_portal_main_frame =
       frame_tree_node->IsMainFrame() &&
@@ -237,7 +251,6 @@
   if (new_cross_process) {
     agent_host = RenderFrameDevToolsAgentHost::CreateForCrossProcessNavigation(
         navigation_request);
-    DCHECK(auto_attached_hosts_.find(agent_host) == auto_attached_hosts_.end());
     attach_callback_.Run(agent_host.get(), wait_for_debugger_on_start_);
     auto_attached_hosts_.insert(agent_host);
     return wait_for_debugger_on_start_ ? agent_host.get() : nullptr;
diff --git a/content/browser/devtools/protocol/target_handler.cc b/content/browser/devtools/protocol/target_handler.cc
index ed5f730..d602d63 100644
--- a/content/browser/devtools/protocol/target_handler.cc
+++ b/content/browser/devtools/protocol/target_handler.cc
@@ -472,7 +472,7 @@
 }
 
 Response TargetHandler::Disable() {
-  SetAutoAttachInternal(false, false, false, base::DoNothing());
+  SetAutoAttachInternal(false, false, false, false, base::DoNothing());
   SetDiscoverTargets(false);
   auto_attached_sessions_.clear();
   attached_sessions_.clear();
@@ -487,6 +487,11 @@
     NavigationHandle* navigation_handle) {
   if (!auto_attacher_.ShouldThrottleFramesNavigation())
     return nullptr;
+  FrameTreeNode* frame_tree_node =
+      NavigationRequest::From(navigation_handle)->frame_tree_node();
+  bool is_window_open = frame_tree_node->original_opener();
+  if (is_window_open && !attach_to_window_open_)
+    return nullptr;
   return std::make_unique<Throttle>(weak_factory_.GetWeakPtr(),
                                     navigation_handle);
 }
@@ -505,8 +510,10 @@
 void TargetHandler::SetAutoAttachInternal(bool auto_attach,
                                           bool wait_for_debugger_on_start,
                                           bool flatten,
+                                          bool window_open,
                                           base::OnceClosure callback) {
   flatten_auto_attach_ = flatten;
+  attach_to_window_open_ = window_open;
   auto_attacher_.SetAutoAttach(auto_attach, wait_for_debugger_on_start,
                                std::move(callback));
   if (!auto_attacher_.ShouldThrottleFramesNavigation())
@@ -574,9 +581,11 @@
     bool auto_attach,
     bool wait_for_debugger_on_start,
     Maybe<bool> flatten,
+    Maybe<bool> window_open,
     std::unique_ptr<SetAutoAttachCallback> callback) {
   SetAutoAttachInternal(
       auto_attach, wait_for_debugger_on_start, flatten.fromMaybe(false),
+      window_open.fromMaybe(false),
       base::BindOnce(&SetAutoAttachCallback::sendSuccess, std::move(callback)));
 }
 
diff --git a/content/browser/devtools/protocol/target_handler.h b/content/browser/devtools/protocol/target_handler.h
index 26361d3..ea0dd51 100644
--- a/content/browser/devtools/protocol/target_handler.h
+++ b/content/browser/devtools/protocol/target_handler.h
@@ -63,6 +63,7 @@
   void SetAutoAttach(bool auto_attach,
                      bool wait_for_debugger_on_start,
                      Maybe<bool> flatten,
+                     Maybe<bool> window_open,
                      std::unique_ptr<SetAutoAttachCallback> callback) override;
   Response SetRemoteLocations(
       std::unique_ptr<protocol::Array<Target::RemoteLocation>>) override;
@@ -114,6 +115,7 @@
   void SetAutoAttachInternal(bool auto_attach,
                              bool wait_for_debugger_on_start,
                              bool flatten,
+                             bool window_open,
                              base::OnceClosure callback);
 
   // DevToolsAgentHostObserver implementation.
@@ -129,6 +131,7 @@
   std::unique_ptr<Target::Frontend> frontend_;
   TargetAutoAttacher auto_attacher_;
   bool flatten_auto_attach_ = false;
+  bool attach_to_window_open_ = false;
   bool discover_;
   std::map<std::string, std::unique_ptr<Session>> attached_sessions_;
   std::map<DevToolsAgentHost*, Session*> auto_attached_sessions_;
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index b96c4d82..6764afb 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -104,7 +104,6 @@
 #include "content/browser/speech/speech_recognition_dispatcher_host.h"
 #include "content/browser/speech/speech_synthesis_impl.h"
 #include "content/browser/storage_partition_impl.h"
-#include "content/browser/wake_lock/wake_lock_service_impl.h"
 #include "content/browser/web_package/bundled_exchanges_handle.h"
 #include "content/browser/web_package/prefetched_signed_exchange_cache.h"
 #include "content/browser/webauth/authenticator_environment_impl.h"
@@ -4453,9 +4452,6 @@
       GetProcess()->GetStoragePartition()->GetFileSystemContext(),
       ChromeBlobStorageContext::GetFor(GetProcess()->GetBrowserContext())));
 
-  registry_->AddInterface(base::BindRepeating(&WakeLockServiceImpl::Create,
-                                              base::Unretained(this)));
-
   if (base::FeatureList::IsEnabled(blink::features::kNativeFileSystemAPI)) {
     registry_->AddInterface(base::BindRepeating(
         [](RenderFrameHostImpl* frame,
diff --git a/content/browser/frame_host/render_frame_host_impl_browsertest.cc b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
index a9c912c..f981978 100644
--- a/content/browser/frame_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
@@ -3159,7 +3159,7 @@
 
  protected:
   // WebContentsObserver:
-  void DocumentLoadedInFrame(RenderFrameHost* render_Frame_host) override {
+  void DOMContentLoaded(RenderFrameHost* render_Frame_host) override {
     callback_.Run();
   }
 
diff --git a/content/browser/frame_host/render_frame_host_manager_browsertest.cc b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
index ee135a8..d851a181 100644
--- a/content/browser/frame_host/render_frame_host_manager_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
@@ -5804,36 +5804,6 @@
   DISALLOW_COPY_AND_ASSIGN(AssertForegroundHelper);
 };
 
-// Observer class that waits until the OS process for a specific
-// RenderProcessHost is ready to be used.
-// TODO(nasko): Consider moving this into RenderProcessHostWatcher.
-class RenderProcessReadyObserver : public RenderProcessHostObserver {
- public:
-  explicit RenderProcessReadyObserver(RenderProcessHost* render_process_host)
-      : render_process_host_(render_process_host),
-        quit_closure_(run_loop_.QuitClosure()) {
-    render_process_host_->AddObserver(this);
-  }
-  ~RenderProcessReadyObserver() override {
-    render_process_host_->RemoveObserver(this);
-  }
-
-  // Waits until the renderer process is ready.
-  void Wait() { run_loop_.Run(); }
-
- private:
-  // RenderProcessHostObserver overrides.
-  void RenderProcessReady(RenderProcessHost* host) override {
-    std::move(quit_closure_).Run();
-  }
-
-  RenderProcessHost* render_process_host_;
-  base::RunLoop run_loop_;
-  base::OnceClosure quit_closure_;
-
-  DISALLOW_COPY_AND_ASSIGN(RenderProcessReadyObserver);
-};
-
 }  // namespace
 
 // This is a regression test for https://crbug.com/560446. It ensures the
@@ -5887,7 +5857,8 @@
 
   // Wait for the underlying OS process to have launched and be ready to
   // receive IPCs.
-  RenderProcessReadyObserver process_observer(speculative_rph);
+  RenderProcessHostWatcher process_observer(
+      speculative_rph, RenderProcessHostWatcher::WATCH_FOR_PROCESS_READY);
   process_observer.Wait();
 
   // Kick off an infinite check against self that the process used for
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index adbfc2c..3d607d0 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -46,7 +46,6 @@
 #include "content/browser/web_package/signed_exchange_utils.h"
 #include "content/browser/webui/url_data_manager_backend.h"
 #include "content/browser/webui/web_ui_url_loader_factory_internal.h"
-#include "content/common/mime_sniffing_throttle.h"
 #include "content/common/net/record_load_histograms.h"
 #include "content/common/throttling_url_loader.h"
 #include "content/public/browser/browser_context.h"
@@ -84,6 +83,7 @@
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "third_party/blink/public/common/loader/mime_sniffing_throttle.h"
 #include "third_party/blink/public/common/mime_util/mime_util.h"
 
 #if defined(OS_ANDROID)
@@ -550,7 +550,7 @@
       // Intercepted requests need MimeSniffingThrottle to do mime sniffing.
       // Non-intercepted requests usually go through the regular network
       // URLLoader, which does mime sniffing.
-      throttles.push_back(std::make_unique<MimeSniffingThrottle>(
+      throttles.push_back(std::make_unique<blink::MimeSniffingThrottle>(
           base::ThreadTaskRunnerHandle::Get()));
 
       default_loader_used_ = false;
diff --git a/content/browser/net_info_browsertest.cc b/content/browser/net_info_browsertest.cc
index 1674d61..2a898f25 100644
--- a/content/browser/net_info_browsertest.cc
+++ b/content/browser/net_info_browsertest.cc
@@ -12,6 +12,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "build/build_config.h"
 #include "content/browser/net/network_quality_observer_impl.h"
+#include "content/public/browser/render_process_host.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
@@ -285,14 +286,13 @@
   // effective connection type.
   GetNetworkQualityTracker()->ReportEffectiveConnectionTypeForTesting(
       net::EFFECTIVE_CONNECTION_TYPE_2G);
-  base::RunLoop().RunUntilIdle();
   EXPECT_EQ("2g", RunScriptExtractString("getEffectiveType()"));
+
   GetNetworkQualityTracker()->ReportEffectiveConnectionTypeForTesting(
       net::EFFECTIVE_CONNECTION_TYPE_3G);
-  base::RunLoop().RunUntilIdle();
   EXPECT_EQ("3g", RunScriptExtractString("getEffectiveType()"));
+
   FetchHistogramsFromChildProcesses();
-  base::RunLoop().RunUntilIdle();
   EXPECT_GT(GetTotalSampleCount(&histogram_tester, "NQE.RenderThreadNotified"),
             samples);
 }
diff --git a/content/browser/network_service_instance_impl.cc b/content/browser/network_service_instance_impl.cc
index 1ff48ed..d8be6b8 100644
--- a/content/browser/network_service_instance_impl.cc
+++ b/content/browser/network_service_instance_impl.cc
@@ -112,9 +112,9 @@
     completion_event->Signal();
 }
 
-void BindNetworkChangeManagerRequest(
-    network::mojom::NetworkChangeManagerRequest request) {
-  GetNetworkService()->GetNetworkChangeManager(std::move(request));
+void BindNetworkChangeManagerReceiver(
+    mojo::PendingReceiver<network::mojom::NetworkChangeManager> receiver) {
+  GetNetworkService()->GetNetworkChangeManager(std::move(receiver));
 }
 
 base::CallbackList<void()>& GetCrashHandlersList() {
@@ -325,7 +325,7 @@
          !BrowserThread::IsThreadInitialized(BrowserThread::UI));
   if (!g_network_connection_tracker) {
     g_network_connection_tracker = new network::NetworkConnectionTracker(
-        base::BindRepeating(&BindNetworkChangeManagerRequest));
+        base::BindRepeating(&BindNetworkChangeManagerReceiver));
   }
   return g_network_connection_tracker;
 }
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 3b99969..93d8f72 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -4629,9 +4629,9 @@
 
 #if BUILDFLAG(ENABLE_MDNS)
 void RenderProcessHostImpl::CreateMdnsResponder(
-    network::mojom::MdnsResponderRequest request) {
+    mojo::PendingReceiver<network::mojom::MdnsResponder> receiver) {
   GetStoragePartition()->GetNetworkContext()->CreateMdnsResponder(
-      std::move(request));
+      std::move(receiver));
 }
 #endif  // BUILDFLAG(ENABLE_MDNS)
 
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 0702e5e..b92bf6c3 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -681,7 +681,8 @@
           receiver);
 
 #if BUILDFLAG(ENABLE_MDNS)
-  void CreateMdnsResponder(network::mojom::MdnsResponderRequest request);
+  void CreateMdnsResponder(
+      mojo::PendingReceiver<network::mojom::MdnsResponder> receiver);
 #endif  // BUILDFLAG(ENABLE_MDNS)
 
   void NotifyRendererIfLockedToSite();
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index b0b3c75..40a44c2 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -1168,8 +1168,7 @@
   StartWorker(blink::ServiceWorkerStatusCode::kErrorNetwork);
 }
 
-// Flaky failures on multiple OSes: crbug.com/1008386
-IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, DISABLED_ReadResourceFailure) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, ReadResourceFailure) {
   StartServerAndNavigateToSetup();
 
   // Create a registration with an active version.
diff --git a/content/browser/tracing/background_tracing_manager_browsertest.cc b/content/browser/tracing/background_tracing_manager_browsertest.cc
index 60cf0f3a..390d9b1 100644
--- a/content/browser/tracing/background_tracing_manager_browsertest.cc
+++ b/content/browser/tracing/background_tracing_manager_browsertest.cc
@@ -1685,7 +1685,7 @@
   tracing::PrivacyFilteringCheck checker;
   checker.CheckProtoForUnexpectedFields(trace_data);
   EXPECT_GT(checker.stats().track_event, 0u);
-  EXPECT_EQ(checker.stats().process_desc, 0u);
+  EXPECT_GT(checker.stats().process_desc, 0u);
   EXPECT_GT(checker.stats().thread_desc, 0u);
   EXPECT_TRUE(checker.stats().has_interned_names);
   EXPECT_TRUE(checker.stats().has_interned_categories);
diff --git a/content/browser/tracing/startup_tracing_browsertest.cc b/content/browser/tracing/startup_tracing_browsertest.cc
index 38861a63..75fead8 100644
--- a/content/browser/tracing/startup_tracing_browsertest.cc
+++ b/content/browser/tracing/startup_tracing_browsertest.cc
@@ -200,7 +200,7 @@
   tracing::PrivacyFilteringCheck checker;
   checker.CheckProtoForUnexpectedFields(trace);
   EXPECT_GT(checker.stats().track_event, 0u);
-  EXPECT_EQ(checker.stats().process_desc, 0u);
+  EXPECT_GT(checker.stats().process_desc, 0u);
   EXPECT_GT(checker.stats().thread_desc, 0u);
   EXPECT_TRUE(checker.stats().has_interned_names);
   EXPECT_TRUE(checker.stats().has_interned_categories);
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 156b2e14..c0d8053 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -2203,6 +2203,14 @@
   // happens after RenderFrameHostManager::Init.
   NotifySwappedFromRenderManager(
       nullptr, GetRenderManager()->current_frame_host(), true);
+
+  // For WebContents that are never shown, do critical initialization here which
+  // would normally only happen when the WebContents is shown.
+  if (params.is_never_visible) {
+    // This has just been created so there can only be one frame. Thus it is
+    // safe to initialize the root.
+    GetMainFrame()->Init();
+  }
 }
 
 void WebContentsImpl::OnWebContentsDestroyed(WebContentsImpl* web_contents) {
@@ -2838,8 +2846,6 @@
     RenderFrameHostImpl* rfh =
         RenderFrameHostImpl::FromID(render_process_id, main_frame_route_id);
     if (rfh) {
-      DCHECK(rfh->IsRenderFrameLive());
-      rfh->Init();
       // TODO(crbug.com/545684): It's super surprising that
       // ShouldCreateWebContents() is actually a way to allow
       // BackgroundWebContents to intercede and provide a completely different
@@ -4819,7 +4825,7 @@
 
 void WebContentsImpl::DOMContentLoaded(RenderFrameHost* render_frame_host) {
   for (auto& observer : observers_)
-    observer.DocumentLoadedInFrame(render_frame_host);
+    observer.DOMContentLoaded(render_frame_host);
 }
 
 void WebContentsImpl::OnDidFinishLoad(RenderFrameHostImpl* source,
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc
index 70aaa76..6262fef0 100644
--- a/content/child/blink_platform_impl.cc
+++ b/content/child/blink_platform_impl.cc
@@ -134,16 +134,6 @@
       return IDS_MEDIA_TRACKS_NO_LABEL;
     case WebLocalizedString::kTextTracksOff:
       return IDS_MEDIA_TRACKS_OFF;
-    case WebLocalizedString::kUnitsKibibytes:
-      return IDS_UNITS_KIBIBYTES;
-    case WebLocalizedString::kUnitsMebibytes:
-      return IDS_UNITS_MEBIBYTES;
-    case WebLocalizedString::kUnitsGibibytes:
-      return IDS_UNITS_GIBIBYTES;
-    case WebLocalizedString::kUnitsTebibytes:
-      return IDS_UNITS_TEBIBYTES;
-    case WebLocalizedString::kUnitsPebibytes:
-      return IDS_UNITS_PEBIBYTES;
     // There is no matched IDS_FOO for kBlockedPluginText. Return -1.
     case WebLocalizedString::kBlockedPluginText:
       return -1;
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 2d73082..0a5b5c5 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -180,10 +180,6 @@
     "media/cdm_info.cc",
     "media/media_player_delegate_messages.h",
     "media/peer_connection_tracker_messages.h",
-    "mime_sniffing_throttle.cc",
-    "mime_sniffing_throttle.h",
-    "mime_sniffing_url_loader.cc",
-    "mime_sniffing_url_loader.h",
     "navigation_gesture.h",
     "navigation_params.cc",
     "navigation_params.h",
diff --git a/content/public/app/content_browser_manifest.cc b/content/public/app/content_browser_manifest.cc
index 85eeaa89..4b6d406 100644
--- a/content/public/app/content_browser_manifest.cc
+++ b/content/public/app/content_browser_manifest.cc
@@ -213,7 +213,6 @@
               std::set<const char*>{
                   "autofill.mojom.AutofillDriver",
                   "autofill.mojom.PasswordManagerDriver",
-                  "blink.mojom.AnchorElementMetricsHost",
                   "blink.mojom.CacheStorage",
                   "blink.mojom.ColorChooserFactory",
                   "blink.mojom.DateTimeChooser",
@@ -236,7 +235,6 @@
                   "blink.mojom.SpeechRecognizer",
                   "blink.mojom.TextSuggestionHost",
                   "blink.mojom.UnhandledTapNotifier",
-                  "blink.mojom.WakeLockService",
                   "blink.mojom.WebUsbService",
                   "content.mojom.BrowserTarget",
                   "content.mojom.InputInjector",
diff --git a/content/public/browser/web_contents.cc b/content/public/browser/web_contents.cc
index df508da..2e133af 100644
--- a/content/public/browser/web_contents.cc
+++ b/content/public/browser/web_contents.cc
@@ -30,7 +30,8 @@
       context(nullptr),
       renderer_initiated_creation(false),
       desired_renderer_state(kOkayToHaveRendererProcess),
-      starting_sandbox_flags(blink::WebSandboxFlags::kNone) {}
+      starting_sandbox_flags(blink::WebSandboxFlags::kNone),
+      is_never_visible(false) {}
 
 WebContents::CreateParams::CreateParams(const CreateParams& other) = default;
 
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index c8c34a9..dd1113e 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -222,6 +222,12 @@
     // default initialized then the value is not passed on to the WebContents
     // and GetLastActiveTime() will return the WebContents' creation time.
     base::TimeTicks last_active_time;
+
+    // Normal WebContents initialization is split between construction and the
+    // first time it is shown. Some WebContents are never shown though.
+    // Setting this to true will invoke the WebContents delayed initialization
+    // that doesn't require visibility.
+    bool is_never_visible;
   };
 
   // Creates a new WebContents.
diff --git a/content/public/browser/web_contents_observer.h b/content/public/browser/web_contents_observer.h
index e7d24013..cdeadfef 100644
--- a/content/public/browser/web_contents_observer.h
+++ b/content/public/browser/web_contents_observer.h
@@ -234,8 +234,7 @@
   // This method is invoked when the document in the given frame finished
   // loading. At this point, scripts marked as defer were executed, and
   // content scripts marked "document_end" get injected into the frame.
-  // TODO(kouhei): rename this to DOMContentLoaded().
-  virtual void DocumentLoadedInFrame(RenderFrameHost* render_frame_host) {}
+  virtual void DOMContentLoaded(RenderFrameHost* render_frame_host) {}
 
   // This method is invoked when the load is done, i.e. the spinner of the tab
   // will stop spinning, and the onload event was dispatched.
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 9f6db34..168f42b9 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -1874,6 +1874,19 @@
 }
 
 void FetchHistogramsFromChildProcesses() {
+  // Wait for all the renderer processes to be initialized before fetching
+  // histograms for the first time.
+  for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
+       !it.IsAtEnd(); it.Advance()) {
+    if (!it.GetCurrentValue()->IsReady()) {
+      RenderProcessHost* render_process_host = it.GetCurrentValue();
+      RenderProcessHostWatcher ready_watcher(
+          render_process_host,
+          RenderProcessHostWatcher::WATCH_FOR_PROCESS_READY);
+      ready_watcher.Wait();
+    }
+  }
+
   base::RunLoop run_loop;
 
   FetchHistogramsAsynchronously(
@@ -2335,6 +2348,11 @@
   run_loop_.Run();
 }
 
+void RenderProcessHostWatcher::RenderProcessReady(RenderProcessHost* host) {
+  if (type_ == WATCH_FOR_PROCESS_READY)
+    std::move(quit_closure_).Run();
+}
+
 void RenderProcessHostWatcher::RenderProcessExited(
     RenderProcessHost* host,
     const ChildProcessTerminationInfo& info) {
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index c6f09ccb..c4ad33ac 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -1030,10 +1030,11 @@
   DISALLOW_COPY_AND_ASSIGN(TitleWatcher);
 };
 
-// Watches a RenderProcessHost and waits for specified destruction events.
+// Watches a RenderProcessHost and waits for a specified lifecycle event.
 class RenderProcessHostWatcher : public RenderProcessHostObserver {
  public:
   enum WatchType {
+    WATCH_FOR_PROCESS_READY,
     WATCH_FOR_PROCESS_EXIT,
     WATCH_FOR_HOST_DESTRUCTION
   };
@@ -1044,7 +1045,7 @@
   RenderProcessHostWatcher(WebContents* web_contents, WatchType type);
   ~RenderProcessHostWatcher() override;
 
-  // Waits until the renderer process exits.
+  // Waits until the expected event is triggered.
   void Wait();
 
   // Returns true if a renderer process exited cleanly (without hitting
@@ -1054,6 +1055,7 @@
 
  private:
   // Overridden RenderProcessHost::LifecycleObserver methods.
+  void RenderProcessReady(RenderProcessHost* host) override;
   void RenderProcessExited(RenderProcessHost* host,
                            const ChildProcessTerminationInfo& info) override;
   void RenderProcessHostDestroyed(RenderProcessHost* host) override;
diff --git a/content/public/test/network_connection_change_simulator.cc b/content/public/test/network_connection_change_simulator.cc
index b52de8d2..aa9cef9 100644
--- a/content/public/test/network_connection_change_simulator.cc
+++ b/content/public/test/network_connection_change_simulator.cc
@@ -12,6 +12,7 @@
 #include "content/public/browser/system_connector.h"
 #include "content/public/common/network_service_util.h"
 #include "content/public/common/service_names.mojom.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/network_change_notifier.h"
 #include "services/network/public/mojom/network_service_test.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
@@ -48,11 +49,10 @@
   // If the network service is enabled, set the connection type for its
   // NetworkChangeNotifier instance as well.
   if (IsOutOfProcessNetworkService()) {
-    network::mojom::NetworkChangeManagerPtr manager_ptr;
-    network::mojom::NetworkChangeManagerRequest request(
-        mojo::MakeRequest(&manager_ptr));
-    GetNetworkService()->GetNetworkChangeManager(std::move(request));
-    manager_ptr->OnNetworkChanged(
+    mojo::Remote<network::mojom::NetworkChangeManager> manager;
+    GetNetworkService()->GetNetworkChangeManager(
+        manager.BindNewPipeAndPassReceiver());
+    manager->OnNetworkChanged(
         /*dns_changed=*/false, /*ip_address_changed=*/false,
         /*connection_type_changed=*/true,
         network::mojom::ConnectionType::CONNECTION_ETHERNET,
diff --git a/content/renderer/loader/resource_dispatcher.cc b/content/renderer/loader/resource_dispatcher.cc
index 470cc1d..ca130dd 100644
--- a/content/renderer/loader/resource_dispatcher.cc
+++ b/content/renderer/loader/resource_dispatcher.cc
@@ -21,7 +21,6 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "content/common/inter_process_time_ticks_converter.h"
-#include "content/common/mime_sniffing_throttle.h"
 #include "content/common/navigation_params.h"
 #include "content/common/throttling_url_loader.h"
 #include "content/public/common/navigation_policy.h"
@@ -45,6 +44,7 @@
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/resource_response.h"
 #include "services/network/public/cpp/url_loader_completion_status.h"
+#include "third_party/blink/public/common/loader/mime_sniffing_throttle.h"
 
 namespace content {
 
@@ -553,7 +553,7 @@
     // MIME sniffing should be disabled for a request initiated by fetch().
     options |= network::mojom::kURLLoadOptionSniffMimeType;
     throttles.push_back(
-        std::make_unique<MimeSniffingThrottle>(loading_task_runner));
+        std::make_unique<blink::MimeSniffingThrottle>(loading_task_runner));
   }
   if (is_sync) {
     options |= network::mojom::kURLLoadOptionSynchronous;
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 9567958b..41cea1a 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1894,7 +1894,6 @@
     "../common/input/touch_event_stream_validator_unittest.cc",
     "../common/inter_process_time_ticks_converter_unittest.cc",
     "../common/mac/attributed_string_coder_unittest.mm",
-    "../common/mime_sniffing_throttle_unittest.cc",
     "../common/origin_util_unittest.cc",
     "../common/page_state_serialization_unittest.cc",
     "../common/service_manager/service_manager_connection_impl_unittest.cc",
diff --git a/content/test/web_contents_observer_sanity_checker.cc b/content/test/web_contents_observer_sanity_checker.cc
index 13118c8a..a885efd 100644
--- a/content/test/web_contents_observer_sanity_checker.cc
+++ b/content/test/web_contents_observer_sanity_checker.cc
@@ -247,7 +247,7 @@
   AssertMainFrameExists();
 }
 
-void WebContentsObserverSanityChecker::DocumentLoadedInFrame(
+void WebContentsObserverSanityChecker::DOMContentLoaded(
     RenderFrameHost* render_frame_host) {
   AssertRenderFrameExists(render_frame_host);
 }
diff --git a/content/test/web_contents_observer_sanity_checker.h b/content/test/web_contents_observer_sanity_checker.h
index 59983a4..cacbecc 100644
--- a/content/test/web_contents_observer_sanity_checker.h
+++ b/content/test/web_contents_observer_sanity_checker.h
@@ -53,7 +53,7 @@
   void DidFinishNavigation(NavigationHandle* navigation_handle) override;
   void DocumentAvailableInMainFrame() override;
   void DocumentOnLoadCompletedInMainFrame() override;
-  void DocumentLoadedInFrame(RenderFrameHost* render_frame_host) override;
+  void DOMContentLoaded(RenderFrameHost* render_frame_host) override;
   void DidFinishLoad(RenderFrameHost* render_frame_host,
                      const GURL& validated_url) override;
   void DidFailLoad(RenderFrameHost* render_frame_host,
diff --git a/docs/infra/cq_builders.md b/docs/infra/cq_builders.md
index 347f4449..50415246 100644
--- a/docs/infra/cq_builders.md
+++ b/docs/infra/cq_builders.md
@@ -310,6 +310,10 @@
 by CQ. These are often used to test new configurations before they are added
 as required builders.
 
+* [android-marshmallow-arm64-coverage-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android-marshmallow-arm64-coverage-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android-marshmallow-arm64-coverage-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android-marshmallow-arm64-coverage-rel))
+
+  * Experimental percentage: 3
+
 * [android-pie-arm64-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android-pie-arm64-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android-pie-arm64-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android-pie-arm64-rel))
 
   * Experimental percentage: 10
diff --git a/docs/testing/testing_in_chromium.md b/docs/testing/testing_in_chromium.md
index eda1efc8..bfbae561 100644
--- a/docs/testing/testing_in_chromium.md
+++ b/docs/testing/testing_in_chromium.md
@@ -57,7 +57,6 @@
 Only subset of browser tests are enabled on Android:
 *   components_browsertests
 *   content_browsertests
-*   nonperfetto_content_browsertests
 
 Other browser tests are not supported on Android yet. [crbug/611756]
 tracks the effort to enable them on Android.
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index dd39f133..8c42f20 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1447,6 +1447,7 @@
   AUTOFILLPRIVATE_SETCREDITCARDFIDOAUTHENABLEDSTATE = 1384,
   USERSPRIVATE_ISWHITELISTEDUSER = 1385,
   PRINTINGMETRICS_GETPRINTJOBS = 1386,
+  AUTOTESTPRIVATE_WAITFORASSISTANTQUERYSTATUS = 1387,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn
index d379352..ac90db3 100644
--- a/gpu/command_buffer/service/BUILD.gn
+++ b/gpu/command_buffer/service/BUILD.gn
@@ -252,6 +252,7 @@
     "transform_feedback_manager.h",
     "validating_abstract_texture_impl.cc",
     "validating_abstract_texture_impl.h",
+    "value_validator.h",
     "vertex_array_manager.cc",
     "vertex_array_manager.h",
     "vertex_attrib_manager.cc",
diff --git a/gpu/command_buffer/service/gles2_cmd_validation.h b/gpu/command_buffer/service/gles2_cmd_validation.h
index 0368e63d..81a2986b8 100644
--- a/gpu/command_buffer/service/gles2_cmd_validation.h
+++ b/gpu/command_buffer/service/gles2_cmd_validation.h
@@ -7,59 +7,12 @@
 #ifndef GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_VALIDATION_H_
 #define GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_VALIDATION_H_
 
-#include <algorithm>
-#include <vector>
 #include "gpu/command_buffer/common/gles2_cmd_format.h"
+#include "gpu/command_buffer/service/value_validator.h"
 
 namespace gpu {
 namespace gles2 {
 
-// ValueValidator returns true if a value is valid.
-template <typename T>
-class ValueValidator {
- public:
-  ValueValidator() = default;
-
-  ValueValidator(const T* valid_values, int num_values) {
-    AddValues(valid_values, num_values);
-  }
-
-  void AddValue(const T value) {
-    if (!IsValid(value)) {
-      valid_values_.push_back(value);
-    }
-  }
-
-  void AddValues(const T* valid_values, int num_values) {
-    for (int ii = 0; ii < num_values; ++ii) {
-      AddValue(valid_values[ii]);
-    }
-  }
-
-  void RemoveValues(const T* invalid_values, int num_values) {
-    for (int ii = 0; ii < num_values; ++ii) {
-      auto iter = std::find(
-          valid_values_.begin(), valid_values_.end(), invalid_values[ii]);
-      if (iter != valid_values_.end()) {
-        valid_values_.erase(iter);
-        DCHECK(!IsValid(invalid_values[ii]));
-      }
-    }
-  }
-
-  bool IsValid(const T value) const {
-    return std::find(valid_values_.begin(), valid_values_.end(), value) !=
-           valid_values_.end();
-  }
-
-  const std::vector<T>& GetValues() const {
-    return valid_values_;
-  }
-
- private:
-  std::vector<T> valid_values_;
-};
-
 struct Validators {
   Validators();
 
diff --git a/gpu/command_buffer/service/raster_cmd_validation.h b/gpu/command_buffer/service/raster_cmd_validation.h
index 10e6552..f1eb739 100644
--- a/gpu/command_buffer/service/raster_cmd_validation.h
+++ b/gpu/command_buffer/service/raster_cmd_validation.h
@@ -7,57 +7,12 @@
 #ifndef GPU_COMMAND_BUFFER_SERVICE_RASTER_CMD_VALIDATION_H_
 #define GPU_COMMAND_BUFFER_SERVICE_RASTER_CMD_VALIDATION_H_
 
-#include <algorithm>
-#include <vector>
 #include "gpu/command_buffer/common/raster_cmd_format.h"
+#include "gpu/command_buffer/service/value_validator.h"
 
 namespace gpu {
 namespace raster {
 
-// ValueValidator returns true if a value is valid.
-template <typename T>
-class ValueValidator {
- public:
-  ValueValidator() = default;
-
-  ValueValidator(const T* valid_values, int num_values) {
-    AddValues(valid_values, num_values);
-  }
-
-  void AddValue(const T value) {
-    if (!IsValid(value)) {
-      valid_values_.push_back(value);
-    }
-  }
-
-  void AddValues(const T* valid_values, int num_values) {
-    for (int ii = 0; ii < num_values; ++ii) {
-      AddValue(valid_values[ii]);
-    }
-  }
-
-  void RemoveValues(const T* invalid_values, int num_values) {
-    for (int ii = 0; ii < num_values; ++ii) {
-      auto iter = std::find(valid_values_.begin(), valid_values_.end(),
-                            invalid_values[ii]);
-      if (iter != valid_values_.end()) {
-        valid_values_.erase(iter);
-        DCHECK(!IsValid(invalid_values[ii]));
-      }
-    }
-  }
-
-  bool IsValid(const T value) const {
-    return std::find(valid_values_.begin(), valid_values_.end(), value) !=
-           valid_values_.end();
-  }
-
-  const std::vector<T>& GetValues() const { return valid_values_; }
-
- private:
-  std::vector<T> valid_values_;
-};
-
 struct Validators {
   Validators();
 
diff --git a/gpu/command_buffer/service/value_validator.h b/gpu/command_buffer/service/value_validator.h
new file mode 100644
index 0000000..dd16689
--- /dev/null
+++ b/gpu/command_buffer/service/value_validator.h
@@ -0,0 +1,61 @@
+// Copyright 2019 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.
+
+// Contains the definition of ValueValidator for the uses in *_cmd_validation.h
+
+#ifndef GPU_COMMAND_BUFFER_SERVICE_VALUE_VALIDATOR_H_
+#define GPU_COMMAND_BUFFER_SERVICE_VALUE_VALIDATOR_H_
+
+#include <algorithm>
+#include <vector>
+
+namespace gpu {
+
+// ValueValidator returns true if a value is valid.
+template <typename T>
+class ValueValidator {
+ public:
+  ValueValidator() = default;
+
+  ValueValidator(const T* valid_values, int num_values) {
+    AddValues(valid_values, num_values);
+  }
+
+  void AddValue(const T value) {
+    if (!IsValid(value)) {
+      valid_values_.push_back(value);
+    }
+  }
+
+  void AddValues(const T* valid_values, int num_values) {
+    for (int ii = 0; ii < num_values; ++ii) {
+      AddValue(valid_values[ii]);
+    }
+  }
+
+  void RemoveValues(const T* invalid_values, int num_values) {
+    for (int ii = 0; ii < num_values; ++ii) {
+      auto iter = std::find(valid_values_.begin(), valid_values_.end(),
+                            invalid_values[ii]);
+      if (iter != valid_values_.end()) {
+        valid_values_.erase(iter);
+        DCHECK(!IsValid(invalid_values[ii]));
+      }
+    }
+  }
+
+  bool IsValid(const T value) const {
+    return std::find(valid_values_.begin(), valid_values_.end(), value) !=
+           valid_values_.end();
+  }
+
+  const std::vector<T>& GetValues() const { return valid_values_; }
+
+ private:
+  std::vector<T> valid_values_;
+};
+
+}  // namespace gpu
+
+#endif  // GPU_COMMAND_BUFFER_SERVICE_VALUE_VALIDATOR_H_
diff --git a/gpu/command_buffer/service/webgpu_cmd_validation.h b/gpu/command_buffer/service/webgpu_cmd_validation.h
index fc0c337..5b43276 100644
--- a/gpu/command_buffer/service/webgpu_cmd_validation.h
+++ b/gpu/command_buffer/service/webgpu_cmd_validation.h
@@ -7,60 +7,14 @@
 #ifndef GPU_COMMAND_BUFFER_SERVICE_WEBGPU_CMD_VALIDATION_H_
 #define GPU_COMMAND_BUFFER_SERVICE_WEBGPU_CMD_VALIDATION_H_
 
-#include <algorithm>
-#include <vector>
 #include "base/stl_util.h"
 #include "gpu/command_buffer/common/webgpu_cmd_enums.h"
 #include "gpu/command_buffer/common/webgpu_cmd_format.h"
-#include "gpu/command_buffer/service/gles2_cmd_validation.h"
+#include "gpu/command_buffer/service/value_validator.h"
 
 namespace gpu {
 namespace webgpu {
 
-// ValueValidator returns true if a value is valid.
-template <typename T>
-class ValueValidator {
- public:
-  ValueValidator() = default;
-
-  ValueValidator(const T* valid_values, int num_values) {
-    AddValues(valid_values, num_values);
-  }
-
-  void AddValue(const T value) {
-    if (!IsValid(value)) {
-      valid_values_.push_back(value);
-    }
-  }
-
-  void AddValues(const T* valid_values, int num_values) {
-    for (int ii = 0; ii < num_values; ++ii) {
-      AddValue(valid_values[ii]);
-    }
-  }
-
-  void RemoveValues(const T* invalid_values, int num_values) {
-    for (int ii = 0; ii < num_values; ++ii) {
-      auto iter = std::find(valid_values_.begin(), valid_values_.end(),
-                            invalid_values[ii]);
-      if (iter != valid_values_.end()) {
-        valid_values_.erase(iter);
-        DCHECK(!IsValid(invalid_values[ii]));
-      }
-    }
-  }
-
-  bool IsValid(const T value) const {
-    return std::find(valid_values_.begin(), valid_values_.end(), value) !=
-           valid_values_.end();
-  }
-
-  const std::vector<T>& GetValues() const { return valid_values_; }
-
- private:
-  std::vector<T> valid_values_;
-};
-
 struct Validators {
   Validators();
 
diff --git a/infra/config/commit-queue.cfg b/infra/config/commit-queue.cfg
index 7636277..5f8813fb 100644
--- a/infra/config/commit-queue.cfg
+++ b/infra/config/commit-queue.cfg
@@ -362,6 +362,10 @@
       ##########################
 
       builders {
+        name: "chromium/try/android-marshmallow-arm64-coverage-rel"
+        experiment_percentage: 3
+      }
+      builders {
         name: "chromium/try/android-pie-arm64-rel"
         experiment_percentage: 10
       }
diff --git a/ios/chrome/browser/application_context_impl.cc b/ios/chrome/browser/application_context_impl.cc
index 1893e7d..a2557ba 100644
--- a/ios/chrome/browser/application_context_impl.cc
+++ b/ios/chrome/browser/application_context_impl.cc
@@ -53,6 +53,7 @@
 #include "ios/chrome/common/channel_info.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #include "ios/web/public/thread/web_thread.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "net/log/net_log.h"
 #include "net/log/net_log_capture_mode.h"
 #include "net/socket/client_socket_pool_manager.h"
@@ -83,11 +84,11 @@
                                 app_context, std::move(request)));
 }
 
-// Passed to NetworkConnectionTracker to bind a NetworkChangeManagerRequest.
-void BindNetworkChangeManagerRequest(
+// Passed to NetworkConnectionTracker to bind a NetworkChangeManager receiver.
+void BindNetworkChangeManagerReceiver(
     network::NetworkChangeManager* network_change_manager,
-    network::mojom::NetworkChangeManagerRequest request) {
-  network_change_manager->AddRequest(std::move(request));
+    mojo::PendingReceiver<network::mojom::NetworkChangeManager> receiver) {
+  network_change_manager->AddReceiver(std::move(receiver));
 }
 
 }  // namespace
@@ -353,7 +354,7 @@
     }
     network_connection_tracker_ =
         std::make_unique<network::NetworkConnectionTracker>(base::BindRepeating(
-            &BindNetworkChangeManagerRequest,
+            &BindNetworkChangeManagerReceiver,
             base::Unretained(network_change_manager_.get())));
   }
   return network_connection_tracker_.get();
diff --git a/ios/chrome/browser/net/cookie_util.mm b/ios/chrome/browser/net/cookie_util.mm
index cd69151..db5ca82 100644
--- a/ios/chrome/browser/net/cookie_util.mm
+++ b/ios/chrome/browser/net/cookie_util.mm
@@ -15,7 +15,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/task/post_task.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/net/cookies/cookie_store_ios_persistent.h"
+#import "ios/net/cookies/cookie_store_ios.h"
 #import "ios/net/cookies/system_cookie_store.h"
 #include "ios/web/common/features.h"
 #include "ios/web/public/thread/web_task_traits.h"
diff --git a/ios/net/BUILD.gn b/ios/net/BUILD.gn
index 1461b07..d0b0739 100644
--- a/ios/net/BUILD.gn
+++ b/ios/net/BUILD.gn
@@ -39,8 +39,6 @@
     "cookies/cookie_store_ios.mm",
     "cookies/cookie_store_ios_client.h",
     "cookies/cookie_store_ios_client.mm",
-    "cookies/cookie_store_ios_persistent.h",
-    "cookies/cookie_store_ios_persistent.mm",
     "cookies/ns_http_system_cookie_store.h",
     "cookies/ns_http_system_cookie_store.mm",
     "cookies/system_cookie_store.h",
@@ -136,7 +134,6 @@
     "chunked_data_stream_uploader_unittest.cc",
     "cookies/cookie_cache_unittest.cc",
     "cookies/cookie_creation_time_manager_unittest.mm",
-    "cookies/cookie_store_ios_persistent_unittest.mm",
     "cookies/cookie_store_ios_unittest.mm",
     "cookies/ns_http_system_cookie_store_unittest.mm",
     "cookies/system_cookie_util_unittest.mm",
diff --git a/ios/net/cookies/cookie_store_ios.h b/ios/net/cookies/cookie_store_ios.h
index 3bb2dec..93724785 100644
--- a/ios/net/cookies/cookie_store_ios.h
+++ b/ios/net/cookies/cookie_store_ios.h
@@ -52,7 +52,6 @@
 // changes are written directly to the system cookie store, then propagated to
 // the backing store by OnSystemCookiesChanged, which is called by the system
 // store once the change to the system store is written back.
-// For not synchronized CookieStore, please see CookieStoreIOSPersistent.
 class CookieStoreIOS : public net::CookieStore,
                        public CookieNotificationObserver {
  public:
diff --git a/ios/net/cookies/cookie_store_ios_persistent.h b/ios/net/cookies/cookie_store_ios_persistent.h
deleted file mode 100644
index d7d01a1..0000000
--- a/ios/net/cookies/cookie_store_ios_persistent.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_NET_COOKIES_COOKIE_STORE_IOS_PERSISTENT_H_
-#define IOS_NET_COOKIES_COOKIE_STORE_IOS_PERSISTENT_H_
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/time/time.h"
-#import "ios/net/cookies/cookie_store_ios.h"
-#include "net/cookies/cookie_monster.h"
-#include "net/cookies/cookie_store.h"
-#include "url/gurl.h"
-
-namespace net {
-
-class NetLog;
-
-// The CookieStoreIOSPersistent is an implementation of CookieStore relying on
-// on backing CookieStore.
-// CookieStoreIOSPersistent is not thread safe.
-//
-// All the changes are written back to the backing CookieStore.
-// For synchronized CookieStore, please see CookieStoreIOS.
-//
-// TODO(crbug.com/686147): evaluate if inheritance is
-// needed here.
-class CookieStoreIOSPersistent : public CookieStoreIOS {
- public:
-  // Constructs a CookieStoreIOS with a default SystemCookieStore.
-  CookieStoreIOSPersistent(
-      net::CookieMonster::PersistentCookieStore* persistent_store,
-      NetLog* net_log);
-
-  // Constructs a CookieStoreIOS backed by |system_store|.
-  CookieStoreIOSPersistent(
-      net::CookieMonster::PersistentCookieStore* persistent_store,
-      std::unique_ptr<SystemCookieStore> system_store,
-      NetLog* net_log);
-
-  ~CookieStoreIOSPersistent() override;
-
-  // Inherited CookieStore methods.
-  void SetCanonicalCookieAsync(std::unique_ptr<CanonicalCookie> cookie,
-                               std::string source_scheme,
-                               const net::CookieOptions& options,
-                               SetCookiesCallback callback) override;
-  void GetCookieListWithOptionsAsync(const GURL& url,
-                                     const net::CookieOptions& options,
-                                     GetCookieListCallback callback) override;
-  void GetAllCookiesAsync(GetAllCookiesCallback callback) override;
-  void DeleteCanonicalCookieAsync(const CanonicalCookie& cookie,
-                                  DeleteCallback callback) override;
-  void DeleteAllCreatedInTimeRangeAsync(
-      const net::CookieDeletionInfo::TimeRange& creation_range,
-      DeleteCallback callback) override;
-  void DeleteAllMatchingInfoAsync(CookieDeletionInfo delete_info,
-                                  DeleteCallback callback) override;
-  void DeleteSessionCookiesAsync(DeleteCallback callback) override;
-
- private:
-  // No-op functions for this class.
-  void WriteToCookieMonster(NSArray* system_cookies) override;
-  void OnSystemCookiesChanged() override;
-
-  DISALLOW_COPY_AND_ASSIGN(CookieStoreIOSPersistent);
-};
-
-}  // namespace net
-
-#endif  // IOS_NET_COOKIES_COOKIE_STORE_IOS_PERSISTENT_H_
diff --git a/ios/net/cookies/cookie_store_ios_persistent.mm b/ios/net/cookies/cookie_store_ios_persistent.mm
deleted file mode 100644
index 78a80840..0000000
--- a/ios/net/cookies/cookie_store_ios_persistent.mm
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright (c) 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/net/cookies/cookie_store_ios_persistent.h"
-
-#import <Foundation/Foundation.h>
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/threading/thread_checker.h"
-#import "ios/net/cookies/ns_http_system_cookie_store.h"
-#import "ios/net/cookies/system_cookie_util.h"
-#include "net/cookies/cookie_monster.h"
-#include "net/log/net_log.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace net {
-
-namespace {
-// Add metrics reporting to GetCookieListWithOptionsAsync cookie monster
-// callback.
-void CookieListCallbackWithMetricsLogging(
-    CookieMonster::GetCookieListCallback callback,
-    const CookieStatusList& cookies,
-    const CookieStatusList& excluded_cookies) {
-  net::ReportGetCookiesForURLResult(SystemCookieStoreType::kCookieMonster,
-                                    !cookies.empty());
-  if (!callback.is_null()) {
-    std::move(callback).Run(cookies, excluded_cookies);
-  }
-}
-}  // namespace
-
-#pragma mark -
-#pragma mark CookieStoreIOSPersistent
-
-CookieStoreIOSPersistent::CookieStoreIOSPersistent(
-    net::CookieMonster::PersistentCookieStore* persistent_store,
-    NetLog* net_log)
-    : CookieStoreIOS(persistent_store,
-                     std::make_unique<net::NSHTTPSystemCookieStore>(),
-                     net_log) {}
-
-CookieStoreIOSPersistent::CookieStoreIOSPersistent(
-    net::CookieMonster::PersistentCookieStore* persistent_store,
-    std::unique_ptr<SystemCookieStore> system_store,
-    NetLog* net_log)
-    : CookieStoreIOS(persistent_store, std::move(system_store), net_log) {}
-
-CookieStoreIOSPersistent::~CookieStoreIOSPersistent() {}
-
-#pragma mark -
-#pragma mark CookieStoreIOSPersistent methods
-
-void CookieStoreIOSPersistent::SetCanonicalCookieAsync(
-    std::unique_ptr<CanonicalCookie> cookie,
-    std::string source_scheme,
-    const net::CookieOptions& options,
-    SetCookiesCallback callback) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  cookie_monster()->SetCanonicalCookieAsync(
-      std::move(cookie), std::move(source_scheme), options,
-      WrapSetCallback(std::move(callback)));
-}
-
-void CookieStoreIOSPersistent::GetCookieListWithOptionsAsync(
-    const GURL& url,
-    const net::CookieOptions& options,
-    GetCookieListCallback callback) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  ReportGetCookiesForURLCall(SystemCookieStoreType::kCookieMonster);
-  cookie_monster()->GetCookieListWithOptionsAsync(
-      url, options,
-      base::BindOnce(&CookieListCallbackWithMetricsLogging,
-                     base::Passed(&callback)));
-}
-
-void CookieStoreIOSPersistent::GetAllCookiesAsync(
-    GetAllCookiesCallback callback) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  cookie_monster()->GetAllCookiesAsync(std::move(callback));
-}
-
-void CookieStoreIOSPersistent::DeleteCanonicalCookieAsync(
-    const CanonicalCookie& cookie,
-    DeleteCallback callback) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  cookie_monster()->DeleteCanonicalCookieAsync(
-      cookie, WrapDeleteCallback(std::move(callback)));
-}
-
-void CookieStoreIOSPersistent::DeleteAllCreatedInTimeRangeAsync(
-    const net::CookieDeletionInfo::TimeRange& creation_range,
-    DeleteCallback callback) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  if (metrics_enabled())
-    ResetCookieCountMetrics();
-
-  cookie_monster()->DeleteAllCreatedInTimeRangeAsync(
-      creation_range, WrapDeleteCallback(std::move(callback)));
-}
-
-void CookieStoreIOSPersistent::DeleteAllMatchingInfoAsync(
-    CookieDeletionInfo delete_info,
-    DeleteCallback callback) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  if (metrics_enabled())
-    ResetCookieCountMetrics();
-
-  cookie_monster()->DeleteAllMatchingInfoAsync(
-      std::move(delete_info), WrapDeleteCallback(std::move(callback)));
-}
-
-void CookieStoreIOSPersistent::DeleteSessionCookiesAsync(
-    DeleteCallback callback) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  if (metrics_enabled())
-    ResetCookieCountMetrics();
-
-  cookie_monster()->DeleteSessionCookiesAsync(
-      WrapDeleteCallback(std::move(callback)));
-}
-
-#pragma mark -
-#pragma mark Private methods
-
-void CookieStoreIOSPersistent::WriteToCookieMonster(NSArray* system_cookies) {}
-
-void CookieStoreIOSPersistent::OnSystemCookiesChanged() {}
-
-}  // namespace net
diff --git a/ios/net/cookies/cookie_store_ios_persistent_unittest.mm b/ios/net/cookies/cookie_store_ios_persistent_unittest.mm
deleted file mode 100644
index e18920a..0000000
--- a/ios/net/cookies/cookie_store_ios_persistent_unittest.mm
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/net/cookies/cookie_store_ios_persistent.h"
-
-#import <Foundation/Foundation.h>
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/memory/ref_counted.h"
-#include "base/run_loop.h"
-#include "base/test/task_environment.h"
-#import "ios/net/cookies/cookie_store_ios_test_util.h"
-#include "net/cookies/canonical_cookie_test_helpers.h"
-#include "net/cookies/cookie_store_change_unittest.h"
-#include "net/cookies/cookie_store_test_callbacks.h"
-#include "net/cookies/cookie_store_unittest.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/platform_test.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace net {
-
-struct PersistentCookieStoreIOSTestTraits {
-  static std::unique_ptr<net::CookieStore> Create() {
-    return std::make_unique<CookieStoreIOSPersistent>(nullptr,
-                                                      nullptr /* net_log */);
-  }
-
-  static void DeliverChangeNotifications() { base::RunLoop().RunUntilIdle(); }
-
-  static const bool is_cookie_monster = false;
-  static const bool supports_http_only = false;
-  static const bool supports_non_dotted_domains = true;
-  static const bool preserves_trailing_dots = true;
-  static const bool filters_schemes = false;
-  static const bool has_path_prefix_bug = false;
-  static const bool forbids_setting_empty_name = false;
-  static const bool supports_global_cookie_tracking = false;
-  // TODO(crbug.com/813931): Fix the bugs uncovered by these tests.
-  static const bool supports_named_cookie_tracking = false;
-  static const bool supports_url_cookie_tracking = false;
-  static const bool supports_multiple_tracking_callbacks = false;
-  static const bool has_exact_change_cause = false;
-  static const bool has_exact_change_ordering = false;
-  static const int creation_time_granularity_in_ms = 0;
-  static const int enforces_prefixes = true;
-  static const bool enforce_strict_secure = false;
-
-  base::test::SingleThreadTaskEnvironment task_environment_;
-};
-
-INSTANTIATE_TYPED_TEST_SUITE_P(PersistentCookieStoreIOS,
-                               CookieStoreTest,
-                               PersistentCookieStoreIOSTestTraits);
-INSTANTIATE_TYPED_TEST_SUITE_P(PersistentCookieStoreIOS,
-                               CookieStoreChangeGlobalTest,
-                               PersistentCookieStoreIOSTestTraits);
-INSTANTIATE_TYPED_TEST_SUITE_P(PersistentCookieStoreIOS,
-                               CookieStoreChangeUrlTest,
-                               PersistentCookieStoreIOSTestTraits);
-INSTANTIATE_TYPED_TEST_SUITE_P(PersistentCookieStoreIOS,
-                               CookieStoreChangeNamedTest,
-                               PersistentCookieStoreIOSTestTraits);
-
-namespace {
-
-// Test fixture to exercise net::CookieStoreIOSPersistent created with
-// TestPersistentCookieStore back-end and not synchronized with
-// SystemCookieStore.
-class CookieStoreIOSPersistentTest : public PlatformTest {
- public:
-  CookieStoreIOSPersistentTest()
-      : kTestCookieURL("http://foo.google.com/bar"),
-        scoped_cookie_store_ios_client_(
-            std::make_unique<TestCookieStoreIOSClient>()),
-        backend_(new net::TestPersistentCookieStore),
-        store_(std::make_unique<net::CookieStoreIOSPersistent>(
-            backend_.get(),
-            nullptr /* net_log */)) {
-    cookie_change_subscription_ =
-        store_->GetChangeDispatcher().AddCallbackForCookie(
-            kTestCookieURL, "abc",
-            base::BindRepeating(&net::RecordCookieChanges, &cookies_changed_,
-                                &cookies_removed_));
-  }
-
-  ~CookieStoreIOSPersistentTest() override {}
-
-  // Gets the cookies. |callback| will be called on completion.
-  void GetCookies(net::CookieStore::GetCookieListCallback callback) {
-    net::CookieOptions options;
-    options.set_include_httponly();
-    store_->GetCookieListWithOptionsAsync(kTestCookieURL, options,
-                                          std::move(callback));
-  }
-
-  // Sets a cookie.
-  void SetCookie(const std::string& cookie_line) {
-    net::SetCookie(cookie_line, kTestCookieURL, store_.get());
-  }
-
- private:
-  const GURL kTestCookieURL;
-
- protected:
-  base::test::SingleThreadTaskEnvironment task_environment_;
-  ScopedTestingCookieStoreIOSClient scoped_cookie_store_ios_client_;
-  scoped_refptr<net::TestPersistentCookieStore> backend_;
-  std::unique_ptr<net::CookieStoreIOS> store_;
-  std::unique_ptr<net::CookieChangeSubscription> cookie_change_subscription_;
-  std::vector<net::CanonicalCookie> cookies_changed_;
-  std::vector<bool> cookies_removed_;
-};
-
-}  // namespace
-
-TEST_F(CookieStoreIOSPersistentTest, SetCookieCallsHook) {
-  ClearCookies();
-  SetCookie("abc=def");
-  EXPECT_EQ(0U, cookies_changed_.size());
-  EXPECT_EQ(0U, cookies_removed_.size());
-  backend_->RunLoadedCallback();
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1U, cookies_changed_.size());
-  EXPECT_EQ(1U, cookies_removed_.size());
-  EXPECT_EQ("abc", cookies_changed_[0].Name());
-  EXPECT_EQ("def", cookies_changed_[0].Value());
-  EXPECT_FALSE(cookies_removed_[0]);
-
-  // Replacing an existing cookie is actually a two-phase delete + set
-  // operation, so we get an extra notification.
-  SetCookie("abc=ghi");
-  EXPECT_EQ(3U, cookies_changed_.size());
-  EXPECT_EQ(3U, cookies_removed_.size());
-  EXPECT_EQ("abc", cookies_changed_[1].Name());
-  EXPECT_EQ("def", cookies_changed_[1].Value());
-  EXPECT_TRUE(cookies_removed_[1]);
-  EXPECT_EQ("abc", cookies_changed_[2].Name());
-  EXPECT_EQ("ghi", cookies_changed_[2].Value());
-  EXPECT_FALSE(cookies_removed_[2]);
-}
-
-// Tests that cookies can be read before the backend is loaded.
-TEST_F(CookieStoreIOSPersistentTest, NotSynchronized) {
-  // Start fetching the cookie.
-  GetCookieListCallback callback;
-  GetCookies(
-      base::BindOnce(&GetCookieListCallback::Run, base::Unretained(&callback)));
-  // Backend loading completes.
-  backend_->RunLoadedCallback();
-  EXPECT_THAT(callback.cookies(), MatchesCookieLine("a=b"));
-}
-
-}  // namespace net
diff --git a/ios/web_view/internal/app/application_context.mm b/ios/web_view/internal/app/application_context.mm
index 7d63790..cd8437d 100644
--- a/ios/web_view/internal/app/application_context.mm
+++ b/ios/web_view/internal/app/application_context.mm
@@ -20,6 +20,7 @@
 #include "ios/web_view/cwv_web_view_buildflags.h"
 #include "ios/web_view/internal/app/web_view_io_thread.h"
 #import "ios/web_view/internal/cwv_flags_internal.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "net/log/net_log.h"
 #include "net/socket/client_socket_pool_manager.h"
 #include "services/network/network_change_manager.h"
@@ -39,11 +40,11 @@
 namespace ios_web_view {
 namespace {
 
-// Passed to NetworkConnectionTracker to bind a NetworkChangeManagerRequest.
-void BindNetworkChangeManagerRequest(
+// Passed to NetworkConnectionTracker to bind a NetworkChangeManager receiver.
+void BindNetworkChangeManagerReceiver(
     network::NetworkChangeManager* network_change_manager,
-    network::mojom::NetworkChangeManagerRequest request) {
-  network_change_manager->AddRequest(std::move(request));
+    mojo::PendingReceiver<network::mojom::NetworkChangeManager> receiver) {
+  network_change_manager->AddReceiver(std::move(receiver));
 }
 
 }  // namespace
@@ -173,7 +174,7 @@
     }
     network_connection_tracker_ =
         std::make_unique<network::NetworkConnectionTracker>(base::BindRepeating(
-            &BindNetworkChangeManagerRequest,
+            &BindNetworkChangeManagerReceiver,
             base::Unretained(network_change_manager_.get())));
   }
   return network_connection_tracker_.get();
diff --git a/ipc/ipc_channel_mojo_unittest.cc b/ipc/ipc_channel_mojo_unittest.cc
index 5ba72fb..e085438a 100644
--- a/ipc/ipc_channel_mojo_unittest.cc
+++ b/ipc/ipc_channel_mojo_unittest.cc
@@ -994,8 +994,7 @@
       public IPC::mojom::IndirectTestDriver,
       public IPC::mojom::PingReceiver {
  public:
-  ListenerWithIndirectProxyAssociatedInterface()
-      : driver_binding_(this), ping_receiver_binding_(this) {}
+  ListenerWithIndirectProxyAssociatedInterface() : driver_binding_(this) {}
   ~ListenerWithIndirectProxyAssociatedInterface() override = default;
 
   // IPC::Listener:
@@ -1016,9 +1015,9 @@
 
  private:
   // IPC::mojom::IndirectTestDriver:
-  void GetPingReceiver(
-      IPC::mojom::PingReceiverAssociatedRequest request) override {
-    ping_receiver_binding_.Bind(std::move(request));
+  void GetPingReceiver(mojo::PendingAssociatedReceiver<IPC::mojom::PingReceiver>
+                           receiver) override {
+    ping_receiver_receiver_.Bind(std::move(receiver));
   }
 
   // IPC::mojom::PingReceiver:
@@ -1028,7 +1027,8 @@
   }
 
   mojo::AssociatedBinding<IPC::mojom::IndirectTestDriver> driver_binding_;
-  mojo::AssociatedBinding<IPC::mojom::PingReceiver> ping_receiver_binding_;
+  mojo::AssociatedReceiver<IPC::mojom::PingReceiver> ping_receiver_receiver_{
+      this};
 
   base::RepeatingClosure ping_handler_;
 };
@@ -1066,9 +1066,9 @@
   // endpoint may not have been bound yet by the time the message is initially
   // processed on the IO thread.
   IPC::mojom::IndirectTestDriverAssociatedPtr driver;
-  IPC::mojom::PingReceiverAssociatedPtr ping_receiver;
+  mojo::AssociatedRemote<IPC::mojom::PingReceiver> ping_receiver;
   proxy()->GetRemoteAssociatedInterface(&driver);
-  driver->GetPingReceiver(mojo::MakeRequest(&ping_receiver));
+  driver->GetPingReceiver(ping_receiver.BindNewEndpointAndPassReceiver());
 
   base::RunLoop loop;
   ping_receiver->Ping(loop.QuitClosure());
@@ -1273,11 +1273,12 @@
     DCHECK_EQ(interface_name, IPC::mojom::SimpleTestClient::Name_);
 
     receiver_.Bind(
-        IPC::mojom::SimpleTestClientAssociatedRequest(std::move(handle)));
+        mojo::PendingAssociatedReceiver<IPC::mojom::SimpleTestClient>(
+            std::move(handle)));
   }
 
   bool use_sync_sender_ = false;
-  mojo::AssociatedBinding<IPC::mojom::SimpleTestClient> receiver_{this};
+  mojo::AssociatedReceiver<IPC::mojom::SimpleTestClient> receiver_{this};
   IPC::Sender* sync_sender_ = nullptr;
   IPC::mojom::SimpleTestDriver* driver_ = nullptr;
   std::unique_ptr<base::RunLoop> run_loop_;
diff --git a/ipc/ipc_mojo_perftest.cc b/ipc/ipc_mojo_perftest.cc
index 2763abe..79b63d23 100644
--- a/ipc/ipc_mojo_perftest.cc
+++ b/ipc/ipc_mojo_perftest.cc
@@ -25,9 +25,13 @@
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/core/test/mojo_test_base.h"
 #include "mojo/core/test/multiprocess_test_helper.h"
-#include "mojo/public/cpp/bindings/associated_binding_set.h"
+#include "mojo/public/cpp/bindings/associated_receiver_set.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 
 namespace IPC {
@@ -378,20 +382,22 @@
   // mojom::InterfacePassingTestDriver implementation:
   void Init(InitCallback callback) override { std::move(callback).Run(); }
 
-  void GetPingReceiver(std::vector<mojom::PingReceiverRequest> requests,
-                       GetPingReceiverCallback callback) override {
-    for (auto& request : requests)
-      ping_receiver_bindings_.AddBinding(this, std::move(request));
-    ping_receiver_bindings_.CloseAllBindings();
+  void GetPingReceiver(
+      std::vector<mojo::PendingReceiver<mojom::PingReceiver>> receivers,
+      GetPingReceiverCallback callback) override {
+    for (auto& receiver : receivers)
+      ping_receiver_receivers_.Add(this, std::move(receiver));
+    ping_receiver_receivers_.Clear();
     std::move(callback).Run();
   }
 
   void GetAssociatedPingReceiver(
-      std::vector<mojom::PingReceiverAssociatedRequest> requests,
+      std::vector<mojo::PendingAssociatedReceiver<mojom::PingReceiver>>
+          receivers,
       GetAssociatedPingReceiverCallback callback) override {
-    for (auto& request : requests)
-      ping_receiver_associated_bindings_.AddBinding(this, std::move(request));
-    ping_receiver_associated_bindings_.CloseAllBindings();
+    for (auto& receiver : receivers)
+      ping_receiver_associated_receivers_.Add(this, std::move(receiver));
+    ping_receiver_associated_receivers_.Clear();
     std::move(callback).Run();
   }
 
@@ -403,9 +409,9 @@
   // mojom::PingReceiver implementation:
   void Ping(PingCallback callback) override { std::move(callback).Run(); }
 
-  mojo::BindingSet<mojom::PingReceiver> ping_receiver_bindings_;
-  mojo::AssociatedBindingSet<mojom::PingReceiver>
-      ping_receiver_associated_bindings_;
+  mojo::ReceiverSet<mojom::PingReceiver> ping_receiver_receivers_;
+  mojo::AssociatedReceiverSet<mojom::PingReceiver>
+      ping_receiver_associated_receivers_;
   mojo::Binding<mojom::InterfacePassingTestDriver> binding_;
 
   base::Closure quit_closure_;
@@ -458,33 +464,34 @@
 
   void DoNextRound() {
     if (associated_) {
-      std::vector<mojom::PingReceiverAssociatedPtr> associated_interfaces(
-          num_interfaces_);
+      std::vector<mojo::AssociatedRemote<mojom::PingReceiver>>
+          associated_remotes(num_interfaces_);
 
-      std::vector<mojom::PingReceiverAssociatedRequest> requests(
-          num_interfaces_);
+      std::vector<mojo::PendingAssociatedReceiver<mojom::PingReceiver>>
+          receivers(num_interfaces_);
       for (size_t i = 0; i < num_interfaces_; ++i) {
-        requests[i] = mojo::MakeRequest(&associated_interfaces[i]);
+        receivers[i] = associated_remotes[i].BindNewEndpointAndPassReceiver();
         // Force the interface pointer to do full initialization.
-        associated_interfaces[i].get();
+        associated_remotes[i].get();
       }
 
       driver_ptr_->GetAssociatedPingReceiver(
-          std::move(requests),
+          std::move(receivers),
           base::Bind(&MojoInterfacePassingPerfTest::OnGetReceiverCallback,
                      base::Unretained(this)));
     } else {
-      std::vector<mojom::PingReceiverPtr> interfaces(num_interfaces_);
+      std::vector<mojo::Remote<mojom::PingReceiver>> remotes(num_interfaces_);
 
-      std::vector<mojom::PingReceiverRequest> requests(num_interfaces_);
+      std::vector<mojo::PendingReceiver<mojom::PingReceiver>> receivers(
+          num_interfaces_);
       for (size_t i = 0; i < num_interfaces_; ++i) {
-        requests[i] = mojo::MakeRequest(&interfaces[i]);
+        receivers[i] = remotes[i].BindNewPipeAndPassReceiver();
         // Force the interface pointer to do full initialization.
-        interfaces[i].get();
+        remotes[i].get();
       }
 
       driver_ptr_->GetPingReceiver(
-          std::move(requests),
+          std::move(receivers),
           base::Bind(&MojoInterfacePassingPerfTest::OnGetReceiverCallback,
                      base::Unretained(this)));
     }
diff --git a/ipc/ipc_test.mojom b/ipc/ipc_test.mojom
index 9276df0..04c4b0e7 100644
--- a/ipc/ipc_test.mojom
+++ b/ipc/ipc_test.mojom
@@ -32,7 +32,7 @@
 };
 
 interface IndirectTestDriver {
-  GetPingReceiver(associated PingReceiver& request);
+  GetPingReceiver(pending_associated_receiver<PingReceiver> receiver);
 };
 
 interface Reflector {
@@ -48,7 +48,8 @@
 
 interface InterfacePassingTestDriver {
   Init() => ();
-  GetPingReceiver(array<PingReceiver&> request) => ();
-  GetAssociatedPingReceiver(array<associated PingReceiver&> request) => ();
+  GetPingReceiver(array<pending_receiver<PingReceiver>> receiver) => ();
+  GetAssociatedPingReceiver(
+      array<pending_associated_receiver<PingReceiver>> receiver) => ();
   Quit();
 };
diff --git a/media/gpu/test/image_processor/image_processor_client.cc b/media/gpu/test/image_processor/image_processor_client.cc
index b33d7f3..68a4aa8 100644
--- a/media/gpu/test/image_processor/image_processor_client.cc
+++ b/media/gpu/test/image_processor/image_processor_client.cc
@@ -120,8 +120,16 @@
 #if defined(OS_CHROMEOS)
     LOG_ASSERT(image_processor_->input_storage_type() ==
                VideoFrame::STORAGE_DMABUFS);
+    // NV12 and YV12 are the only formats that can be allocated with
+    // gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE. So
+    // gfx::BufferUsage::GPU_READ_CPU_READ_WRITE is specified for RGB formats.
+    gfx::BufferUsage dst_buffer_usage =
+        IsYuvPlanar(input_image.PixelFormat())
+            ? gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE
+            : gfx::BufferUsage::GPU_READ_CPU_READ_WRITE;
     return CloneVideoFrame(CreateVideoFrameFromImage(input_image).get(),
-                           input_layout, VideoFrame::STORAGE_DMABUFS);
+                           input_layout, VideoFrame::STORAGE_DMABUFS,
+                           dst_buffer_usage);
 #endif
     return nullptr;
   }
diff --git a/media/gpu/test/video_frame_helpers.cc b/media/gpu/test/video_frame_helpers.cc
index e602f874..38e86ef 100644
--- a/media/gpu/test/video_frame_helpers.cc
+++ b/media/gpu/test/video_frame_helpers.cc
@@ -202,7 +202,8 @@
 scoped_refptr<VideoFrame> CloneVideoFrame(
     const VideoFrame* const src_frame,
     const VideoFrameLayout& dst_layout,
-    VideoFrame::StorageType dst_storage_type) {
+    VideoFrame::StorageType dst_storage_type,
+    base::Optional<gfx::BufferUsage> dst_buffer_usage) {
   if (!src_frame)
     return nullptr;
   if (!src_frame->IsMappable()) {
@@ -215,11 +216,14 @@
 #if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
     case VideoFrame::STORAGE_GPU_MEMORY_BUFFER:
     case VideoFrame::STORAGE_DMABUFS:
+      if (!dst_buffer_usage) {
+        LOG(ERROR) << "Buffer usage is not specified for a graphic buffer";
+        return nullptr;
+      }
       dst_frame = CreatePlatformVideoFrame(
           dst_layout.format(), dst_layout.coded_size(),
           src_frame->visible_rect(), src_frame->visible_rect().size(),
-          src_frame->timestamp(),
-          gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE);
+          src_frame->timestamp(), *dst_buffer_usage);
       break;
 #endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
     case VideoFrame::STORAGE_OWNED_MEMORY:
diff --git a/media/gpu/test/video_frame_helpers.h b/media/gpu/test/video_frame_helpers.h
index f1f0523..2e9f580 100644
--- a/media/gpu/test/video_frame_helpers.h
+++ b/media/gpu/test/video_frame_helpers.h
@@ -53,13 +53,15 @@
 // If |dst_storage_type| is STORAGE_DMABUFS, this function creates DMABUF-backed
 // VideoFrame with |dst_layout|. If |dst_storage_type| is STORAGE_OWNED_MEMORY,
 // this function creates memory-backed VideoFrame with |dst_layout|.
+// |dst_buffer_usage| must be specified if |dst_storage_type| is STORAGE_DMABUFS
+// or STORAGE_GPU_MEMORY_BUFFER . A graphic buffer is created with this usage.
 // The created VideoFrame's content is the same as |src_frame|. The created
 // VideoFrame owns the buffer. Returns nullptr on failure.
 scoped_refptr<VideoFrame> CloneVideoFrame(
     const VideoFrame* const src_frame,
     const VideoFrameLayout& dst_layout,
-    VideoFrame::StorageType dst_storage_type =
-        VideoFrame::STORAGE_OWNED_MEMORY);
+    VideoFrame::StorageType dst_storage_type = VideoFrame::STORAGE_OWNED_MEMORY,
+    base::Optional<gfx::BufferUsage> dst_buffer_usage = base::nullopt);
 
 // Get VideoFrame that contains Load()ed data. The returned VideoFrame doesn't
 // own the data and thus must not be changed.
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
index dd6aa94..b2385ad 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
@@ -16,6 +16,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/bits.h"
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
@@ -69,6 +70,35 @@
                             VAVEA_ENCODER_FAILURES_MAX + 1);
 }
 
+// Calculate the szie of the allocated buffer aligned to hardware/driver
+// requirements.
+gfx::Size GetInputFrameSize(VideoPixelFormat format,
+                            const gfx::Size& visible_size) {
+  if (format == PIXEL_FORMAT_I420) {
+    // Since we don't have gfx::BufferFormat for I420, replace I420 with YV12.
+    // Remove this workaround once crrev.com/c/1573718 is landed.
+    format = PIXEL_FORMAT_YV12;
+  }
+  // Get a VideoFrameLayout of a graphic buffer with the same gfx::BufferUsage
+  // as camera stack.
+  base::Optional<VideoFrameLayout> layout = GetPlatformVideoFrameLayout(
+      format, visible_size,
+      gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE);
+  if (!layout || layout->planes().empty()) {
+    VLOGF(1) << "Failed to allocate VideoFrameLayout";
+    return gfx::Size();
+  }
+
+  int32_t stride = layout->planes()[0].stride;
+  size_t plane_size = layout->planes()[0].size;
+  if (stride == 0 || plane_size == 0) {
+    VLOGF(1) << "Unexpected stride=" << stride << ", plane_size=" << plane_size;
+    return gfx::Size();
+  }
+
+  return gfx::Size(stride, plane_size / stride);
+}
+
 }  // namespace
 
 // Encode job for one frame. Created when an input frame is awaiting and
@@ -349,7 +379,13 @@
     return;
   }
 
-  coded_size_ = encoder_->GetCodedSize();
+  aligned_input_size_ =
+      GetInputFrameSize(config.input_format, config.input_visible_size);
+  if (aligned_input_size_.IsEmpty()) {
+    NOTIFY_ERROR(kPlatformFailureError, "Failed to get frame size");
+    return;
+  }
+
   output_buffer_byte_size_ = encoder_->GetBitstreamBufferSize();
   const size_t max_ref_frames = encoder_->GetMaxNumOfRefFrames();
 
@@ -368,7 +404,7 @@
       (native_input_mode_ ? 0 : kNumSurfacesPerInputVideoFrame);
 
   if (!vaapi_wrapper_->CreateContextAndSurfaces(
-          kVaSurfaceFormat, coded_size_,
+          kVaSurfaceFormat, aligned_input_size_,
           VaapiWrapper::SurfaceUsageHint::kVideoEncoder,
           (num_frames_in_flight + 1) * va_surfaces_per_video_frame_,
           &available_va_surface_ids_)) {
@@ -378,7 +414,7 @@
 
   child_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&Client::RequireBitstreamBuffers, client_,
-                                num_frames_in_flight, coded_size_,
+                                num_frames_in_flight, aligned_input_size_,
                                 output_buffer_byte_size_));
 
   SetState(kEncoding);
@@ -546,13 +582,13 @@
     }
   } else {
     input_surface =
-        new VASurface(available_va_surface_ids_.back(), coded_size_,
+        new VASurface(available_va_surface_ids_.back(), aligned_input_size_,
                       kVaSurfaceFormat, base::BindOnce(va_surface_release_cb_));
     available_va_surface_ids_.pop_back();
   }
 
   scoped_refptr<VASurface> reconstructed_surface =
-      new VASurface(available_va_surface_ids_.back(), coded_size_,
+      new VASurface(available_va_surface_ids_.back(), aligned_input_size_,
                     kVaSurfaceFormat, base::BindOnce(va_surface_release_cb_));
   available_va_surface_ids_.pop_back();
 
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.h b/media/gpu/vaapi/vaapi_video_encode_accelerator.h
index a57cbc7..0bc19a2f 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator.h
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.h
@@ -144,8 +144,8 @@
   // and will free them on destruction.
   scoped_refptr<VaapiWrapper> vaapi_wrapper_;
 
-  // The coded size of an input buffer.
-  gfx::Size coded_size_;
+  // The aligned size of the allocated physical buffer for input buffer.
+  gfx::Size aligned_input_size_;
 
   // Size in bytes required for output bitstream buffers.
   size_t output_buffer_byte_size_;
diff --git a/media/gpu/video_encode_accelerator_unittest.cc b/media/gpu/video_encode_accelerator_unittest.cc
index bd168d2d..9736e629 100644
--- a/media/gpu/video_encode_accelerator_unittest.cc
+++ b/media/gpu/video_encode_accelerator_unittest.cc
@@ -2145,9 +2145,10 @@
               current_framerate_));
   if (video_frame && g_native_input) {
 #if defined(OS_LINUX)
-    video_frame =
-        test::CloneVideoFrame(video_frame.get(), video_frame->layout(),
-                              VideoFrame::STORAGE_GPU_MEMORY_BUFFER);
+    video_frame = test::CloneVideoFrame(
+        video_frame.get(), video_frame->layout(),
+        VideoFrame::STORAGE_GPU_MEMORY_BUFFER,
+        gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE);
 #else
     video_frame = nullptr;
 #endif
diff --git a/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
index 6e8bc9e..3a69de9 100644
--- a/mojo/public/tools/bindings/chromium_bindings_configuration.gni
+++ b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
@@ -10,6 +10,7 @@
   "//chromecast/typemaps.gni",
   "//chromeos/typemaps.gni",
   "//chromeos/components/multidevice/mojom/typemaps.gni",
+  "//chromeos/services/device_sync/public/mojom/typemaps.gni",
   "//chromeos/services/network_config/public/mojom/typemaps.gni",
   "//chromeos/services/secure_channel/public/mojom/typemaps.gni",
   "//components/arc/mojom/typemaps.gni",
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 93c3b8b..21ec3a8 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -5103,6 +5103,7 @@
     "cert/caching_cert_verifier_unittest.cc",
     "cert/cert_verifier_unittest.cc",
     "cert/cert_verify_proc_android_unittest.cc",
+    "cert/cert_verify_proc_builtin_unittest.cc",
     "cert/cert_verify_proc_ios_unittest.cc",
     "cert/cert_verify_proc_mac_unittest.cc",
     "cert/cert_verify_proc_unittest.cc",
diff --git a/net/cert/cert_verify_proc_builtin.cc b/net/cert/cert_verify_proc_builtin.cc
index 7029a98..68a28d86 100644
--- a/net/cert/cert_verify_proc_builtin.cc
+++ b/net/cert/cert_verify_proc_builtin.cc
@@ -136,7 +136,8 @@
 
   // This is called for each built chain, including ones which failed. It is
   // responsible for adding errors to the built chain if it is not acceptable.
-  void CheckPathAfterVerification(CertPathBuilderResultPath* path) override {
+  void CheckPathAfterVerification(const CertPathBuilder& path_builder,
+                                  CertPathBuilderResultPath* path) override {
     // If the path is already invalid, don't check revocation status. The chain
     // is expected to be valid when doing revocation checks (since for instance
     // the correct issuer for a certificate may need to be known). Also if
@@ -191,8 +192,8 @@
     // respective certificates, so |errors->ContainsHighSeverityErrors()| will
     // reflect the revocation status of the chain after this call.
     CheckValidatedChainRevocation(
-        path->certs, policy, stapled_leaf_ocsp_response_, net_fetcher_,
-        &path->errors,
+        path->certs, policy, path_builder.deadline(),
+        stapled_leaf_ocsp_response_, net_fetcher_, &path->errors,
         &PathBuilderDelegateDataImpl::GetOrCreate(path)
              ->stapled_ocsp_verify_result);
   }
@@ -734,4 +735,8 @@
       std::move(net_fetcher), std::move(system_trust_store_provider));
 }
 
+base::TimeDelta GetCertVerifyProcBuiltinTimeLimitForTesting() {
+  return kMaxVerificationTime;
+}
+
 }  // namespace net
diff --git a/net/cert/cert_verify_proc_builtin.h b/net/cert/cert_verify_proc_builtin.h
index c114216..095555a 100644
--- a/net/cert/cert_verify_proc_builtin.h
+++ b/net/cert/cert_verify_proc_builtin.h
@@ -42,6 +42,10 @@
     scoped_refptr<CertNetFetcher> net_fetcher,
     std::unique_ptr<SystemTrustStoreProvider> system_trust_store_provider);
 
+// Returns the time limit used by CertVerifyProcBuiltin. Intended for test use.
+NET_EXPORT_PRIVATE base::TimeDelta
+GetCertVerifyProcBuiltinTimeLimitForTesting();
+
 }  // namespace net
 
 #endif  // NET_CERT_CERT_VERIFY_PROC_BUILTIN_H_
diff --git a/net/cert/cert_verify_proc_builtin_unittest.cc b/net/cert/cert_verify_proc_builtin_unittest.cc
new file mode 100644
index 0000000..cb43e260
--- /dev/null
+++ b/net/cert/cert_verify_proc_builtin_unittest.cc
@@ -0,0 +1,299 @@
+// Copyright 2019 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 "net/cert/cert_verify_proc_builtin.h"
+
+#include "base/run_loop.h"
+#include "base/task/post_task.h"
+#include "base/test/task_environment.h"
+#include "net/base/test_completion_callback.h"
+#include "net/cert/cert_verify_proc.h"
+#include "net/cert/crl_set.h"
+#include "net/cert/internal/system_trust_store.h"
+#include "net/cert_net/cert_net_fetcher_impl.h"
+#include "net/log/net_log_with_source.h"
+#include "net/test/cert_builder.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "net/test/embedded_test_server/request_handler_util.h"
+#include "net/test/gtest_util.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using net::test::IsError;
+using net::test::IsOk;
+
+namespace net {
+
+namespace {
+
+class DummySystemTrustStoreProvider : public SystemTrustStoreProvider {
+ public:
+  std::unique_ptr<SystemTrustStore> CreateSystemTrustStore() override {
+    return CreateEmptySystemTrustStore();
+  }
+};
+
+std::unique_ptr<test_server::HttpResponse> HangRequestAndCallback(
+    base::Closure callback,
+    const test_server::HttpRequest& request) {
+  callback.Run();
+  return std::make_unique<test_server::HungResponse>();
+}
+
+void FailTest(const std::string& message) {
+  ADD_FAILURE() << message;
+}
+
+std::unique_ptr<test_server::HttpResponse> FailRequestAndFailTest(
+    const std::string& message,
+    scoped_refptr<base::TaskRunner> main_task_runner,
+    const test_server::HttpRequest& request) {
+  main_task_runner->PostTask(FROM_HERE, base::Bind(FailTest, message));
+  auto response = std::make_unique<test_server::BasicHttpResponse>();
+  response->set_code(HTTP_NOT_ACCEPTABLE);
+  return response;
+}
+
+int VerifyOnWorkerThread(const scoped_refptr<CertVerifyProc>& verify_proc,
+                         scoped_refptr<X509Certificate> cert,
+                         const std::string& hostname,
+                         int flags,
+                         const CertificateList& additional_trust_anchors,
+                         CertVerifyResult* verify_result) {
+  base::ScopedAllowBaseSyncPrimitivesForTesting scoped_allow_blocking;
+  scoped_refptr<CRLSet> crl_set = CRLSet::EmptyCRLSetForTesting();
+  int error =
+      verify_proc->Verify(cert.get(), hostname,
+                          /*ocsp_response=*/std::string(),
+                          /*sct_list=*/std::string(), flags, crl_set.get(),
+                          additional_trust_anchors, verify_result);
+  verify_result->DetachFromSequence();
+  return error;
+}
+
+}  // namespace
+
+class CertVerifyProcBuiltinTest : public ::testing::Test {
+ public:
+  // CertVerifyProcBuiltinTest() {}
+
+  void SetUp() override {
+    cert_net_fetcher_ = base::MakeRefCounted<CertNetFetcherImpl>();
+    verify_proc_ = CreateCertVerifyProcBuiltin(
+        cert_net_fetcher_, std::make_unique<DummySystemTrustStoreProvider>());
+
+    context_ = std::make_unique<net::TestURLRequestContext>();
+
+    cert_net_fetcher_->SetURLRequestContext(context_.get());
+  }
+
+  void TearDown() override { cert_net_fetcher_->Shutdown(); }
+
+  void Verify(scoped_refptr<X509Certificate> cert,
+              const std::string& hostname,
+              int flags,
+              const CertificateList& additional_trust_anchors,
+              CertVerifyResult* verify_result,
+              CompletionOnceCallback callback) {
+    verify_result->DetachFromSequence();
+    base::PostTaskAndReplyWithResult(
+        FROM_HERE,
+        {base::ThreadPool(), base::MayBlock(),
+         base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+        base::BindOnce(&VerifyOnWorkerThread, verify_proc_, std::move(cert),
+                       hostname, flags, additional_trust_anchors,
+                       verify_result),
+        std::move(callback));
+  }
+
+  base::test::TaskEnvironment& task_environment() { return task_environment_; }
+
+  void CreateChain(std::unique_ptr<CertBuilder>* out_leaf,
+                   std::unique_ptr<CertBuilder>* out_intermediate,
+                   std::unique_ptr<CertBuilder>* out_root) {
+    CertBuilder::CreateSimpleChain(out_leaf, out_intermediate, out_root);
+    ASSERT_TRUE(*out_leaf && *out_intermediate && *out_root);
+    // This test uses MOCK_TIME, so need to set the cert validity dates based
+    // on whatever the mock time happens to start at.
+    base::Time not_before = base::Time::Now() - base::TimeDelta::FromDays(1);
+    base::Time not_after = base::Time::Now() + base::TimeDelta::FromDays(10);
+    (*out_leaf)->SetValidity(not_before, not_after);
+    (*out_intermediate)->SetValidity(not_before, not_after);
+    (*out_root)->SetValidity(not_before, not_after);
+  }
+
+ private:
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME,
+      base::test::TaskEnvironment::MainThreadType::IO,
+  };
+
+  CertVerifier::Config config_;
+  std::unique_ptr<net::TestURLRequestContext> context_;
+  scoped_refptr<CertVerifyProc> verify_proc_;
+  scoped_refptr<CertNetFetcherImpl> cert_net_fetcher_;
+};
+
+TEST_F(CertVerifyProcBuiltinTest, SimpleSuccess) {
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
+  ASSERT_TRUE(chain.get());
+
+  CertVerifyResult verify_result;
+  TestCompletionCallback callback;
+  Verify(chain.get(), "www.example.com", /*flags=*/0,
+         /*additional_trust_anchors=*/{root->GetX509Certificate()},
+         &verify_result, callback.callback());
+
+  int error = callback.WaitForResult();
+  EXPECT_THAT(error, IsOk());
+}
+
+// Tests that if the verification deadline is exceeded during revocation
+// checking, additional CRL fetches will not be attempted.
+TEST_F(CertVerifyProcBuiltinTest, RevocationCheckDeadlineCRL) {
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  const base::TimeDelta timeout_increment =
+      CertNetFetcherImpl::GetDefaultTimeoutForTesting() +
+      base::TimeDelta::FromMilliseconds(1);
+  const int expected_request_count =
+      GetCertVerifyProcBuiltinTimeLimitForTesting() / timeout_increment + 1;
+
+  EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTP);
+  ASSERT_TRUE(test_server.InitializeAndListen());
+
+  // Set up the test cert to have enough crlDistributionPoint urls that if the
+  // first N-1 requests hang the deadline will be exceeded before the Nth
+  // request is made.
+  std::vector<GURL> crl_urls;
+  std::vector<base::RunLoop> runloops(expected_request_count);
+  for (int i = 0; i < expected_request_count; ++i) {
+    std::string path = base::StringPrintf("/hung/%i", i);
+    crl_urls.emplace_back(test_server.GetURL(path));
+    test_server.RegisterRequestHandler(
+        base::BindRepeating(&test_server::HandlePrefixedRequest, path,
+                            base::BindRepeating(&HangRequestAndCallback,
+                                                runloops[i].QuitClosure())));
+  }
+  // Add CRL URLs and handlers that will add test failures if requested.
+  for (int i = expected_request_count; i < expected_request_count + 1; ++i) {
+    std::string path = base::StringPrintf("/failtest/%i", i);
+    crl_urls.emplace_back(test_server.GetURL(path));
+    test_server.RegisterRequestHandler(base::Bind(
+        &test_server::HandlePrefixedRequest, path,
+        base::BindRepeating(FailRequestAndFailTest,
+                            "additional request made after deadline exceeded",
+                            base::SequencedTaskRunnerHandle::Get())));
+  }
+  leaf->SetCrlDistributionPointUrls(crl_urls);
+
+  test_server.StartAcceptingConnections();
+
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
+  ASSERT_TRUE(chain.get());
+
+  CertVerifyResult verify_result;
+  TestCompletionCallback verify_callback;
+  Verify(chain.get(), "www.example.com",
+         CertVerifyProc::VERIFY_REV_CHECKING_ENABLED,
+         /*additional_trust_anchors=*/{root->GetX509Certificate()},
+         &verify_result, verify_callback.callback());
+
+  for (int i = 0; i < expected_request_count; i++) {
+    // Wait for request #|i| to be made.
+    runloops[i].Run();
+    // Advance virtual time to cause the timeout task to become runnable.
+    task_environment().AdvanceClock(timeout_increment);
+  }
+
+  // Once |expected_request_count| requests have been made and timed out, the
+  // overall deadline should be reached, and no more requests should have been
+  // made. (If they were, the test will fail due to the ADD_FAILURE callback in
+  // the request handlers.)
+  int error = verify_callback.WaitForResult();
+  // Soft-fail revocation checking was used, therefore verification result
+  // should be OK even though none of the CRLs could be retrieved.
+  EXPECT_THAT(error, IsOk());
+}
+
+// Tests that if the verification deadline is exceeded during revocation
+// checking, additional OCSP fetches will not be attempted.
+TEST_F(CertVerifyProcBuiltinTest, RevocationCheckDeadlineOCSP) {
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  const base::TimeDelta timeout_increment =
+      CertNetFetcherImpl::GetDefaultTimeoutForTesting() +
+      base::TimeDelta::FromMilliseconds(1);
+  const int expected_request_count =
+      GetCertVerifyProcBuiltinTimeLimitForTesting() / timeout_increment + 1;
+
+  EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTP);
+  ASSERT_TRUE(test_server.InitializeAndListen());
+
+  // Set up the test cert to have enough OCSP urls that if the
+  // first N-1 requests hang the deadline will be exceeded before the Nth
+  // request is made.
+  std::vector<GURL> ocsp_urls;
+  std::vector<base::RunLoop> runloops(expected_request_count);
+  for (int i = 0; i < expected_request_count; ++i) {
+    std::string path = base::StringPrintf("/hung/%i", i);
+    ocsp_urls.emplace_back(test_server.GetURL(path));
+    test_server.RegisterRequestHandler(
+        base::BindRepeating(&test_server::HandlePrefixedRequest, path,
+                            base::BindRepeating(&HangRequestAndCallback,
+                                                runloops[i].QuitClosure())));
+  }
+  // Add OCSP URLs and handlers that will add test failures if requested.
+  for (int i = expected_request_count; i < expected_request_count + 1; ++i) {
+    std::string path = base::StringPrintf("/failtest/%i", i);
+    ocsp_urls.emplace_back(test_server.GetURL(path));
+    test_server.RegisterRequestHandler(base::Bind(
+        &test_server::HandlePrefixedRequest, path,
+        base::BindRepeating(FailRequestAndFailTest,
+                            "additional request made after deadline exceeded",
+                            base::SequencedTaskRunnerHandle::Get())));
+  }
+  leaf->SetCaIssuersAndOCSPUrls({}, ocsp_urls);
+
+  test_server.StartAcceptingConnections();
+
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
+  ASSERT_TRUE(chain.get());
+
+  CertVerifyResult verify_result;
+  TestCompletionCallback verify_callback;
+  Verify(chain.get(), "www.example.com",
+         CertVerifyProc::VERIFY_REV_CHECKING_ENABLED,
+         /*additional_trust_anchors=*/{root->GetX509Certificate()},
+         &verify_result, verify_callback.callback());
+
+  for (int i = 0; i < expected_request_count; i++) {
+    // Wait for request #|i| to be made.
+    runloops[i].Run();
+    // Advance virtual time to cause the timeout task to become runnable.
+    task_environment().AdvanceClock(timeout_increment);
+  }
+
+  // Once |expected_request_count| requests have been made and timed out, the
+  // overall deadline should be reached, and no more requests should have been
+  // made. (If they were, the test will fail due to the ADD_FAILURE callback in
+  // the request handlers.)
+  int error = verify_callback.WaitForResult();
+  // Soft-fail revocation checking was used, therefore verification result
+  // should be OK even though none of the OCSP responses could be retrieved.
+  EXPECT_THAT(error, IsOk());
+}
+
+}  // namespace net
diff --git a/net/cert/internal/path_builder.cc b/net/cert/internal/path_builder.cc
index ce9c839..8da4222 100644
--- a/net/cert/internal/path_builder.cc
+++ b/net/cert/internal/path_builder.cc
@@ -618,7 +618,7 @@
              << result_path->errors.ToDebugString(result_path->certs);
 
     // Give the delegate a chance to add errors to the path.
-    delegate_->CheckPathAfterVerification(result_path.get());
+    delegate_->CheckPathAfterVerification(*this, result_path.get());
 
     bool path_is_good = result_path->IsValid();
 
diff --git a/net/cert/internal/path_builder.h b/net/cert/internal/path_builder.h
index ae7f8c25..7dba3e5 100644
--- a/net/cert/internal/path_builder.h
+++ b/net/cert/internal/path_builder.h
@@ -24,6 +24,7 @@
 struct GeneralizedTime;
 }
 
+class CertPathBuilder;
 class CertPathIter;
 class CertIssuerSource;
 
@@ -92,7 +93,8 @@
   // been run through RFC 5280 verification. |path| may already have errors
   // and warnings set on it. Delegates can "reject" a candidate path from path
   // building by adding high severity errors.
-  virtual void CheckPathAfterVerification(CertPathBuilderResultPath* path) = 0;
+  virtual void CheckPathAfterVerification(const CertPathBuilder& path_builder,
+                                          CertPathBuilderResultPath* path) = 0;
 };
 
 // Checks whether a certificate is trusted by building candidate paths to trust
@@ -184,6 +186,10 @@
   // will be when path building is aborted.
   void SetDeadline(base::TimeTicks deadline);
 
+  // Returns the deadline for path building, if any. If no deadline is set,
+  // |deadline().is_null()| will be true.
+  base::TimeTicks deadline() const { return deadline_; }
+
   // Executes verification of the target certificate.
   //
   // Run must not be called more than once on each CertPathBuilder instance.
diff --git a/net/cert/internal/path_builder_pkits_unittest.cc b/net/cert/internal/path_builder_pkits_unittest.cc
index 16ac561..dca35a8c 100644
--- a/net/cert/internal/path_builder_pkits_unittest.cc
+++ b/net/cert/internal/path_builder_pkits_unittest.cc
@@ -35,8 +35,9 @@
         verify_time_(verify_time),
         max_age_(max_age) {}
 
-  void CheckPathAfterVerification(CertPathBuilderResultPath* path) override {
-    SimplePathBuilderDelegate::CheckPathAfterVerification(path);
+  void CheckPathAfterVerification(const CertPathBuilder& path_builder,
+                                  CertPathBuilderResultPath* path) override {
+    SimplePathBuilderDelegate::CheckPathAfterVerification(path_builder, path);
 
     if (!path->IsValid())
       return;
diff --git a/net/cert/internal/path_builder_unittest.cc b/net/cert/internal/path_builder_unittest.cc
index 9b7d0b8..e9dd8fb 100644
--- a/net/cert/internal/path_builder_unittest.cc
+++ b/net/cert/internal/path_builder_unittest.cc
@@ -550,23 +550,24 @@
         initial_policy_mapping_inhibit_, initial_any_policy_inhibit_);
     path_builder.AddCertIssuerSource(&sync_certs);
 
+    base::TimeTicks deadline;
     if (insufficient_limit) {
       // Set a deadline one millisecond in the past. Path building should fail
       // since the deadline is already past.
-      path_builder.SetDeadline(base::TimeTicks::Now() -
-                               base::TimeDelta::FromMilliseconds(1));
+      deadline = base::TimeTicks::Now() - base::TimeDelta::FromMilliseconds(1);
     } else {
       // The other tests in this file exercise the case that |SetDeadline|
       // isn't called. Therefore set a sufficient limit for the path to be
       // found.
-      path_builder.SetDeadline(base::TimeTicks::Now() +
-                               base::TimeDelta::FromDays(1));
+      deadline = base::TimeTicks::Now() + base::TimeDelta::FromDays(1);
     }
+    path_builder.SetDeadline(deadline);
 
     auto result = path_builder.Run();
 
     EXPECT_EQ(!insufficient_limit, result.HasValidPath());
     EXPECT_EQ(insufficient_limit, result.exceeded_deadline);
+    EXPECT_EQ(deadline, path_builder.deadline());
   }
 }
 
@@ -1408,21 +1409,23 @@
       : SimplePathBuilderDelegate(
             1024,
             SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1) {}
-  void CheckPathAfterVerification(CertPathBuilderResultPath* path) override {
+  void CheckPathAfterVerification(const CertPathBuilder& path_builder,
+                                  CertPathBuilderResultPath* path) override {
     ADD_FAILURE() << "Tests must override this";
   }
 };
 
 class MockPathBuilderDelegate : public CertPathBuilderDelegateBase {
  public:
-  MOCK_METHOD1(CheckPathAfterVerification,
-               void(CertPathBuilderResultPath* path));
+  MOCK_METHOD2(CheckPathAfterVerification,
+               void(const CertPathBuilder& path_builder,
+                    CertPathBuilderResultPath* path));
 };
 
 TEST_F(PathBuilderCheckPathAfterVerificationTest, NoOpToValidPath) {
   StrictMock<MockPathBuilderDelegate> delegate;
   // Just verify that the hook is called.
-  EXPECT_CALL(delegate, CheckPathAfterVerification(_));
+  EXPECT_CALL(delegate, CheckPathAfterVerification(_, _));
 
   CertPathBuilder::Result result = RunPathBuilder(nullptr, &delegate);
   EXPECT_TRUE(result.HasValidPath());
@@ -1432,7 +1435,8 @@
 
 class AddWarningPathBuilderDelegate : public CertPathBuilderDelegateBase {
  public:
-  void CheckPathAfterVerification(CertPathBuilderResultPath* path) override {
+  void CheckPathAfterVerification(const CertPathBuilder& path_builder,
+                                  CertPathBuilderResultPath* path) override {
     path->errors.GetErrorsForCert(1)->AddWarning(kWarningFromDelegate, nullptr);
   }
 };
@@ -1453,7 +1457,8 @@
 
 class AddErrorPathBuilderDelegate : public CertPathBuilderDelegateBase {
  public:
-  void CheckPathAfterVerification(CertPathBuilderResultPath* path) override {
+  void CheckPathAfterVerification(const CertPathBuilder& path_builder,
+                                  CertPathBuilderResultPath* path) override {
     path->errors.GetErrorsForCert(2)->AddError(kErrorFromDelegate, nullptr);
   }
 };
@@ -1479,7 +1484,7 @@
 TEST_F(PathBuilderCheckPathAfterVerificationTest, NoopToAlreadyInvalidPath) {
   StrictMock<MockPathBuilderDelegate> delegate;
   // Just verify that the hook is called (on an invalid path).
-  EXPECT_CALL(delegate, CheckPathAfterVerification(_));
+  EXPECT_CALL(delegate, CheckPathAfterVerification(_, _));
 
   // Run the pathbuilder with certificate at index 1 actively distrusted.
   CertPathBuilder::Result result = RunPathBuilder(test_.chain[1], &delegate);
@@ -1492,7 +1497,8 @@
 
 class SetsDelegateDataPathBuilderDelegate : public CertPathBuilderDelegateBase {
  public:
-  void CheckPathAfterVerification(CertPathBuilderResultPath* path) override {
+  void CheckPathAfterVerification(const CertPathBuilder& path_builder,
+                                  CertPathBuilderResultPath* path) override {
     path->delegate_data = std::make_unique<DelegateData>();
   }
 };
diff --git a/net/cert/internal/revocation_checker.cc b/net/cert/internal/revocation_checker.cc
index 9761fd56..458f0079 100644
--- a/net/cert/internal/revocation_checker.cc
+++ b/net/cert/internal/revocation_checker.cc
@@ -35,6 +35,7 @@
 bool CheckCertRevocation(const ParsedCertificateList& certs,
                          size_t target_cert_index,
                          const RevocationPolicy& policy,
+                         base::TimeTicks deadline,
                          base::StringPiece stapled_ocsp_response,
                          base::TimeDelta max_age,
                          CertNetFetcher* net_fetcher,
@@ -92,6 +93,11 @@
 
       found_revocation_info = true;
 
+      // Check the deadline after setting found_revocation_info, to not give a
+      // misleading kNoRevocationMechanism failure.
+      if (!deadline.is_null() && base::TimeTicks::Now() > deadline)
+        break;
+
       if (!policy.networking_allowed)
         continue;
 
@@ -114,9 +120,6 @@
       // TODO(eroman): Issue POST instead of GET if request is larger than 255
       //               bytes?
       // TODO(eroman): Improve interplay with HTTP cache.
-      //
-      // TODO(eroman): Bound the maximum time allowed spent doing network
-      // requests.
       std::unique_ptr<CertNetFetcher::Request> net_ocsp_request =
           net_fetcher->FetchOcsp(get_url, CertNetFetcher::DEFAULT,
                                  CertNetFetcher::DEFAULT);
@@ -172,6 +175,11 @@
 
           found_revocation_info = true;
 
+          // Check the deadline after setting found_revocation_info, to not give
+          // a misleading kNoRevocationMechanism failure.
+          if (!deadline.is_null() && base::TimeTicks::Now() > deadline)
+            break;
+
           if (!policy.networking_allowed)
             continue;
 
@@ -185,9 +193,6 @@
           // Note that no attempt is made to refetch without cache if a cached
           // CRL is too old, nor is there a separate CRL cache. It is assumed
           // the CRL server will send reasonable HTTP caching headers.
-          //
-          // TODO(eroman): Bound the maximum time allowed spent doing network
-          // requests.
           std::unique_ptr<CertNetFetcher::Request> net_crl_request =
               net_fetcher->FetchCrl(parsed_crl_url, CertNetFetcher::DEFAULT,
                                     CertNetFetcher::DEFAULT);
@@ -259,6 +264,7 @@
 void CheckValidatedChainRevocation(
     const ParsedCertificateList& certs,
     const RevocationPolicy& policy,
+    base::TimeTicks deadline,
     base::StringPiece stapled_leaf_ocsp_response,
     CertNetFetcher* net_fetcher,
     CertPathErrors* errors,
@@ -292,7 +298,7 @@
     // Check whether this certificate's revocation status complies with the
     // policy.
     bool cert_ok =
-        CheckCertRevocation(certs, i, policy, stapled_ocsp, max_age,
+        CheckCertRevocation(certs, i, policy, deadline, stapled_ocsp, max_age,
                             net_fetcher, errors->GetErrorsForCert(i),
                             (i == 0) ? stapled_ocsp_verify_result : nullptr);
 
diff --git a/net/cert/internal/revocation_checker.h b/net/cert/internal/revocation_checker.h
index 35b0d0f..cb26b87 100644
--- a/net/cert/internal/revocation_checker.h
+++ b/net/cert/internal/revocation_checker.h
@@ -90,6 +90,12 @@
 // any failures to |errors|. On failure errors are added to |errors|. On success
 // no errors are added.
 //
+// |deadline|, if not null, will limit the overall amount of time spent doing
+// online revocation checks. If |base::TimeTicks::Now()| exceeds |deadline|, no
+// more revocation checks will be attempted. Note that this is not a hard
+// limit, the deadline may be exceeded by the individual request timetout of a
+// single CertNetFetcher.
+//
 // |certs| must be a successfully validated chain according to RFC 5280 section
 // 6.1, in order from leaf to trust anchor.
 //
@@ -101,6 +107,7 @@
 NET_EXPORT_PRIVATE void CheckValidatedChainRevocation(
     const ParsedCertificateList& certs,
     const RevocationPolicy& policy,
+    base::TimeTicks deadline,
     base::StringPiece stapled_leaf_ocsp_response,
     CertNetFetcher* net_fetcher,
     CertPathErrors* errors,
diff --git a/net/cert/internal/simple_path_builder_delegate.cc b/net/cert/internal/simple_path_builder_delegate.cc
index f5c0cbd8..713baa6 100644
--- a/net/cert/internal/simple_path_builder_delegate.cc
+++ b/net/cert/internal/simple_path_builder_delegate.cc
@@ -48,6 +48,7 @@
       digest_policy_(digest_policy) {}
 
 void SimplePathBuilderDelegate::CheckPathAfterVerification(
+    const CertPathBuilder& path_builder,
     CertPathBuilderResultPath* path) {
   // Do nothing - consider all candidate paths valid.
 }
diff --git a/net/cert/internal/simple_path_builder_delegate.h b/net/cert/internal/simple_path_builder_delegate.h
index ede49e5..ca76705b 100644
--- a/net/cert/internal/simple_path_builder_delegate.h
+++ b/net/cert/internal/simple_path_builder_delegate.h
@@ -53,7 +53,8 @@
   bool IsPublicKeyAcceptable(EVP_PKEY* public_key, CertErrors* errors) override;
 
   // No-op implementation.
-  void CheckPathAfterVerification(CertPathBuilderResultPath* path) override;
+  void CheckPathAfterVerification(const CertPathBuilder& path_builder,
+                                  CertPathBuilderResultPath* path) override;
 
  private:
   bool IsAcceptableDigest(DigestAlgorithm digest) const WARN_UNUSED_RESULT;
diff --git a/net/cert_net/cert_net_fetcher_impl.cc b/net/cert_net/cert_net_fetcher_impl.cc
index deabea902..0a3ed47 100644
--- a/net/cert_net/cert_net_fetcher_impl.cc
+++ b/net/cert_net/cert_net_fetcher_impl.cc
@@ -500,10 +500,11 @@
   url_request_->Start();
 
   // Start a timer to limit how long the job runs for.
-  if (request_params_->timeout > base::TimeDelta())
+  if (request_params_->timeout > base::TimeDelta()) {
     timer_.Start(FROM_HERE, request_params_->timeout,
                  base::BindOnce(&Job::FailRequest, base::Unretained(this),
                                 ERR_TIMED_OUT));
+  }
 }
 
 void Job::Cancel() {
@@ -745,6 +746,11 @@
   context_ = context;
 }
 
+// static
+base::TimeDelta CertNetFetcherImpl::GetDefaultTimeoutForTesting() {
+  return GetTimeout(CertNetFetcher::DEFAULT);
+}
+
 void CertNetFetcherImpl::Shutdown() {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   if (impl_) {
diff --git a/net/cert_net/cert_net_fetcher_impl.h b/net/cert_net/cert_net_fetcher_impl.h
index b1ad43e..c4774bea 100644
--- a/net/cert_net/cert_net_fetcher_impl.h
+++ b/net/cert_net/cert_net_fetcher_impl.h
@@ -36,6 +36,9 @@
   // |context_| must stay valid until Shutdown() is called.
   void SetURLRequestContext(URLRequestContext* context);
 
+  // Returns the default timeout value. Intended for test use only.
+  static base::TimeDelta GetDefaultTimeoutForTesting();
+
   // CertNetFetcher impl:
   void Shutdown() override;
   std::unique_ptr<Request> FetchCaIssuers(const GURL& url,
diff --git a/net/test/cert_builder.cc b/net/test/cert_builder.cc
index 1f3158c..b61df8db 100644
--- a/net/test/cert_builder.cc
+++ b/net/test/cert_builder.cc
@@ -167,7 +167,18 @@
 }
 
 void CertBuilder::SetCaIssuersUrl(const GURL& url) {
-  std::string url_spec = url.spec();
+  SetCaIssuersAndOCSPUrls({url}, {});
+}
+
+void CertBuilder::SetCaIssuersAndOCSPUrls(
+    const std::vector<GURL>& ca_issuers_urls,
+    const std::vector<GURL>& ocsp_urls) {
+  std::vector<std::pair<der::Input, GURL>> entries;
+  for (const auto& url : ca_issuers_urls)
+    entries.emplace_back(AdCaIssuersOid(), url);
+  for (const auto& url : ocsp_urls)
+    entries.emplace_back(AdOcspOid(), url);
+  ASSERT_GT(entries.size(), 0U);
 
   // From RFC 5280:
   //
@@ -178,25 +189,33 @@
   //           accessMethod          OBJECT IDENTIFIER,
   //           accessLocation        GeneralName  }
   bssl::ScopedCBB cbb;
-  CBB aia, ca_issuer, access_method, access_location;
-  ASSERT_TRUE(CBB_init(cbb.get(), url_spec.size()));
+  CBB aia;
+  ASSERT_TRUE(CBB_init(cbb.get(), 64));
   ASSERT_TRUE(CBB_add_asn1(cbb.get(), &aia, CBS_ASN1_SEQUENCE));
-  ASSERT_TRUE(CBB_add_asn1(&aia, &ca_issuer, CBS_ASN1_SEQUENCE));
-  ASSERT_TRUE(CBB_add_asn1(&ca_issuer, &access_method, CBS_ASN1_OBJECT));
-  ASSERT_TRUE(CBBAddBytes(&access_method, AdCaIssuersOid().AsStringPiece()));
-  ASSERT_TRUE(CBB_add_asn1(&ca_issuer, &access_location,
-                           CBS_ASN1_CONTEXT_SPECIFIC | 6));
-  ASSERT_TRUE(CBBAddBytes(&access_location, url_spec));
+
+  for (const auto& entry : entries) {
+    CBB access_description, access_method, access_location;
+    ASSERT_TRUE(CBB_add_asn1(&aia, &access_description, CBS_ASN1_SEQUENCE));
+    ASSERT_TRUE(
+        CBB_add_asn1(&access_description, &access_method, CBS_ASN1_OBJECT));
+    ASSERT_TRUE(CBBAddBytes(&access_method, entry.first.AsStringPiece()));
+    ASSERT_TRUE(CBB_add_asn1(&access_description, &access_location,
+                             CBS_ASN1_CONTEXT_SPECIFIC | 6));
+    ASSERT_TRUE(CBBAddBytes(&access_location, entry.second.spec()));
+    ASSERT_TRUE(CBB_flush(&aia));
+  }
 
   SetExtension(AuthorityInfoAccessOid(), FinishCBB(cbb.get()));
 }
 
 void CertBuilder::SetCrlDistributionPointUrl(const GURL& url) {
-  std::string url_spec = url.spec();
+  SetCrlDistributionPointUrls({url});
+}
 
+void CertBuilder::SetCrlDistributionPointUrls(const std::vector<GURL>& urls) {
   bssl::ScopedCBB cbb;
-  ASSERT_TRUE(CBB_init(cbb.get(), url_spec.size()));
-  CBB dps, dp, dp_name, dp_fullname, dp_url;
+  ASSERT_TRUE(CBB_init(cbb.get(), 64));
+  CBB dps, dp, dp_name, dp_fullname;
 
   //    CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
   ASSERT_TRUE(CBB_add_asn1(cbb.get(), &dps, CBS_ASN1_SEQUENCE));
@@ -219,9 +238,13 @@
   //   GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
   //   GeneralName ::= CHOICE {
   // uniformResourceIdentifier       [6]     IA5String,
-  ASSERT_TRUE(
-      CBB_add_asn1(&dp_fullname, &dp_url, CBS_ASN1_CONTEXT_SPECIFIC | 6));
-  ASSERT_TRUE(CBBAddBytes(&dp_url, url_spec));
+  for (const auto& url : urls) {
+    CBB dp_url;
+    ASSERT_TRUE(
+        CBB_add_asn1(&dp_fullname, &dp_url, CBS_ASN1_CONTEXT_SPECIFIC | 6));
+    ASSERT_TRUE(CBBAddBytes(&dp_url, url.spec()));
+    ASSERT_TRUE(CBB_flush(&dp_fullname));
+  }
 
   SetExtension(CrlDistributionPointsOid(), FinishCBB(cbb.get()));
 }
@@ -268,6 +291,20 @@
   SetExtension(SubjectAltNameOid(), FinishCBB(cbb.get()));
 }
 
+void CertBuilder::SetValidity(base::Time not_before, base::Time not_after) {
+  // From RFC 5280:
+  //   Validity ::= SEQUENCE {
+  //        notBefore      Time,
+  //        notAfter       Time }
+  bssl::ScopedCBB cbb;
+  CBB validity;
+  ASSERT_TRUE(CBB_init(cbb.get(), 64));
+  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &validity, CBS_ASN1_SEQUENCE));
+  ASSERT_TRUE(CBBAddTime(&validity, not_before));
+  ASSERT_TRUE(CBBAddTime(&validity, not_after));
+  validity_tlv_ = FinishCBB(cbb.get());
+}
+
 void CertBuilder::SetSignatureAlgorithmRsaPkca1(DigestAlgorithm digest) {
   switch (digest) {
     case DigestAlgorithm::Sha256: {
diff --git a/net/test/cert_builder.h b/net/test/cert_builder.h
index 8660e73..153e739 100644
--- a/net/test/cert_builder.h
+++ b/net/test/cert_builder.h
@@ -55,13 +55,27 @@
   // Sets an AIA extension with a single caIssuers access method.
   void SetCaIssuersUrl(const GURL& url);
 
+  // Sets an AIA extension with the specified caIssuers and OCSP urls. Either
+  // list can have 0 or more URLs, but it is an error for both lists to be
+  // empty.
+  void SetCaIssuersAndOCSPUrls(const std::vector<GURL>& ca_issuers_urls,
+                               const std::vector<GURL>& ocsp_urls);
+
+  // Sets a cRLDistributionPoints extension with a single DistributionPoint
+  // with |url| in distributionPoint.fullName.
   void SetCrlDistributionPointUrl(const GURL& url);
 
+  // Sets a cRLDistributionPoints extension with a single DistributionPoint
+  // with |urls| in distributionPoints.fullName.
+  void SetCrlDistributionPointUrls(const std::vector<GURL>& urls);
+
   void SetSubjectCommonName(const std::string common_name);
 
   // Sets the SAN for the certificate to a single dNSName.
   void SetSubjectAltName(const std::string& dns_name);
 
+  void SetValidity(base::Time not_before, base::Time not_after);
+
   // Sets the signature algorithm for the certificate to either
   // sha256WithRSAEncryption or sha1WithRSAEncryption.
   void SetSignatureAlgorithmRsaPkca1(DigestAlgorithm digest);
diff --git a/pdf/out_of_process_instance.cc b/pdf/out_of_process_instance.cc
index 3d8c99d..a3786a8 100644
--- a/pdf/out_of_process_instance.cc
+++ b/pdf/out_of_process_instance.cc
@@ -24,6 +24,7 @@
 #include "net/base/escape.h"
 #include "net/base/filename_util.h"
 #include "pdf/accessibility.h"
+#include "pdf/document_layout.h"
 #include "pdf/pdf.h"
 #include "pdf/pdf_features.h"
 #include "ppapi/c/dev/ppb_cursor_control_dev.h"
@@ -1284,20 +1285,20 @@
   }
 }
 
-void OutOfProcessInstance::DocumentSizeUpdated(const pp::Size& size) {
-  document_size_ = size;
+void OutOfProcessInstance::ProposeDocumentLayout(const DocumentLayout& layout) {
+  document_size_ = layout.size();
 
   pp::VarDictionary dimensions;
   dimensions.Set(kType, kJSDocumentDimensionsType);
   dimensions.Set(kJSDocumentWidth, pp::Var(document_size_.width()));
   dimensions.Set(kJSDocumentHeight, pp::Var(document_size_.height()));
   pp::VarArray page_dimensions_array;
-  size_t num_pages = engine_->GetNumberOfPages();
+  size_t num_pages = layout.page_count();
   if (page_is_processed_.size() < num_pages)
     page_is_processed_.resize(num_pages);
 
   for (size_t i = 0; i < num_pages; ++i) {
-    pp::Rect page_rect = engine_->GetPageRect(i);
+    pp::Rect page_rect = layout.page_rect(i);
     pp::VarDictionary page_dimensions;
     page_dimensions.Set(kJSPageX, pp::Var(page_rect.x()));
     page_dimensions.Set(kJSPageY, pp::Var(page_rect.y()));
diff --git a/pdf/out_of_process_instance.h b/pdf/out_of_process_instance.h
index 769f4c4..3724abc 100644
--- a/pdf/out_of_process_instance.h
+++ b/pdf/out_of_process_instance.h
@@ -97,7 +97,7 @@
   void OnPrint(int32_t);
 
   // PDFEngine::Client implementation.
-  void DocumentSizeUpdated(const pp::Size& size) override;
+  void ProposeDocumentLayout(const DocumentLayout& layout) override;
   void Invalidate(const pp::Rect& rect) override;
   void DidScroll(const pp::Point& point) override;
   void ScrollToX(int x_in_screen_coords) override;
diff --git a/pdf/pdf_engine.h b/pdf/pdf_engine.h
index e94a557b..c61bc71 100644
--- a/pdf/pdf_engine.h
+++ b/pdf/pdf_engine.h
@@ -55,6 +55,8 @@
 
 namespace chrome_pdf {
 
+class DocumentLayout;
+
 // Do one time initialization of the SDK.
 // If |enable_v8| is false, then the PDFEngine will not be able to run
 // JavaScript.
@@ -136,8 +138,10 @@
    public:
     virtual ~Client() {}
 
-    // Informs the client about the document's size in pixels.
-    virtual void DocumentSizeUpdated(const pp::Size& size) {}
+    // Proposes a document layout to the client.
+    // TODO(crbug.com/885110): Layout still occurs immediately for now. In the
+    // future, the client will need to accept the layout before it takes effect.
+    virtual void ProposeDocumentLayout(const DocumentLayout& layout) = 0;
 
     // Informs the client that the given rect needs to be repainted.
     virtual void Invalidate(const pp::Rect& rect) {}
@@ -340,8 +344,6 @@
       const std::string& destination) = 0;
   // Gets the index of the most visible page, or -1 if none are visible.
   virtual int GetMostVisiblePage() = 0;
-  // Gets the rectangle of the page including shadow.
-  virtual pp::Rect GetPageRect(int index) = 0;
   // Gets the rectangle of the page not including the shadow.
   virtual pp::Rect GetPageBoundsRect(int index) = 0;
   // Gets the rectangle of the page excluding any additional areas.
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index 1628b4c..97105e8 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -2223,12 +2223,6 @@
   return most_visible_page_;
 }
 
-pp::Rect PDFiumEngine::GetPageRect(int index) {
-  pp::Rect rc(pages_[index]->rect());
-  InsetPage(index, pages_.size(), /*multiplier=*/-1, &rc);
-  return rc;
-}
-
 pp::Rect PDFiumEngine::GetPageBoundsRect(int index) {
   return pages_[index]->rect();
 }
@@ -2556,7 +2550,7 @@
   CalculateVisiblePages();
   if (layout_.dirty()) {
     layout_.clear_dirty();
-    client_->DocumentSizeUpdated(layout_.size());
+    client_->ProposeDocumentLayout(layout_);
   }
 }
 
diff --git a/pdf/pdfium/pdfium_engine.h b/pdf/pdfium/pdfium_engine.h
index d03384a2..4c0d1c9 100644
--- a/pdf/pdfium/pdfium_engine.h
+++ b/pdf/pdfium/pdfium_engine.h
@@ -105,7 +105,6 @@
   base::Optional<PDFEngine::NamedDestination> GetNamedDestination(
       const std::string& destination) override;
   int GetMostVisiblePage() override;
-  pp::Rect GetPageRect(int index) override;
   pp::Rect GetPageBoundsRect(int index) override;
   pp::Rect GetPageContentsRect(int index) override;
   pp::Rect GetPageScreenRect(int page_index) const override;
diff --git a/pdf/pdfium/pdfium_engine_unittest.cc b/pdf/pdfium/pdfium_engine_unittest.cc
index c1761c4..36d78a7 100644
--- a/pdf/pdfium/pdfium_engine_unittest.cc
+++ b/pdf/pdfium/pdfium_engine_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "pdf/pdfium/pdfium_engine.h"
 
+#include "pdf/document_layout.h"
 #include "pdf/pdfium/pdfium_page.h"
 #include "pdf/pdfium/pdfium_test_base.h"
 #include "pdf/test/test_client.h"
@@ -18,10 +19,21 @@
 using ::testing::InSequence;
 using ::testing::NiceMock;
 
+MATCHER_P2(LayoutWithSize, width, height, "") {
+  return arg.size() == pp::Size(width, height);
+}
+
 class MockTestClient : public TestClient {
  public:
+  MockTestClient() {
+    ON_CALL(*this, ProposeDocumentLayout)
+        .WillByDefault([this](const DocumentLayout& layout) {
+          TestClient::ProposeDocumentLayout(layout);
+        });
+  }
+
   // TODO(crbug.com/989095): MOCK_METHOD() triggers static_assert on Windows.
-  MOCK_METHOD1(DocumentSizeUpdated, void(const pp::Size& size));
+  MOCK_METHOD1(ProposeDocumentLayout, void(const DocumentLayout& layout));
 };
 
 class PDFiumEngineTest : public PDFiumTestBase {
@@ -37,7 +49,7 @@
 
 TEST_F(PDFiumEngineTest, InitializeWithRectanglesMultiPagesPdf) {
   NiceMock<MockTestClient> client;
-  EXPECT_CALL(client, DocumentSizeUpdated(pp::Size(343, 1664)));
+  EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(343, 1664)));
 
   std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
       &client, FILE_PATH_LITERAL("rectangles_multi_pages.pdf"));
@@ -55,8 +67,8 @@
   NiceMock<MockTestClient> client;
   {
     InSequence normal_then_append;
-    EXPECT_CALL(client, DocumentSizeUpdated(pp::Size(343, 1664)));
-    EXPECT_CALL(client, DocumentSizeUpdated(pp::Size(276, 1037)));
+    EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(343, 1664)));
+    EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(276, 1037)));
   }
 
   std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
@@ -75,8 +87,8 @@
   NiceMock<MockTestClient> client;
   {
     InSequence normal_then_append;
-    EXPECT_CALL(client, DocumentSizeUpdated(pp::Size(343, 1664)));
-    EXPECT_CALL(client, DocumentSizeUpdated(pp::Size(276, 2425)));
+    EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(343, 1664)));
+    EXPECT_CALL(client, ProposeDocumentLayout(LayoutWithSize(276, 2425)));
   }
 
   std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
diff --git a/pdf/preview_mode_client.cc b/pdf/preview_mode_client.cc
index 01718c37..eff65022 100644
--- a/pdf/preview_mode_client.cc
+++ b/pdf/preview_mode_client.cc
@@ -7,11 +7,16 @@
 #include <stdint.h>
 
 #include "base/logging.h"
+#include "pdf/document_layout.h"
 
 namespace chrome_pdf {
 
 PreviewModeClient::PreviewModeClient(Client* client) : client_(client) {}
 
+void PreviewModeClient::ProposeDocumentLayout(const DocumentLayout& layout) {
+  NOTREACHED();
+}
+
 void PreviewModeClient::Invalidate(const pp::Rect& rect) {
   NOTREACHED();
 }
diff --git a/pdf/preview_mode_client.h b/pdf/preview_mode_client.h
index e620492..e7b2e28 100644
--- a/pdf/preview_mode_client.h
+++ b/pdf/preview_mode_client.h
@@ -27,6 +27,7 @@
   ~PreviewModeClient() override {}
 
   // PDFEngine::Client implementation.
+  void ProposeDocumentLayout(const DocumentLayout& layout) override;
   void Invalidate(const pp::Rect& rect) override;
   void DidScroll(const pp::Point& point) override;
   void ScrollToX(int x_in_screen_coords) override;
diff --git a/pdf/test/test_client.cc b/pdf/test/test_client.cc
index 6c3f79b..44c8dcc 100644
--- a/pdf/test/test_client.cc
+++ b/pdf/test/test_client.cc
@@ -4,12 +4,18 @@
 
 #include "pdf/test/test_client.h"
 
+#include "pdf/document_layout.h"
+
 namespace chrome_pdf {
 
 TestClient::TestClient() = default;
 
 TestClient::~TestClient() = default;
 
+void TestClient::ProposeDocumentLayout(const DocumentLayout& layout) {
+  CHECK(engine());
+}
+
 bool TestClient::Confirm(const std::string& message) {
   return false;
 }
diff --git a/pdf/test/test_client.h b/pdf/test/test_client.h
index f61ade0..4eaf930 100644
--- a/pdf/test/test_client.h
+++ b/pdf/test/test_client.h
@@ -25,6 +25,7 @@
   void set_engine(PDFEngine* engine) { engine_ = engine; }
 
   // PDFEngine::Client:
+  void ProposeDocumentLayout(const DocumentLayout& layout) override;
   bool Confirm(const std::string& message) override;
   std::string Prompt(const std::string& question,
                      const std::string& default_answer) override;
diff --git a/remoting/protocol/host_control_dispatcher.cc b/remoting/protocol/host_control_dispatcher.cc
index 9aa060f..b383308d 100644
--- a/remoting/protocol/host_control_dispatcher.cc
+++ b/remoting/protocol/host_control_dispatcher.cc
@@ -67,6 +67,14 @@
     const CursorShapeInfo& cursor_shape) {
   ControlMessage message;
   message.mutable_cursor_shape()->CopyFrom(cursor_shape);
+  std::size_t message_size = message.ByteSizeLong();
+  if (message_size > max_message_size_) {
+    // Better to drop the event than drop the connection, which can happen if
+    // the browser receives a message larger than it can handle.
+    LOG(WARNING) << "Cursor message dropped because message size "
+                 << message_size << " is larger than " << max_message_size_;
+    return;
+  }
   message_pipe()->Send(&message, base::Closure());
 }
 
diff --git a/services/network/mdns_responder.cc b/services/network/mdns_responder.cc
index 63569ae6..a7a2b71 100644
--- a/services/network/mdns_responder.cc
+++ b/services/network/mdns_responder.cc
@@ -879,15 +879,15 @@
 }
 
 void MdnsResponderManager::CreateMdnsResponder(
-    mojom::MdnsResponderRequest request) {
+    mojo::PendingReceiver<mojom::MdnsResponder> receiver) {
   if (start_result_ == SocketHandlerStartResult::UNSPECIFIED ||
       start_result_ == SocketHandlerStartResult::ALL_FAILURE) {
     LOG(ERROR) << "The mDNS responder manager is not started yet.";
     ReportServiceError(MdnsResponderServiceError::kFailToCreateResponder);
-    request = nullptr;
+    receiver = mojo::NullReceiver();
     return;
   }
-  auto responder = std::make_unique<MdnsResponder>(std::move(request), this);
+  auto responder = std::make_unique<MdnsResponder>(std::move(receiver), this);
   responders_.insert(std::move(responder));
 }
 
@@ -1138,12 +1138,13 @@
   return result;
 }
 
-MdnsResponder::MdnsResponder(mojom::MdnsResponderRequest request,
-                             MdnsResponderManager* manager)
-    : binding_(this, std::move(request)),
+MdnsResponder::MdnsResponder(
+    mojo::PendingReceiver<mojom::MdnsResponder> receiver,
+    MdnsResponderManager* manager)
+    : receiver_(this, std::move(receiver)),
       manager_(manager),
       name_generator_(manager_->name_generator()) {
-  binding_.set_connection_error_handler(
+  receiver_.set_disconnect_handler(
       base::BindOnce(&MdnsResponderManager::OnMojoConnectionError,
                      base::Unretained(manager_), this));
 }
@@ -1163,7 +1164,7 @@
   if (!address.IsValid()) {
     LOG(ERROR) << "Invalid IP address to create a name for";
     ReportServiceError(MdnsResponderServiceError::kInvalidIpToRegisterName);
-    binding_.Close();
+    receiver_.reset();
     manager_->OnMojoConnectionError(this);
     return;
   }
diff --git a/services/network/mdns_responder.h b/services/network/mdns_responder.h
index a7e6f90..db0a5a8 100644
--- a/services/network/mdns_responder.h
+++ b/services/network/mdns_responder.h
@@ -16,7 +16,8 @@
 #include "base/containers/unique_ptr_adapters.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "net/dns/dns_query.h"
 #include "net/dns/dns_response.h"
 #include "services/network/public/mojom/mdns_responder.mojom.h"
@@ -143,9 +144,9 @@
   explicit MdnsResponderManager(net::MDnsSocketFactory* socket_factory);
   ~MdnsResponderManager();
 
-  // Creates an instance of MdnsResponder for the binding request from an
-  // InterfacePtr.
-  void CreateMdnsResponder(mojom::MdnsResponderRequest request);
+  // Creates an instance of MdnsResponder for the receiver.
+  void CreateMdnsResponder(
+      mojo::PendingReceiver<mojom::MdnsResponder> receiver);
   // The methods below are used to bookkeep names owned by responders, and
   // also for the extra uniqueness validation of these names. By default,
   // we use the RandomUuidNameGenerator (see mdns_responder.cc), which
@@ -262,7 +263,7 @@
 class COMPONENT_EXPORT(NETWORK_SERVICE) MdnsResponder
     : public mojom::MdnsResponder {
  public:
-  MdnsResponder(mojom::MdnsResponderRequest request,
+  MdnsResponder(mojo::PendingReceiver<mojom::MdnsResponder> receiver,
                 MdnsResponderManager* manager);
   // When destroyed, clears all existing name-address associations owned by this
   // responder in the local network by sending out goodbye packets. See
@@ -312,7 +313,7 @@
   std::map<std::string, net::IPAddress>::iterator FindNameCreatedForAddress(
       const net::IPAddress& address);
 
-  mojo::Binding<network::mojom::MdnsResponder> binding_;
+  mojo::Receiver<network::mojom::MdnsResponder> receiver_;
   // A back pointer to the responder manager that owns this responder. The
   // responder should be destroyed before |manager_| becomes invalid or a weak
   // reference should be used to access the manager when there is no such
diff --git a/services/network/mdns_responder_unittest.cc b/services/network/mdns_responder_unittest.cc
index a963224c..591819f 100644
--- a/services/network/mdns_responder_unittest.cc
+++ b/services/network/mdns_responder_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "mojo/public/cpp/bindings/connector.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/ip_address.h"
 #include "net/base/net_errors.h"
 #include "net/dns/dns_query.h"
@@ -423,14 +424,12 @@
   }
 
   void CreateMdnsResponders() {
-    auto request1 = mojo::MakeRequest(&client_[0]);
-    client_[0].set_connection_error_handler(base::BindOnce(
+    host_manager_->CreateMdnsResponder(client_[0].BindNewPipeAndPassReceiver());
+    client_[0].set_disconnect_handler(base::BindOnce(
         &MdnsResponderTest::OnMojoConnectionError, base::Unretained(this), 0));
-    host_manager_->CreateMdnsResponder(std::move(request1));
-    auto request2 = mojo::MakeRequest(&client_[1]);
-    client_[1].set_connection_error_handler(base::BindOnce(
+    host_manager_->CreateMdnsResponder(client_[1].BindNewPipeAndPassReceiver());
+    client_[1].set_disconnect_handler(base::BindOnce(
         &MdnsResponderTest::OnMojoConnectionError, base::Unretained(this), 1));
-    host_manager_->CreateMdnsResponder(std::move(request2));
   }
 
   // The following method is synchronous for testing by waiting on running the
@@ -493,7 +492,7 @@
   // of time and avoid any actual sleeps.
   NiceMock<net::MockMDnsSocketFactory> socket_factory_;
   NiceMock<MockFailingMdnsSocketFactory> failing_socket_factory_;
-  mojom::MdnsResponderPtr client_[2];
+  mojo::Remote<mojom::MdnsResponder> client_[2];
   std::unique_ptr<MdnsResponderManager> host_manager_;
   std::string last_name_created_;
 };
diff --git a/services/network/network_change_manager.cc b/services/network/network_change_manager.cc
index 4b9ab32a..a1529f5 100644
--- a/services/network/network_change_manager.cc
+++ b/services/network/network_change_manager.cc
@@ -26,9 +26,9 @@
   net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
 }
 
-void NetworkChangeManager::AddRequest(
-    mojom::NetworkChangeManagerRequest request) {
-  bindings_.AddBinding(this, std::move(request));
+void NetworkChangeManager::AddReceiver(
+    mojo::PendingReceiver<mojom::NetworkChangeManager> receiver) {
+  receivers_.Add(this, std::move(receiver));
 }
 
 void NetworkChangeManager::RequestNotifications(
diff --git a/services/network/network_change_manager.h b/services/network/network_change_manager.h
index bf975f21..4234842 100644
--- a/services/network/network_change_manager.h
+++ b/services/network/network_change_manager.h
@@ -12,7 +12,7 @@
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "build/build_config.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "net/base/network_change_notifier.h"
 #include "services/network/public/mojom/network_change_manager.mojom.h"
 
@@ -33,9 +33,9 @@
 
   ~NetworkChangeManager() override;
 
-  // Binds a NetworkChangeManager request to this object. Mojo messages
+  // Binds a NetworkChangeManager receiver to this object. Mojo messages
   // coming through the associated pipe will be served by this object.
-  void AddRequest(mojom::NetworkChangeManagerRequest request);
+  void AddReceiver(mojo::PendingReceiver<mojom::NetworkChangeManager> receiver);
 
   // mojom::NetworkChangeManager implementation:
   void RequestNotifications(
@@ -62,7 +62,7 @@
       net::NetworkChangeNotifier::ConnectionType type) override;
 
   std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier_;
-  mojo::BindingSet<mojom::NetworkChangeManager> bindings_;
+  mojo::ReceiverSet<mojom::NetworkChangeManager> receivers_;
   std::vector<mojom::NetworkChangeManagerClientPtr> clients_;
   mojom::ConnectionType connection_type_;
 
diff --git a/services/network/network_change_manager_unittest.cc b/services/network/network_change_manager_unittest.cc
index 723d2b7..7783893 100644
--- a/services/network/network_change_manager_unittest.cc
+++ b/services/network/network_change_manager_unittest.cc
@@ -10,6 +10,8 @@
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/network_change_notifier.h"
 #include "services/network/public/mojom/network_change_manager.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -38,15 +40,14 @@
         notification_type_to_wait_(NONE),
         connection_type_(mojom::ConnectionType::CONNECTION_UNKNOWN),
         binding_(this) {
-    mojom::NetworkChangeManagerPtr manager_ptr;
-    mojom::NetworkChangeManagerRequest request(mojo::MakeRequest(&manager_ptr));
-    network_change_manager->AddRequest(std::move(request));
+    mojo::Remote<mojom::NetworkChangeManager> manager;
+    network_change_manager->AddReceiver(manager.BindNewPipeAndPassReceiver());
 
     mojom::NetworkChangeManagerClientPtr client_ptr;
     mojom::NetworkChangeManagerClientRequest client_request(
         mojo::MakeRequest(&client_ptr));
     binding_.Bind(std::move(client_request));
-    manager_ptr->RequestNotifications(std::move(client_ptr));
+    manager->RequestNotifications(std::move(client_ptr));
   }
 
   ~TestNetworkChangeManagerClient() override {}
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 1e57454..8f45d4d 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -1411,12 +1411,12 @@
 }
 
 void NetworkContext::CreateMdnsResponder(
-    mojom::MdnsResponderRequest responder_request) {
+    mojo::PendingReceiver<mojom::MdnsResponder> responder_receiver) {
 #if BUILDFLAG(ENABLE_MDNS)
   if (!mdns_responder_manager_)
     mdns_responder_manager_ = std::make_unique<MdnsResponderManager>();
 
-  mdns_responder_manager_->CreateMdnsResponder(std::move(responder_request));
+  mdns_responder_manager_->CreateMdnsResponder(std::move(responder_receiver));
 #else
   NOTREACHED();
 #endif  // BUILDFLAG(ENABLE_MDNS)
diff --git a/services/network/network_context.h b/services/network/network_context.h
index 8a5a74b..62ba8f8 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -330,7 +330,7 @@
       mojom::P2PTrustedSocketManagerRequest trusted_socket_manager,
       mojom::P2PSocketManagerRequest socket_manager_request) override;
   void CreateMdnsResponder(
-      mojom::MdnsResponderRequest responder_request) override;
+      mojo::PendingReceiver<mojom::MdnsResponder> responder_receiver) override;
   void QueueReport(const std::string& type,
                    const std::string& group,
                    const GURL& url,
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index e6e0c74..ad82512 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -557,8 +557,8 @@
 }
 
 void NetworkService::GetNetworkChangeManager(
-    mojom::NetworkChangeManagerRequest request) {
-  network_change_manager_->AddRequest(std::move(request));
+    mojo::PendingReceiver<mojom::NetworkChangeManager> receiver) {
+  network_change_manager_->AddReceiver(std::move(receiver));
 }
 
 void NetworkService::GetNetworkQualityEstimatorManager(
diff --git a/services/network/network_service.h b/services/network/network_service.h
index 1e839ab..e760aad 100644
--- a/services/network/network_service.h
+++ b/services/network/network_service.h
@@ -140,7 +140,7 @@
                            const std::vector<url::Origin>& origins) override;
   void SetMaxConnectionsPerProxy(int32_t max_connections) override;
   void GetNetworkChangeManager(
-      mojom::NetworkChangeManagerRequest request) override;
+      mojo::PendingReceiver<mojom::NetworkChangeManager> receiver) override;
   void GetNetworkQualityEstimatorManager(
       mojo::PendingReceiver<mojom::NetworkQualityEstimatorManager> receiver)
       override;
diff --git a/services/network/network_service_unittest.cc b/services/network/network_service_unittest.cc
index 754acca..050cfe3b 100644
--- a/services/network/network_service_unittest.cc
+++ b/services/network/network_service_unittest.cc
@@ -1324,15 +1324,15 @@
       mojom::NetworkService* network_service)
       : connection_type_(mojom::ConnectionType::CONNECTION_UNKNOWN),
         binding_(this) {
-    mojom::NetworkChangeManagerPtr manager_ptr;
-    mojom::NetworkChangeManagerRequest request(mojo::MakeRequest(&manager_ptr));
-    network_service->GetNetworkChangeManager(std::move(request));
+    mojo::Remote<mojom::NetworkChangeManager> manager_remote;
+    network_service->GetNetworkChangeManager(
+        manager_remote.BindNewPipeAndPassReceiver());
 
     mojom::NetworkChangeManagerClientPtr client_ptr;
     mojom::NetworkChangeManagerClientRequest client_request(
         mojo::MakeRequest(&client_ptr));
     binding_.Bind(std::move(client_request));
-    manager_ptr->RequestNotifications(std::move(client_ptr));
+    manager_remote->RequestNotifications(std::move(client_ptr));
   }
 
   ~TestNetworkChangeManagerClient() override {}
diff --git a/services/network/public/cpp/network_connection_tracker.h b/services/network/public/cpp/network_connection_tracker.h
index e338ac5..c564a083d 100644
--- a/services/network/public/cpp/network_connection_tracker.h
+++ b/services/network/public/cpp/network_connection_tracker.h
@@ -39,7 +39,7 @@
     : public network::mojom::NetworkChangeManagerClient {
  public:
   using BindingCallback = base::RepeatingCallback<void(
-      network::mojom::NetworkChangeManagerRequest)>;
+      mojo::PendingReceiver<network::mojom::NetworkChangeManager>)>;
   using ConnectionTypeCallback =
       base::OnceCallback<void(network::mojom::ConnectionType)>;
 
diff --git a/services/network/public/cpp/network_connection_tracker_unittest.cc b/services/network/public/cpp/network_connection_tracker_unittest.cc
index f6d6b488..101b26d 100644
--- a/services/network/public/cpp/network_connection_tracker_unittest.cc
+++ b/services/network/public/cpp/network_connection_tracker_unittest.cc
@@ -186,12 +186,13 @@
         NetworkService::Create(std::move(network_service_request),
                                /*netlog=*/nullptr);
     tracker_ = std::make_unique<NetworkConnectionTracker>(base::BindRepeating(
-        &NetworkConnectionTrackerTest::BindRequest, base::Unretained(this)));
+        &NetworkConnectionTrackerTest::BindReceiver, base::Unretained(this)));
     observer_ = std::make_unique<TestNetworkConnectionObserver>(tracker_.get());
   }
 
-  void BindRequest(network::mojom::NetworkChangeManagerRequest request) {
-    network_service_->GetNetworkChangeManager(std::move(request));
+  void BindReceiver(
+      mojo::PendingReceiver<network::mojom::NetworkChangeManager> receiver) {
+    network_service_->GetNetworkChangeManager(std::move(receiver));
   }
 
   NetworkConnectionTracker* network_connection_tracker() {
@@ -328,8 +329,8 @@
       mojo::MakeRequest(network_service_ptr);
   NetworkConnectionTracker::BindingCallback callback = base::BindRepeating(
       [](network::mojom::NetworkService* service,
-         network::mojom::NetworkChangeManagerRequest request) {
-        return service->GetNetworkChangeManager(std::move(request));
+         mojo::PendingReceiver<network::mojom::NetworkChangeManager> receiver) {
+        return service->GetNetworkChangeManager(std::move(receiver));
       },
       base::Unretained(network_service_ptr->get()));
 
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index 94d19db..93dfcca54 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -1069,7 +1069,7 @@
                          P2PSocketManager& socket_manager);
 
   // Creates an MdnsResponder instance.
-  CreateMdnsResponder(MdnsResponder& responder_request);
+  CreateMdnsResponder(pending_receiver<MdnsResponder> responder_receiver);
 
   // Resolves the given hostname (or IP address literal). See documentation at
   // HostResolver::ResolveHost.
diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom
index b810da84..55077a48 100644
--- a/services/network/public/mojom/network_service.mojom
+++ b/services/network/public/mojom/network_service.mojom
@@ -255,7 +255,7 @@
 
   // Gets the NetworkChangeManager.
   GetNetworkChangeManager(
-      NetworkChangeManager& network_change_manager);
+      pending_receiver<NetworkChangeManager> network_change_manager);
 
   // Gets the NetworkQualityEstimatorManager.
   GetNetworkQualityEstimatorManager(
diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h
index 252df767..9a4424f 100644
--- a/services/network/test/test_network_context.h
+++ b/services/network/test/test_network_context.h
@@ -207,7 +207,8 @@
       mojom::P2PTrustedSocketManagerRequest trusted_socket_manager,
       mojom::P2PSocketManagerRequest socket_manager_request) override {}
   void CreateMdnsResponder(
-      mojom::MdnsResponderRequest responder_request) override {}
+      mojo::PendingReceiver<mojom::MdnsResponder> responder_receiver) override {
+  }
   void ResetURLLoaderFactories() override {}
   void ForceReloadProxyConfig(
       ForceReloadProxyConfigCallback callback) override {}
diff --git a/services/tracing/perfetto/privacy_filtered_fields-inl.h b/services/tracing/perfetto/privacy_filtered_fields-inl.h
index d59da43..ad0ee3b 100644
--- a/services/tracing/perfetto/privacy_filtered_fields-inl.h
+++ b/services/tracing/perfetto/privacy_filtered_fields-inl.h
@@ -137,14 +137,15 @@
 constexpr int kStreamingProfilePacketIndices[] = {1, 2, -1};
 constexpr MessageInfo kStreamingProfilePacket = {kStreamingProfilePacketIndices,
                                                  nullptr};
-// EDIT: Contains field numbers: {3} which are not autogenerated.
+// EDIT: Contains field numbers: {3, 8} which are not autogenerated.
 
 // Proto Message: TracePacket
-constexpr int kTracePacketIndices[] = {3,  10, 11, 12, 35, 36, 41,
-                                       42, 43, 44, 51, 54, -1};
+constexpr int kTracePacketIndices[] = {3,  8,  10, 11, 12, 35, 36,
+                                       41, 42, 43, 44, 51, 54, -1};
 constexpr MessageInfo const* kTracePacketComplexMessages[] = {
     nullptr,
     nullptr,
+    nullptr,
     &kTrackEvent,
     &kInternedData,
     &kTraceStats,
diff --git a/services/tracing/perfetto/privacy_filtering_check.cc b/services/tracing/perfetto/privacy_filtering_check.cc
index 7dfc5a3..4ca40f4 100644
--- a/services/tracing/perfetto/privacy_filtering_check.cc
+++ b/services/tracing/perfetto/privacy_filtering_check.cc
@@ -45,7 +45,7 @@
         error << " : " << a;
       }
       error << " : " << f.id();
-      LOG(DFATAL) << error.rdbuf();
+      DCHECK(false) << error.rdbuf();
       continue;
     }
     if (root->sub_messages && root->sub_messages[index] != nullptr) {
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
index b456e4d..e20abb58 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
@@ -21,6 +21,7 @@
 #include "base/no_destructor.h"
 #include "base/pickle.h"
 #include "base/sequence_checker.h"
+#include "base/strings/pattern.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task/common/scoped_defer_task_posting.h"
 #include "base/threading/sequenced_task_runner_handle.h"
@@ -44,6 +45,7 @@
 #include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_metadata.pbzero.h"
 #include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
+#include "third_party/perfetto/protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
 
 #if defined(OS_ANDROID)
 #include "base/android/build_info.h"
@@ -52,13 +54,34 @@
 using TraceLog = base::trace_event::TraceLog;
 using TraceEvent = base::trace_event::TraceEvent;
 using TraceConfig = base::trace_event::TraceConfig;
+using TracePacketHandle = perfetto::TraceWriter::TracePacketHandle;
 using perfetto::protos::pbzero::ChromeMetadataPacket;
+using perfetto::protos::pbzero::ProcessDescriptor;
 
 namespace tracing {
 namespace {
 
 TraceEventMetadataSource* g_trace_event_metadata_source_for_testing = nullptr;
 
+ProcessDescriptor::ChromeProcessType GetProcessType(const std::string& name) {
+  if (name == "Browser") {
+    return ProcessDescriptor::PROCESS_BROWSER;
+  } else if (name == "Renderer") {
+    return ProcessDescriptor::PROCESS_RENDERER;
+  } else if (name == "GPU Process") {
+    return ProcessDescriptor::PROCESS_GPU;
+  } else if (base::MatchPattern(name, "Service:*")) {
+    return ProcessDescriptor::PROCESS_UTILITY;
+  } else if (name == "HeadlessBrowser") {
+    return ProcessDescriptor::PROCESS_BROWSER;
+  } else if (name == "PPAPI Process") {
+    return ProcessDescriptor::PROCESS_PPAPI_PLUGIN;
+  } else if (name == "PPAPI Broker Process") {
+    return ProcessDescriptor::PROCESS_PPAPI_BROKER;
+  }
+  return ProcessDescriptor::PROCESS_UNSPECIFIED;
+}
+
 void WriteMetadataProto(ChromeMetadataPacket* metadata_proto,
                         bool privacy_filtering_enabled) {
 #if defined(OS_ANDROID) && defined(OFFICIAL_BUILD)
@@ -142,7 +165,7 @@
   for (auto& generator : generator_functions_) {
     generator.Run(chrome_metadata, privacy_filtering_enabled_);
   }
-  trace_packet = perfetto::TraceWriter::TracePacketHandle();
+  trace_packet = TracePacketHandle();
 
   // We already have the |trace_writer| and |trace_packet|, so regardless of if
   // we need to return due to privacy we need to null out the |producer_| to
@@ -305,7 +328,11 @@
     privacy_filtering_enabled_ = privacy_filtering_enabled;
     startup_writer_registry_ =
         std::make_unique<perfetto::StartupTraceWriterRegistry>();
+
+    DCHECK(!trace_writer_);
+    trace_writer_ = CreateTraceWriterLocked();
   }
+  EmitProcessDescriptor();
   RegisterWithTraceLog();
   if (base::SequencedTaskRunnerHandle::IsSet()) {
     OnTaskSchedulerAvailable();
@@ -337,6 +364,7 @@
   }
   DCHECK_CALLED_ON_VALID_SEQUENCE(perfetto_sequence_checker_);
   std::unique_ptr<perfetto::StartupTraceWriterRegistry> registry;
+  std::unique_ptr<perfetto::StartupTraceWriter> trace_writer;
   {
     base::AutoLock lock(lock_);
     if (!startup_writer_registry_) {
@@ -346,6 +374,10 @@
     // created.
     startup_writer_registry_.reset();
     flushing_trace_log_ = true;
+    trace_writer = std::move(trace_writer_);
+  }
+  if (trace_writer) {
+    ReturnTraceWriter(std::move(trace_writer));
   }
   auto* trace_log = base::trace_event::TraceLog::GetInstance();
   trace_log->SetDisabled();
@@ -424,6 +456,10 @@
 
     // Protected by |lock_| for CreateThreadLocalEventSink().
     session_id_.fetch_add(1u, std::memory_order_relaxed);
+
+    if (!trace_writer_) {
+      trace_writer_ = CreateTraceWriterLocked();
+    }
   }
 
   if (unbound_writer_registry) {
@@ -434,6 +470,7 @@
     producer->BindStartupTraceWriterRegistry(
         std::move(unbound_writer_registry), data_source_config.target_buffer());
   } else {
+    EmitProcessDescriptor();
     RegisterWithTraceLog();
   }
 
@@ -469,6 +506,7 @@
     TraceLog::GetInstance()->SetDisabled();
   }
 
+  std::unique_ptr<perfetto::StartupTraceWriter> trace_writer;
   {
     base::AutoLock lock(lock_);
     if (flush_complete_task_) {
@@ -487,6 +525,10 @@
     producer_ = nullptr;
     target_buffer_ = 0;
     flushing_trace_log_ = was_enabled;
+    trace_writer = std::move(trace_writer_);
+  }
+  if (trace_writer) {
+    ReturnTraceWriter(std::move(trace_writer));
   }
 
   if (was_enabled) {
@@ -556,21 +598,22 @@
 
 void TraceEventDataSource::ClearIncrementalState() {
   TrackEventThreadLocalEventSink::ClearIncrementalState();
+  EmitProcessDescriptor();
 }
 
-ThreadLocalEventSink* TraceEventDataSource::CreateThreadLocalEventSink(
-    bool thread_will_flush) {
+std::unique_ptr<perfetto::StartupTraceWriter>
+TraceEventDataSource::CreateTraceWriterLocked() {
+  lock_.AssertAcquired();
+
   // The call to CreateTraceWriter() below posts a task which is not allowed
   // while holding |lock_|. Since we have to call it while holding |lock_|, we
   // defer the task posting until after the lock is released.
   base::ScopedDeferTaskPosting defer_task_posting;
 
-  base::AutoLock lock(lock_);
   // |startup_writer_registry_| only exists during startup tracing before we
   // connect to the service. |producer_| is reset when tracing is
   // stopped.
   std::unique_ptr<perfetto::StartupTraceWriter> trace_writer;
-  uint32_t session_id = session_id_.load(std::memory_order_relaxed);
   if (startup_writer_registry_) {
     // Chromium uses BufferExhaustedPolicy::kDrop to avoid stalling trace
     // writers when the chunks in the SMB are exhausted. Stalling could
@@ -584,7 +627,15 @@
     trace_writer = std::make_unique<perfetto::StartupTraceWriter>(
         producer_->CreateTraceWriter(target_buffer_));
   }
+  return trace_writer;
+}
 
+ThreadLocalEventSink* TraceEventDataSource::CreateThreadLocalEventSink(
+    bool thread_will_flush) {
+  base::AutoLock lock(lock_);
+  uint32_t session_id = session_id_.load(std::memory_order_relaxed);
+
+  auto trace_writer = CreateTraceWriterLocked();
   if (!trace_writer) {
     return nullptr;
   }
@@ -733,4 +784,42 @@
           trace_writer_raw));
 }
 
+void TraceEventDataSource::EmitProcessDescriptor() {
+  // TODO(ssid): Emitting process descriptor for dead process will cause
+  // telemetry failures due to multiple main threads with same pid. So, fix the
+  // json exporter.
+  if (!privacy_filtering_enabled_) {
+    return;
+  }
+  // Initialize here since the constructor can be called before the process id
+  // in trace log is set.
+  if (process_id_ == base::kNullProcessId) {
+    process_name_ = TraceLog::GetInstance()->process_name();
+    process_id_ = TraceLog::GetInstance()->process_id();
+  }
+  if (process_id_ == base::kNullProcessId) {
+    // Do not emit descriptor without process id.
+    return;
+  }
+
+  TracePacketHandle trace_packet;
+  {
+    base::AutoLock lock(lock_);
+    if (!trace_writer_) {
+      return;
+    }
+    trace_packet = trace_writer_->NewTracePacket();
+  }
+  trace_packet->set_incremental_state_cleared(true);
+  trace_packet->set_timestamp(
+      TRACE_TIME_TICKS_NOW().since_origin().InNanoseconds());
+  auto process_type = GetProcessType(TraceLog::GetInstance()->process_name());
+  ProcessDescriptor* process_desc = trace_packet->set_process_descriptor();
+  process_desc->set_pid(process_id_);
+  process_desc->set_chrome_process_type(process_type);
+
+  trace_packet = TracePacketHandle();
+  trace_writer_->Flush();
+}
+
 }  // namespace tracing
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.h b/services/tracing/public/cpp/perfetto/trace_event_data_source.h
index e082ce23..ddc9c57a6 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source.h
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.h
@@ -157,6 +157,7 @@
   void RegisterWithTraceLog();
   void UnregisterFromTraceLog();
 
+  std::unique_ptr<perfetto::StartupTraceWriter> CreateTraceWriterLocked();
   ThreadLocalEventSink* CreateThreadLocalEventSink(bool thread_will_flush);
 
   // Callback from TraceLog, can be called from any thread.
@@ -176,6 +177,7 @@
   void LogHistograms();
   // Logs a given histogram in traces.
   void LogHistogram(base::HistogramBase* histogram);
+  void EmitProcessDescriptor();
 
   bool disable_interning_ = false;
   base::OnceClosure stop_complete_callback_;
@@ -198,11 +200,14 @@
   // SetupStartupTracing() is called.
   std::unique_ptr<perfetto::StartupTraceWriterRegistry>
       startup_writer_registry_;
+  std::unique_ptr<perfetto::StartupTraceWriter> trace_writer_;
   base::OneShotTimer startup_tracing_timer_;
   bool flushing_trace_log_ = false;
   base::OnceClosure flush_complete_task_;
   std::vector<std::string> histograms_;
   bool privacy_filtering_enabled_ = false;
+  std::string process_name_;
+  int process_id_ = base::kNullProcessId;
   SEQUENCE_CHECKER(perfetto_sequence_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(TraceEventDataSource);
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
index 6cd41aa..5fe111e9 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
@@ -36,6 +36,7 @@
 #include "third_party/perfetto/include/perfetto/protozero/scattered_stream_writer.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h"
 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
+#include "third_party/perfetto/protos/perfetto/trace/track_event/process_descriptor.pb.h"
 #include "third_party/perfetto/protos/perfetto/trace/track_event/thread_descriptor.pb.h"
 
 using TrackEvent = perfetto::protos::TrackEvent;
@@ -298,6 +299,13 @@
     EXPECT_TRUE(packet->incremental_state_cleared());
   }
 
+  void ExpectProcessDescriptor(const perfetto::protos::TracePacket* packet) {
+    EXPECT_TRUE(packet->has_process_descriptor());
+    EXPECT_NE(packet->process_descriptor().pid(), 0);
+    EXPECT_EQ(packet->process_descriptor().chrome_process_type(),
+              perfetto::protos::ProcessDescriptor::PROCESS_UNSPECIFIED);
+  }
+
   void ExpectTraceEvent(const perfetto::protos::TracePacket* packet,
                         uint32_t category_iid,
                         uint32_t name_iid,
@@ -977,12 +985,15 @@
   CreateTraceEventDataSource(/* privacy_filtering_enabled =*/true);
   TRACE_EVENT_BEGIN0(kCategoryGroup, "bar");
 
-  EXPECT_EQ(producer_client()->GetFinalizedPacketCount(), 2u);
+  EXPECT_EQ(producer_client()->GetFinalizedPacketCount(), 3u);
 
-  auto* td_packet = producer_client()->GetFinalizedPacket();
+  auto* pd_packet = producer_client()->GetFinalizedPacket(0);
+  ExpectProcessDescriptor(pd_packet);
+
+  auto* td_packet = producer_client()->GetFinalizedPacket(1);
   ExpectThreadDescriptor(td_packet, 1u, 1u, /*filtering_enabled=*/true);
 
-  auto* e_packet = producer_client()->GetFinalizedPacket(1);
+  auto* e_packet = producer_client()->GetFinalizedPacket(2);
   ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
                    TRACE_EVENT_PHASE_BEGIN);
 
@@ -996,8 +1007,8 @@
   TRACE_EVENT_INSTANT2(kCategoryGroup, "bar", TRACE_EVENT_SCOPE_THREAD, "foo",
                        42, "bar", "string_val");
 
-  EXPECT_EQ(producer_client()->GetFinalizedPacketCount(), 2u);
-  auto* e_packet = producer_client()->GetFinalizedPacket(1);
+  EXPECT_EQ(producer_client()->GetFinalizedPacketCount(), 3u);
+  auto* e_packet = producer_client()->GetFinalizedPacket(2);
   ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
                    TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
 
@@ -1019,8 +1030,8 @@
                            TRACE_EVENT_FLAG_JAVA_STRING_LITERALS,
                        "arg1_name", "arg1_val", "arg2_name", "arg2_val");
 
-  EXPECT_EQ(producer_client()->GetFinalizedPacketCount(), 3u);
-  auto* e_packet = producer_client()->GetFinalizedPacket(1);
+  EXPECT_EQ(producer_client()->GetFinalizedPacketCount(), 4u);
+  auto* e_packet = producer_client()->GetFinalizedPacket(2);
   ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
                    TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
 
@@ -1031,7 +1042,7 @@
   ExpectEventNames(e_packet, {{1u, "PRIVACY_FILTERED"}});
   ExpectDebugAnnotationNames(e_packet, {});
 
-  e_packet = producer_client()->GetFinalizedPacket(2);
+  e_packet = producer_client()->GetFinalizedPacket(3);
   ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/2u,
                    TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
 
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index a5701f1..063870e 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -1796,28 +1796,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chrome.tests",
-              "ssd": "0"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index f2bd16a..e269899 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -1932,65 +1932,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices",
-          "--avd-name=20190903T160000Z_android_23_google_apis_x86",
-          "--emulator-home=../../.android"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "nonperfetto_content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "chromium/third_party/android_sdk/public/avds/android-23/google_apis/x86",
-              "location": ".android",
-              "revision": "cfoclSzHrFOS_AcwBL09RQ5PvJByQHu3MX6EbC5aWZIC"
-            },
-            {
-              "cipd_package": "chromium/third_party/android_sdk/public/emulator",
-              "location": "third_party/android_sdk/public",
-              "revision": "f4WdgkPvDdVCE8zBWPzcSIj4N9WFhKp3CSKDWylXuLEC"
-            },
-            {
-              "cipd_package": "chromium/third_party/android_sdk/public/system-images/android-23/google_apis/x86",
-              "location": "third_party/android_sdk/public",
-              "revision": "npuCAATVbhmywZwGhI3tMoECTrBBzzyJLpjAPXqtmYYC"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "device_os": null,
-              "device_type": null,
-              "os": "Ubuntu-16.04"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
           "--avd-name=20190903T160000Z_android_23_google_apis_x86",
@@ -5942,65 +5883,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices",
-          "--avd-name=20190716T160000Z_android_28_google_apis_x86",
-          "--emulator-home=../../.android"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "nonperfetto_content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "chromium/third_party/android_sdk/public/avds/android-28/google_apis/x86",
-              "location": ".android",
-              "revision": "J5MFTg7OhekLKsOX7bmkVTSCDzohydCvXrn4sJzy0xsC"
-            },
-            {
-              "cipd_package": "chromium/third_party/android_sdk/public/emulator",
-              "location": "third_party/android_sdk/public",
-              "revision": "xhyuoquVvBTcJelgRjMKZeoBVSQRjB7pLVJPt5C9saIC"
-            },
-            {
-              "cipd_package": "chromium/third_party/android_sdk/public/system-images/android-28/google_apis/x86",
-              "location": "third_party/android_sdk/public",
-              "revision": "LDa0XkTjgGYx7Amzg5qjIRgCfc4F_pq7rKMJVdACYx8C"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "device_os": null,
-              "device_type": null,
-              "os": "Ubuntu-16.04"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
           "--avd-name=20190716T160000Z_android_28_google_apis_x86",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 7a80cb5..71e20a0 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -3146,53 +3146,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "nonperfetto_content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84P",
-              "device_os_type": "userdebug",
-              "device_type": "hammerhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -6003,54 +5956,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "nonperfetto_content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84Z",
-              "device_os_type": "userdebug",
-              "device_type": "flo",
-              "os": "Android"
-            }
-          ],
-          "expiration": 10800,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -9182,54 +9087,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "nonperfetto_content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "LMY48I",
-              "device_os_type": "userdebug",
-              "device_type": "hammerhead",
-              "os": "Android"
-            }
-          ],
-          "expiration": 10800,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -12313,54 +12170,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "nonperfetto_content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "LMY49B",
-              "device_os_type": "userdebug",
-              "device_type": "flo",
-              "os": "Android"
-            }
-          ],
-          "expiration": 10800,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -15511,53 +15320,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "nonperfetto_content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -18596,54 +18358,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "nonperfetto_content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MRA58Z",
-              "device_os_type": "userdebug",
-              "device_type": "flo",
-              "os": "Android"
-            }
-          ],
-          "expiration": 10800,
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -23607,7 +23321,7 @@
               "name": "shard #${SHARD_INDEX} logcats"
             }
           ],
-          "shards": 12
+          "shards": 20
         },
         "test": "chrome_public_test_apk"
       },
@@ -23832,54 +23546,7 @@
               "name": "shard #${SHARD_INDEX} logcats"
             }
           ],
-          "shards": 9
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "nonperfetto_content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84P",
-              "device_os_type": "userdebug",
-              "device_type": "hammerhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
+          "shards": 15
         },
         "test": "content_browsertests"
       },
@@ -25712,123 +25379,6 @@
           ]
         }
       }
-    ],
-    "junit_tests": [
-      {
-        "name": "android_webview_junit_tests",
-        "swarming": {},
-        "test": "android_webview_junit_tests"
-      },
-      {
-        "name": "base_junit_tests",
-        "swarming": {},
-        "test": "base_junit_tests"
-      },
-      {
-        "name": "chrome_junit_tests",
-        "swarming": {},
-        "test": "chrome_junit_tests"
-      },
-      {
-        "name": "components_background_task_scheduler_junit_tests",
-        "swarming": {},
-        "test": "components_background_task_scheduler_junit_tests"
-      },
-      {
-        "name": "components_gcm_driver_junit_tests",
-        "swarming": {},
-        "test": "components_gcm_driver_junit_tests"
-      },
-      {
-        "name": "components_invalidation_impl_junit_tests",
-        "swarming": {},
-        "test": "components_invalidation_impl_junit_tests"
-      },
-      {
-        "name": "components_policy_junit_tests",
-        "swarming": {},
-        "test": "components_policy_junit_tests"
-      },
-      {
-        "name": "components_signin_junit_tests",
-        "swarming": {},
-        "test": "components_signin_junit_tests"
-      },
-      {
-        "name": "components_variations_junit_tests",
-        "swarming": {},
-        "test": "components_variations_junit_tests"
-      },
-      {
-        "name": "content_junit_tests",
-        "swarming": {},
-        "test": "content_junit_tests"
-      },
-      {
-        "name": "device_junit_tests",
-        "swarming": {},
-        "test": "device_junit_tests"
-      },
-      {
-        "name": "junit_unit_tests",
-        "swarming": {},
-        "test": "junit_unit_tests"
-      },
-      {
-        "name": "keyboard_accessory_junit_tests",
-        "swarming": {},
-        "test": "keyboard_accessory_junit_tests"
-      },
-      {
-        "name": "media_base_junit_tests",
-        "swarming": {},
-        "test": "media_base_junit_tests"
-      },
-      {
-        "name": "media_router_junit_tests",
-        "swarming": {},
-        "test": "media_router_junit_tests"
-      },
-      {
-        "name": "module_installer_junit_tests",
-        "swarming": {},
-        "test": "module_installer_junit_tests"
-      },
-      {
-        "name": "net_junit_tests",
-        "swarming": {},
-        "test": "net_junit_tests"
-      },
-      {
-        "name": "service_junit_tests",
-        "swarming": {},
-        "test": "service_junit_tests"
-      },
-      {
-        "name": "touch_to_fill_junit_tests",
-        "swarming": {},
-        "test": "touch_to_fill_junit_tests"
-      },
-      {
-        "name": "ui_junit_tests",
-        "swarming": {},
-        "test": "ui_junit_tests"
-      },
-      {
-        "name": "webapk_client_junit_tests",
-        "swarming": {},
-        "test": "webapk_client_junit_tests"
-      },
-      {
-        "name": "webapk_shell_apk_h2o_junit_tests",
-        "swarming": {},
-        "test": "webapk_shell_apk_h2o_junit_tests"
-      },
-      {
-        "name": "webapk_shell_apk_junit_tests",
-        "swarming": {},
-        "test": "webapk_shell_apk_junit_tests"
-      }
     ]
   },
   "android-marshmallow-arm64-rel": {
@@ -26948,53 +26498,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "nonperfetto_content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -28939,6 +28442,123 @@
           "shards": 15
         }
       }
+    ],
+    "junit_tests": [
+      {
+        "name": "android_webview_junit_tests",
+        "swarming": {},
+        "test": "android_webview_junit_tests"
+      },
+      {
+        "name": "base_junit_tests",
+        "swarming": {},
+        "test": "base_junit_tests"
+      },
+      {
+        "name": "chrome_junit_tests",
+        "swarming": {},
+        "test": "chrome_junit_tests"
+      },
+      {
+        "name": "components_background_task_scheduler_junit_tests",
+        "swarming": {},
+        "test": "components_background_task_scheduler_junit_tests"
+      },
+      {
+        "name": "components_gcm_driver_junit_tests",
+        "swarming": {},
+        "test": "components_gcm_driver_junit_tests"
+      },
+      {
+        "name": "components_invalidation_impl_junit_tests",
+        "swarming": {},
+        "test": "components_invalidation_impl_junit_tests"
+      },
+      {
+        "name": "components_policy_junit_tests",
+        "swarming": {},
+        "test": "components_policy_junit_tests"
+      },
+      {
+        "name": "components_signin_junit_tests",
+        "swarming": {},
+        "test": "components_signin_junit_tests"
+      },
+      {
+        "name": "components_variations_junit_tests",
+        "swarming": {},
+        "test": "components_variations_junit_tests"
+      },
+      {
+        "name": "content_junit_tests",
+        "swarming": {},
+        "test": "content_junit_tests"
+      },
+      {
+        "name": "device_junit_tests",
+        "swarming": {},
+        "test": "device_junit_tests"
+      },
+      {
+        "name": "junit_unit_tests",
+        "swarming": {},
+        "test": "junit_unit_tests"
+      },
+      {
+        "name": "keyboard_accessory_junit_tests",
+        "swarming": {},
+        "test": "keyboard_accessory_junit_tests"
+      },
+      {
+        "name": "media_base_junit_tests",
+        "swarming": {},
+        "test": "media_base_junit_tests"
+      },
+      {
+        "name": "media_router_junit_tests",
+        "swarming": {},
+        "test": "media_router_junit_tests"
+      },
+      {
+        "name": "module_installer_junit_tests",
+        "swarming": {},
+        "test": "module_installer_junit_tests"
+      },
+      {
+        "name": "net_junit_tests",
+        "swarming": {},
+        "test": "net_junit_tests"
+      },
+      {
+        "name": "service_junit_tests",
+        "swarming": {},
+        "test": "service_junit_tests"
+      },
+      {
+        "name": "touch_to_fill_junit_tests",
+        "swarming": {},
+        "test": "touch_to_fill_junit_tests"
+      },
+      {
+        "name": "ui_junit_tests",
+        "swarming": {},
+        "test": "ui_junit_tests"
+      },
+      {
+        "name": "webapk_client_junit_tests",
+        "swarming": {},
+        "test": "webapk_client_junit_tests"
+      },
+      {
+        "name": "webapk_shell_apk_h2o_junit_tests",
+        "swarming": {},
+        "test": "webapk_shell_apk_h2o_junit_tests"
+      },
+      {
+        "name": "webapk_shell_apk_junit_tests",
+        "swarming": {},
+        "test": "webapk_shell_apk_junit_tests"
+      }
     ]
   },
   "android-pie-arm64-dbg": {
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 2e1d01d..9c6d529 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -1247,27 +1247,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "isolate_coverage_data": true,
         "merge": {
           "args": [],
@@ -2843,26 +2822,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4361,26 +4320,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index 32b6474f..4231d20 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -405,26 +405,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -1862,27 +1842,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -3484,27 +3443,6 @@
         "test": "compositor_unittests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5740,53 +5678,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "nonperfetto_content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84P",
-              "device_os_type": "userdebug",
-              "device_type": "hammerhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -8960,53 +8851,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "nonperfetto_content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -11317,26 +11161,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=UseSkiaRenderer",
           "--test-launcher-filter-file=../../testing/buildbot/filters/skia_renderer.content_browsertests.filter"
         ],
@@ -12811,27 +12635,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=UseSkiaRenderer",
           "--test-launcher-filter-file=../../testing/buildbot/filters/skia_renderer.content_browsertests.filter",
           "--test-launcher-print-test-stdio=always"
@@ -14402,26 +14205,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=UseSkiaRenderer",
           "--test-launcher-filter-file=../../testing/buildbot/filters/skia_renderer.content_browsertests.filter"
         ],
@@ -15799,26 +15582,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=UseSkiaRenderer",
           "--test-launcher-filter-file=../../testing/buildbot/filters/skia_renderer.content_browsertests.filter"
         ],
@@ -17198,26 +16961,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=UseSkiaRenderer",
           "--test-launcher-filter-file=../../testing/buildbot/filters/skia_renderer.content_browsertests.filter"
         ],
@@ -18597,26 +18340,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=UseSkiaRenderer",
           "--test-launcher-filter-file=../../testing/buildbot/filters/skia_renderer.content_browsertests.filter"
         ],
@@ -20012,26 +19735,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=UseSkiaRenderer",
           "--test-launcher-filter-file=../../testing/buildbot/filters/skia_renderer.content_browsertests.filter"
         ],
@@ -21277,21 +20980,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -22379,22 +22067,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--test-launcher-print-test-stdio=always"
         ],
         "merge": {
@@ -23381,21 +23053,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -24462,21 +24119,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -25523,21 +25165,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -26584,21 +26211,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -27819,27 +27431,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -29480,27 +29071,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -31141,27 +30711,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -32628,21 +32177,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -33689,21 +33223,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -34750,21 +34269,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -35811,21 +35315,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -36872,21 +36361,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -38034,26 +37508,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=UseSkiaRenderer",
           "--test-launcher-filter-file=../../testing/buildbot/filters/skia_renderer.content_browsertests.filter"
         ],
@@ -39474,26 +38928,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 43c5a84..37ad168 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -1444,21 +1444,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -2738,27 +2723,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-17134"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5577,54 +5541,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "nonperfetto_content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -10868,26 +10784,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -11630,27 +11526,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--enable-features=BlinkHeapConcurrentMarking"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=BlinkHeapConcurrentMarking"
         ],
         "merge": {
@@ -12403,26 +12278,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -13454,27 +13309,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "isolate_coverage_data": true,
         "merge": {
           "args": [],
@@ -14962,26 +14796,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=UseSkiaRenderer",
           "--test-launcher-filter-file=../../testing/buildbot/filters/skia_renderer.content_browsertests.filter"
         ],
@@ -16766,27 +16580,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "isolate_coverage_data": true,
         "merge": {
           "args": [],
@@ -22569,28 +22362,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.15"
-            }
-          ],
-          "expiration": 21600
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 3c5e067..5a667a4 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -13662,36 +13662,6 @@
       },
       {
         "args": [
-          "--enable-gpu",
-          "--test-launcher-bot-mode",
-          "--test-launcher-jobs=1",
-          "--gtest_filter=CastStreamingApiTestWithPixelOutput.EndToEnd*:TabCaptureApiPixelTest.EndToEnd*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "tab_capture_end2end_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "mac-intel-stable"
-            },
-            {
-              "gpu": "1002:6821",
-              "hidpi": "1",
-              "os": "mac-amd-stable",
-              "pool": "Chrome-GPU"
-            }
-          ]
-        },
-        "test": "browser_tests"
-      },
-      {
-        "args": [
           "--use-gpu-in-tests",
           "--use-cmd-decoder=validating"
         ],
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index 2910de8..011bd741 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -347,26 +347,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -1164,26 +1144,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -2695,27 +2655,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=UseSkiaRenderer",
           "--test-launcher-filter-file=../../testing/buildbot/filters/skia_renderer.content_browsertests.filter"
         ],
@@ -4572,26 +4511,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=UseSkiaRenderer",
           "--test-launcher-filter-file=../../testing/buildbot/filters/skia_renderer.content_browsertests.filter"
         ],
@@ -6358,26 +6277,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=UseSkiaRenderer",
           "--test-launcher-filter-file=../../testing/buildbot/filters/skia_renderer.content_browsertests.filter"
         ],
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index 78b1494..4f37ea6b 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -413,27 +413,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "none",
-              "os": "Mac-10.10"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -1982,27 +1961,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "none",
-              "os": "Mac-10.11"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -3551,27 +3509,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.12.6"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5165,27 +5102,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "none",
-              "os": "Mac-10.13.6"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -6769,27 +6685,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "none",
-              "os": "Mac-10.13.6"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 79fa3fe..4985ac6 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -1167,53 +1167,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "nonperfetto_content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -3596,27 +3549,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--test-launcher-print-test-stdio=always"
         ],
         "merge": {
@@ -5264,26 +5196,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -6859,27 +6771,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--test-launcher-print-test-stdio=always"
         ],
         "merge": {
@@ -8685,28 +8576,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--test-launcher-print-test-stdio=always"
         ],
         "merge": {
@@ -10439,28 +10308,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--test-launcher-print-test-stdio=always"
         ],
         "merge": {
@@ -12122,27 +11969,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-gpu",
           "--test-launcher-bot-mode",
           "--test-launcher-jobs=1",
@@ -13627,22 +13453,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--test-launcher-print-test-stdio=always"
         ],
         "merge": {
@@ -17581,26 +17391,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json
index 9abec44e..6b54729 100644
--- a/testing/buildbot/chromium.win.json
+++ b/testing/buildbot/chromium.win.json
@@ -321,21 +321,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -1768,27 +1753,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -3630,27 +3594,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5429,21 +5372,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -6698,21 +6626,6 @@
         "test": "content_browsertests"
       },
       {
-        "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
-        "test": "content_browsertests"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 1995d41..ff5c0bc 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -287,11 +287,6 @@
           'shards': 12,
         }
       },
-      'android-kitkat-arm-rel': {
-        'swarming': {
-          'shards': 12,
-        },
-      },
       'KitKat Phone Tester (dbg)': {
         'swarming': {
           'shards': 20,
@@ -389,6 +384,11 @@
           'shards': 13,
         },
       },
+      'android-kitkat-arm-rel': {
+        'swarming': {
+          'shards': 15,
+        },
+      },
       # chromium.linux
       'Cast Audio Linux': {
         'args': [
@@ -1174,11 +1174,6 @@
       },
     },
   },
-  'nonperfetto_content_browsertests': {
-    'remove_from': [
-      'android-asan',
-    ],
-  },
   'not_site_per_process_webkit_layout_tests': {
     'remove_from': [
       # chromium.linux
@@ -1426,6 +1421,8 @@
       'Linux FYI SkiaRenderer Vulkan (Intel HD 630)',
       'Linux FYI SkiaRenderer Vulkan (NVIDIA)',
       'Linux Release (NVIDIA)',
+      # Fails on Mac ASAN due to crbug.com/1008508
+      'Mac FYI GPU ASAN Release',
     ],
   },
   'telemetry_perf_unittests': {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 858818d..d754e7b 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -2325,13 +2325,6 @@
           'shards': 3,
         },
       },
-      'nonperfetto_content_browsertests': {
-        'args': [
-          '--disable-perfetto',
-          '--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*',
-        ],
-        'test': 'content_browsertests',
-      },
       'perfetto_unittests': {},
       'services_unittests': {},
       'shell_dialogs_unittests': {},
diff --git a/testing/buildbot/tryserver.chromium.android.json b/testing/buildbot/tryserver.chromium.android.json
index e631aa3..ec9ca73 100644
--- a/testing/buildbot/tryserver.chromium.android.json
+++ b/testing/buildbot/tryserver.chromium.android.json
@@ -1008,54 +1008,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "nonperfetto_content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "KTU84P",
-              "device_os_type": "userdebug",
-              "device_type": "hammerhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -4209,54 +4161,6 @@
       },
       {
         "args": [
-          "--disable-perfetto",
-          "--gtest_filter=TracingControllerTest.*:BackgroundTracingManagerBrowserTest.*",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "nonperfetto_content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "nonperfetto_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 9b6b36a..bfdaf5e 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -608,7 +608,6 @@
         ],
         'test_suites': {
           'gtest_tests': 'chromium_android_gtests',
-          'junit_tests': 'chromium_junit_tests',
           'isolated_scripts': 'monochrome_apk_checker_isolated_script',
         },
         'os_type': 'android',
@@ -624,6 +623,7 @@
         ],
         'test_suites': {
           'gtest_tests': 'android_lollipop_marshmallow_gtests',
+          'junit_tests': 'chromium_junit_tests',
           'isolated_scripts': 'marshmallow_isolated_scripts',
         },
         'os_type': 'android',
diff --git a/third_party/blink/common/BUILD.gn b/third_party/blink/common/BUILD.gn
index cf273d4..a41aeb7 100644
--- a/third_party/blink/common/BUILD.gn
+++ b/third_party/blink/common/BUILD.gn
@@ -45,6 +45,8 @@
     "indexeddb/indexeddb_key_path.cc",
     "indexeddb/indexeddb_key_range.cc",
     "indexeddb/indexeddb_metadata.cc",
+    "loader/mime_sniffing_throttle.cc",
+    "loader/mime_sniffing_url_loader.cc",
     "loader/request_destination.cc",
     "loader/url_loader_factory_bundle.cc",
     "loader/url_loader_factory_bundle_mojom_traits.cc",
@@ -157,6 +159,7 @@
     "feature_policy/feature_policy_unittest.cc",
     "frame/user_activation_state_unittest.cc",
     "indexeddb/indexeddb_key_unittest.cc",
+    "loader/mime_sniffing_throttle_unittest.cc",
     "manifest/manifest_icon_selector_unittest.cc",
     "manifest/manifest_util_unittest.cc",
     "mediastream/media_devices_unittest.cc",
@@ -176,6 +179,7 @@
     "//base/test:test_support",
     "//mojo/core/embedder",
     "//net",
+    "//services/network:test_support",
     "//skia/public/mojom",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/third_party/blink/common/DEPS b/third_party/blink/common/DEPS
index 0a072bb7..16f25be 100644
--- a/third_party/blink/common/DEPS
+++ b/third_party/blink/common/DEPS
@@ -26,5 +26,5 @@
     "+url",
 ]
 specific_include_rules = {
-  '.*unittest.?.cc': [ "+v8" ],
+  '.*unittest.?.cc': [ "+v8", "+services/network/test" ],
 }
diff --git a/content/common/mime_sniffing_throttle.cc b/third_party/blink/common/loader/mime_sniffing_throttle.cc
similarity index 92%
rename from content/common/mime_sniffing_throttle.cc
rename to third_party/blink/common/loader/mime_sniffing_throttle.cc
index b69db33..959f4f1 100644
--- a/content/common/mime_sniffing_throttle.cc
+++ b/third_party/blink/common/loader/mime_sniffing_throttle.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/common/mime_sniffing_throttle.h"
+#include "third_party/blink/public/common/loader/mime_sniffing_throttle.h"
 
-#include "content/common/mime_sniffing_url_loader.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "net/base/mime_sniffer.h"
+#include "third_party/blink/public/common/loader/mime_sniffing_url_loader.h"
 
-namespace content {
+namespace blink {
 
 MimeSniffingThrottle::MimeSniffingThrottle(
     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
@@ -62,4 +62,4 @@
   delegate_->Resume();
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/content/common/mime_sniffing_throttle_unittest.cc b/third_party/blink/common/loader/mime_sniffing_throttle_unittest.cc
similarity index 97%
rename from content/common/mime_sniffing_throttle_unittest.cc
rename to third_party/blink/common/loader/mime_sniffing_throttle_unittest.cc
index 5ff0c32..4b68cf0 100644
--- a/content/common/mime_sniffing_throttle_unittest.cc
+++ b/third_party/blink/common/loader/mime_sniffing_throttle_unittest.cc
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/common/mime_sniffing_throttle.h"
+#include "third_party/blink/public/common/loader/mime_sniffing_throttle.h"
 
 #include <memory>
 
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
-#include "content/common/mime_sniffing_url_loader.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -17,10 +16,11 @@
 #include "services/network/test/test_url_loader_client.h"
 #include "services/network/test/test_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/loader/mime_sniffing_url_loader.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
 #include "url/gurl.h"
 
-namespace content {
+namespace blink {
 
 namespace {
 
@@ -341,10 +341,12 @@
   EXPECT_TRUE(defer);
   EXPECT_TRUE(delegate->is_intercepted());
 
-  mojo::DataPipe pipe;
+  mojo::ScopedDataPipeProducerHandle producer;
+  mojo::ScopedDataPipeConsumerHandle consumer;
+  CHECK_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(nullptr, &producer, &consumer));
   delegate->source_loader_client_remote()->OnStartLoadingResponseBody(
-      std::move(pipe.consumer_handle));
-  pipe.producer_handle.reset();  // The pipe is empty.
+      std::move(consumer));
+  producer.reset();  // The pipe is empty.
 
   delegate->source_loader_client_remote()->OnComplete(
       network::URLLoaderCompletionStatus());
@@ -515,4 +517,4 @@
   task_environment_.RunUntilIdle();
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/content/common/mime_sniffing_url_loader.cc b/third_party/blink/common/loader/mime_sniffing_url_loader.cc
similarity index 98%
rename from content/common/mime_sniffing_url_loader.cc
rename to third_party/blink/common/loader/mime_sniffing_url_loader.cc
index 51b1811..41c0578 100644
--- a/content/common/mime_sniffing_url_loader.cc
+++ b/third_party/blink/common/loader/mime_sniffing_url_loader.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/common/mime_sniffing_url_loader.h"
+#include "third_party/blink/public/common/loader/mime_sniffing_url_loader.h"
 
 #include "base/bind.h"
-#include "content/common/mime_sniffing_throttle.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "net/base/mime_sniffer.h"
+#include "third_party/blink/public/common/loader/mime_sniffing_throttle.h"
 
-namespace content {
+namespace blink {
 
 // static
 const char MimeSniffingURLLoader::kDefaultMimeType[] = "text/plain";
@@ -369,4 +369,4 @@
   // has already been destroyed by some reason.
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index 6747496..a656bc9 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -76,6 +76,8 @@
     "indexeddb/indexeddb_key_range.h",
     "indexeddb/indexeddb_metadata.h",
     "indexeddb/web_idb_types.h",
+    "loader/mime_sniffing_throttle.h",
+    "loader/mime_sniffing_url_loader.h",
     "loader/request_destination.h",
     "loader/url_loader_factory_bundle.h",
     "loader/url_loader_factory_bundle_mojom_traits.h",
diff --git a/third_party/blink/public/common/DEPS b/third_party/blink/public/common/DEPS
index 8085a97..be3d08b 100644
--- a/third_party/blink/public/common/DEPS
+++ b/third_party/blink/public/common/DEPS
@@ -12,6 +12,7 @@
     "+media",
     "+mojo",
     "+services/network/public/cpp/resource_request_body.h",
+    "+services/network/public/cpp/resource_response.h",
     "+services/network/public/cpp/shared_url_loader_factory.h",
     "+services/network/public/mojom/url_loader.mojom.h",
     "+services/network/public/mojom/url_loader.mojom-forward.h",
diff --git a/content/common/mime_sniffing_throttle.h b/third_party/blink/public/common/loader/mime_sniffing_throttle.h
similarity index 75%
rename from content/common/mime_sniffing_throttle.h
rename to third_party/blink/public/common/loader/mime_sniffing_throttle.h
index 135b3d1..224e5b6 100644
--- a/content/common/mime_sniffing_throttle.h
+++ b/third_party/blink/public/common/loader/mime_sniffing_throttle.h
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_COMMON_MIME_SNIFFING_THROTTLE_H_
-#define CONTENT_COMMON_MIME_SNIFFING_THROTTLE_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_LOADER_MIME_SNIFFING_THROTTLE_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_LOADER_MIME_SNIFFING_THROTTLE_H_
 
 #include "base/memory/weak_ptr.h"
-#include "content/common/content_export.h"
+#include "third_party/blink/public/common/common_export.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
 
-namespace content {
+namespace blink {
 
 // Throttle for mime type sniffing. This may intercept the request and
 // modify the response's mime type in the response head.
-class CONTENT_EXPORT MimeSniffingThrottle : public blink::URLLoaderThrottle {
+class BLINK_COMMON_EXPORT MimeSniffingThrottle : public URLLoaderThrottle {
  public:
   // |task_runner| is used to bind the right task runner for handling incoming
   // IPC in MimeSniffingLoader. |task_runner| is supposed to be bound to the
@@ -36,6 +36,6 @@
   base::WeakPtrFactory<MimeSniffingThrottle> weak_factory_{this};
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_COMMON_MIME_SNIFFING_THROTTLE_H_
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_LOADER_MIME_SNIFFING_THROTTLE_H_
diff --git a/content/common/mime_sniffing_url_loader.h b/third_party/blink/public/common/loader/mime_sniffing_url_loader.h
similarity index 94%
rename from content/common/mime_sniffing_url_loader.h
rename to third_party/blink/public/common/loader/mime_sniffing_url_loader.h
index a04f6bdc..44dc0078 100644
--- a/content/common/mime_sniffing_url_loader.h
+++ b/third_party/blink/public/common/loader/mime_sniffing_url_loader.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_COMMON_MIME_SNIFFING_URL_LOADER_H_
-#define CONTENT_COMMON_MIME_SNIFFING_URL_LOADER_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_LOADER_MIME_SNIFFING_URL_LOADER_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_LOADER_MIME_SNIFFING_URL_LOADER_H_
 
 #include <tuple>
 
@@ -11,7 +11,6 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string_piece.h"
-#include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -23,9 +22,10 @@
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "third_party/blink/public/common/common_export.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
 
-namespace content {
+namespace blink {
 
 class MimeSniffingThrottle;
 
@@ -54,7 +54,7 @@
 //           the source loader to |this| are stopped. All incoming messages from
 //           the destination (through network::mojom::URLLoader) are ignored in
 //           this state.
-class CONTENT_EXPORT MimeSniffingURLLoader
+class BLINK_COMMON_EXPORT MimeSniffingURLLoader
     : public network::mojom::URLLoaderClient,
       public network::mojom::URLLoader {
  public:
@@ -154,6 +154,6 @@
   DISALLOW_COPY_AND_ASSIGN(MimeSniffingURLLoader);
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_COMMON_MIME_SNIFFING_URL_LOADER_H_
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_LOADER_MIME_SNIFFING_URL_LOADER_H_
diff --git a/third_party/blink/public/platform/web_localized_string.h b/third_party/blink/public/platform/web_localized_string.h
index 643bbd7..2135f44 100644
--- a/third_party/blink/public/platform/web_localized_string.h
+++ b/third_party/blink/public/platform/web_localized_string.h
@@ -63,11 +63,6 @@
     kSubmitButtonDefaultLabel,
     kTextTracksNoLabel,
     kTextTracksOff,
-    kUnitsKibibytes,
-    kUnitsMebibytes,
-    kUnitsGibibytes,
-    kUnitsTebibytes,
-    kUnitsPebibytes,
     kValidationStepMismatchCloseToLimit,
     kValidationTooShort,
     kValidationTooShortPlural,
diff --git a/third_party/blink/renderer/bindings/core/v8/custom/v8_pop_state_event_custom.cc b/third_party/blink/renderer/bindings/core/v8/custom/v8_pop_state_event_custom.cc
index 28c17d9..5ac764e 100644
--- a/third_party/blink/renderer/bindings/core/v8/custom/v8_pop_state_event_custom.cc
+++ b/third_party/blink/renderer/bindings/core/v8/custom/v8_pop_state_event_custom.cc
@@ -37,12 +37,6 @@
 
 namespace blink {
 
-namespace {
-// |kHistoryStateSymbolKey| is a key for a cached attribute for History.state.
-// TODO(peria): Do not use this cached attribute directly.
-constexpr char kHistoryStateSymbolKey[] = "History#State";
-}
-
 // Save the state value to a hidden attribute in the V8PopStateEvent, and return
 // it, for convenience.
 static v8::Local<v8::Value> CacheState(ScriptState* script_state,
@@ -94,7 +88,7 @@
   bool is_same_state = history->IsSameAsCurrentState(event->SerializedState());
   if (is_same_state) {
     V8PrivateProperty::Symbol history_state =
-        V8PrivateProperty::GetSymbol(isolate, kHistoryStateSymbolKey);
+        V8PrivateProperty::GetHistoryStateSymbol(info.GetIsolate());
     v8::Local<v8::Value> v8_history_value =
         ToV8(history, info.Holder(), isolate);
     if (v8_history_value.IsEmpty())
diff --git a/third_party/blink/renderer/bindings/templates/attributes.cc.tmpl b/third_party/blink/renderer/bindings/templates/attributes.cc.tmpl
index 3601da6..17a6000 100644
--- a/third_party/blink/renderer/bindings/templates/attributes.cc.tmpl
+++ b/third_party/blink/renderer/bindings/templates/attributes.cc.tmpl
@@ -75,9 +75,14 @@
 
   {% if attribute.cached_attribute_validation_method %}
   // [CachedAttribute]
+  {% if cpp_class == 'History' and attribute.camel_case_name == 'State' %}
+  V8PrivateProperty::Symbol property_symbol =
+      V8PrivateProperty::GetHistoryStateSymbol(info.GetIsolate());
+  {% else %}
   V8PrivateProperty::Symbol property_symbol =
       V8PrivateProperty::GetSymbol(info.GetIsolate(),
           "{{cpp_class}}#{{attribute.camel_case_name}}");
+  {% endif %}
   if (!static_cast<const {{cpp_class}}*>(impl)->{{attribute.cached_attribute_validation_method}}()) {
     v8::Local<v8::Value> v8_value;
     if (property_symbol.GetOrUndefined(holder).ToLocal(&v8_value) && !v8_value->IsUndefined()) {
diff --git a/third_party/blink/renderer/core/clipboard/data_object_item.cc b/third_party/blink/renderer/core/clipboard/data_object_item.cc
index d61c83d3..74fc7191 100644
--- a/third_party/blink/renderer/core/clipboard/data_object_item.cc
+++ b/third_party/blink/renderer/core/clipboard/data_object_item.cc
@@ -141,9 +141,12 @@
     SkPixmap pixmap;
     bitmap.peekPixels(&pixmap);
 
-    Vector<uint8_t> png_data;
+    // Set encoding options to favor speed over size.
     SkPngEncoder::Options options;
-    options.fZLibLevel = 1;  // Fastest compression.
+    options.fZLibLevel = 1;
+    options.fFilterFlags = SkPngEncoder::FilterFlag::kNone;
+
+    Vector<uint8_t> png_data;
     if (!ImageEncoder::Encode(&png_data, pixmap, options))
       return nullptr;
 
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index bb7716f..f34d03c 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -4345,10 +4345,16 @@
 }
 
 void Document::SetParsingState(ParsingState parsing_state) {
+  ParsingState previous_state = parsing_state_;
   parsing_state_ = parsing_state;
 
   if (Parsing() && !element_data_cache_)
     element_data_cache_ = MakeGarbageCollected<ElementDataCache>();
+  if (previous_state != kFinishedParsing &&
+      parsing_state_ == kFinishedParsing) {
+    if (form_controller_ && form_controller_->HasFormStates())
+      form_controller_->ScheduleRestore();
+  }
 }
 
 bool Document::ShouldScheduleLayout() const {
diff --git a/third_party/blink/renderer/core/exported/web_frame.cc b/third_party/blink/renderer/core/exported/web_frame.cc
index 2c5f20a..ae81b3f 100644
--- a/third_party/blink/renderer/core/exported/web_frame.cc
+++ b/third_party/blink/renderer/core/exported/web_frame.cc
@@ -34,6 +34,8 @@
   Frame* old_frame = ToCoreFrame(*this);
   if (!old_frame->IsAttached())
     return false;
+  FrameOwner* owner = old_frame->Owner();
+  FrameSwapScope frame_swap_scope(owner);
 
   // Unload the current Document in this frame: this calls unload handlers,
   // detaches child frames, etc. Since this runs script, make sure this frame
@@ -89,7 +91,6 @@
 
   Page* page = old_frame->GetPage();
   AtomicString name = old_frame->Tree().GetName();
-  FrameOwner* owner = old_frame->Owner();
 
   v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
   WindowProxyManager::GlobalProxyVector global_proxies;
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index ac01193..b17b1677 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -8687,6 +8687,35 @@
   child_core_frame.Clear();
 }
 
+class WebFrameSwapTestClient : public frame_test_helpers::TestWebFrameClient {
+ public:
+  WebFrameSwapTestClient() {}
+
+  WebLocalFrame* CreateChildFrame(WebLocalFrame* parent,
+                                  WebTreeScopeType scope,
+                                  const WebString& name,
+                                  const WebString& fallback_name,
+                                  const FramePolicy&,
+                                  const WebFrameOwnerProperties&,
+                                  FrameOwnerElementType) override {
+    return CreateLocalChild(*parent, scope,
+                            std::make_unique<WebFrameSwapTestClient>());
+  }
+
+  void DidChangeFrameOwnerProperties(
+      WebFrame* child_frame,
+      const WebFrameOwnerProperties& properties) override {
+    did_propagate_display_none_ |= properties.is_display_none;
+  }
+
+  bool DidPropagateDisplayNoneProperty() const {
+    return did_propagate_display_none_;
+  }
+
+ private:
+  bool did_propagate_display_none_ = false;
+};
+
 class WebFrameSwapTest : public WebFrameTest {
  protected:
   WebFrameSwapTest() {
@@ -8696,7 +8725,8 @@
     RegisterMockedHttpURLLoad("subframe-c.html");
     RegisterMockedHttpURLLoad("subframe-hello.html");
 
-    web_view_helper_.InitializeAndLoad(base_url_ + "frame-a-b-c.html");
+    web_view_helper_.InitializeAndLoad(base_url_ + "frame-a-b-c.html",
+                                       &main_frame_client_);
   }
 
   void Reset() { web_view_helper_.Reset(); }
@@ -8705,6 +8735,7 @@
 
  private:
   frame_test_helpers::WebViewHelper web_view_helper_;
+  WebFrameSwapTestClient main_frame_client_;
 };
 
 TEST_F(WebFrameSwapTest, SwapMainFrame) {
@@ -8848,6 +8879,26 @@
   EXPECT_EQ("  \n\nhello\n\nb \n\na\n\nc", content);
 }
 
+TEST_F(WebFrameSwapTest, DoNotPropagateDisplayNonePropertyOnSwap) {
+  WebFrameSwapTestClient* main_frame_client =
+      static_cast<WebFrameSwapTestClient*>(MainFrame()->Client());
+  EXPECT_FALSE(main_frame_client->DidPropagateDisplayNoneProperty());
+
+  WebLocalFrame* child_frame = MainFrame()->FirstChild()->ToWebLocalFrame();
+  frame_test_helpers::LoadFrame(child_frame, "subframe-hello.html");
+  EXPECT_FALSE(main_frame_client->DidPropagateDisplayNoneProperty());
+
+  WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
+  child_frame->Swap(remote_frame);
+  EXPECT_FALSE(main_frame_client->DidPropagateDisplayNoneProperty());
+
+  WebLocalFrame* local_frame =
+      frame_test_helpers::CreateProvisional(*remote_frame);
+  remote_frame->Swap(local_frame);
+  EXPECT_FALSE(main_frame_client->DidPropagateDisplayNoneProperty());
+  Reset();
+}
+
 void WebFrameTest::SwapAndVerifyMiddleChildConsistency(
     const char* const message,
     WebFrame* parent,
diff --git a/third_party/blink/renderer/core/frame/frame_owner.h b/third_party/blink/renderer/core/frame/frame_owner.h
index ba29a7b..178e3b3 100644
--- a/third_party/blink/renderer/core/frame/frame_owner.h
+++ b/third_party/blink/renderer/core/frame/frame_owner.h
@@ -14,12 +14,15 @@
 namespace blink {
 
 class Frame;
+class FrameSwapScope;
 class ResourceTimingInfo;
 
 // Oilpan: all FrameOwner instances are GCed objects. FrameOwner additionally
 // derives from GarbageCollectedMixin so that Member<FrameOwner> references can
 // be kept (e.g., Frame::m_owner.)
 class CORE_EXPORT FrameOwner : public GarbageCollectedMixin {
+  friend class FrameSwapScope;
+
  public:
   virtual ~FrameOwner() = default;
 
@@ -73,6 +76,42 @@
 
   // Returns whether or not children of the owned frame should be lazily loaded.
   virtual bool ShouldLazyLoadChildren() const = 0;
+
+ protected:
+  virtual void FrameOwnerPropertiesChanged() {}
+
+ private:
+  virtual void SetIsSwappingFrames(bool) {}
+};
+
+// The purpose of this class is to suppress the propagation of frame owner
+// properties while a frame is being replaced. In particular, it prevents the
+// erroneous propagation of is_display_none=true, which would otherwise happen
+// when the old frame is detached prior to attaching the new frame. This class
+// will postpone the propagation until the properties are in their new stable
+// state.
+//
+// It is only intended to handle cases where one frame is detached and a new
+// frame immediately attached. For normal frame unload/teardown, we don't need
+// to suppress the propagation.
+class FrameSwapScope {
+  STACK_ALLOCATED();
+
+ public:
+  FrameSwapScope(FrameOwner* frame_owner) : frame_owner_(frame_owner) {
+    if (frame_owner)
+      frame_owner->SetIsSwappingFrames(true);
+  }
+
+  ~FrameSwapScope() {
+    if (frame_owner_) {
+      frame_owner_->SetIsSwappingFrames(false);
+      frame_owner_->FrameOwnerPropertiesChanged();
+    }
+  }
+
+ private:
+  Member<FrameOwner> frame_owner_;
 };
 
 // TODO(dcheng): This class is an internal implementation detail of provisional
diff --git a/third_party/blink/renderer/core/html/anchor_element_metrics_sender.cc b/third_party/blink/renderer/core/html/anchor_element_metrics_sender.cc
index 00aa4df7..c08cce6 100644
--- a/third_party/blink/renderer/core/html/anchor_element_metrics_sender.cc
+++ b/third_party/blink/renderer/core/html/anchor_element_metrics_sender.cc
@@ -5,7 +5,7 @@
 #include "third_party/blink/renderer/core/html/anchor_element_metrics_sender.h"
 
 #include "base/metrics/histogram_macros.h"
-#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
@@ -113,7 +113,7 @@
   if (!document->GetFrame())
     return false;
 
-  document->GetFrame()->GetInterfaceProvider().GetInterface(
+  document->GetBrowserInterfaceBroker().GetInterface(
       metrics_host_.BindNewPipeAndPassReceiver());
   return true;
 }
diff --git a/third_party/blink/renderer/core/html/forms/form_controller.cc b/third_party/blink/renderer/core/html/forms/form_controller.cc
index a7643ab..36838e20 100644
--- a/third_party/blink/renderer/core/html/forms/form_controller.cc
+++ b/third_party/blink/renderer/core/html/forms/form_controller.cc
@@ -44,6 +44,10 @@
 
 namespace {
 
+// TODO(crbug.com/1008708): Remove this flag when we're sure the new behavior
+// is better than the previous one.
+constexpr bool kRestoreOnLoad = true;
+
 inline HTMLFormElement* OwnerFormForState(const ListedElement& control) {
   // Assume controls with form attribute have no owners because we restore
   // state during parsing and form owners of such controls might be
@@ -58,6 +62,20 @@
   return To<ElementInternals>(control).Target().localName();
 }
 
+bool IsDirtyControl(const ListedElement& control) {
+  if (control.IsFormControlElementWithState())
+    return ToHTMLFormControlElementWithState(control).UserHasEditedTheField();
+  if (control.IsElementInternals()) {
+    // We have no ways to know the dirtiness of a form-associated custom
+    // element.  Assume it is dirty if it has focus.
+    // TODO(tkent): If this approach is not enough, we should check existence
+    // of past user-input events such as 'mousedown', 'keydown', 'touchstart'.
+    return control.ToHTMLElement().HasFocusWithin();
+  }
+  DCHECK(!control.ClassSupportsStateRestore());
+  return false;
+}
+
 }  // namespace
 
 // ----------------------------------------------------------------------------
@@ -425,6 +443,19 @@
   form_controls_dirty_ = true;
 }
 
+const DocumentState::FormElementList& DocumentState::ControlList() {
+  if (form_controls_dirty_) {
+    for (auto& element : Traversal<Element>::DescendantsOf(*document_)) {
+      if (auto* control = ListedElement::From(element)) {
+        if (control->ClassSupportsStateRestore())
+          form_controls_.push_back(control);
+      }
+    }
+    form_controls_dirty_ = false;
+  }
+  return form_controls_;
+}
+
 static String FormStateSignature() {
   // In the legacy version of serialized state, the first item was a name
   // attribute value of a form control. The following string literal should
@@ -435,19 +466,10 @@
 }
 
 Vector<String> DocumentState::ToStateVector() {
-  if (form_controls_dirty_) {
-    for (auto& element : Traversal<Element>::DescendantsOf(*document_)) {
-      if (auto* control = ListedElement::From(element)) {
-        if (control->ClassSupportsStateRestore())
-          form_controls_.push_back(control);
-      }
-    }
-    form_controls_dirty_ = false;
-  }
   auto* key_generator = MakeGarbageCollected<FormKeyGenerator>();
   std::unique_ptr<SavedFormStateMap> state_map =
       base::WrapUnique(new SavedFormStateMap);
-  for (auto& control : form_controls_) {
+  for (auto& control : ControlList()) {
     DCHECK(control->ToHTMLElement().isConnected());
     if (!control->ShouldSaveAndRestoreFormControlState())
       continue;
@@ -461,7 +483,7 @@
   }
 
   Vector<String> state_vector;
-  state_vector.ReserveInitialCapacity(form_controls_.size() * 4);
+  state_vector.ReserveInitialCapacity(ControlList().size() * 4);
   state_vector.push_back(FormStateSignature());
   for (const auto& saved_form_state : *state_map) {
     state_vector.push_back(saved_form_state.key);
@@ -476,11 +498,13 @@
 // ----------------------------------------------------------------------------
 
 FormController::FormController(Document& document)
-    : document_state_(MakeGarbageCollected<DocumentState>(document)) {}
+    : document_(document),
+      document_state_(MakeGarbageCollected<DocumentState>(document)) {}
 
 FormController::~FormController() = default;
 
 void FormController::Trace(Visitor* visitor) {
+  visitor->Trace(document_);
   visitor->Trace(document_state_);
   visitor->Trace(form_key_generator_);
 }
@@ -544,6 +568,8 @@
 }
 
 void FormController::RestoreControlStateFor(ListedElement& control) {
+  if (kRestoreOnLoad && !document_->HasFinishedParsing())
+    return;
   // We don't save state of a control with
   // ShouldSaveAndRestoreFormControlState() == false. But we need to skip
   // restoring process too because a control in another form might have the same
@@ -553,11 +579,17 @@
   if (OwnerFormForState(control))
     return;
   FormControlState state = TakeStateForFormElement(control);
-  if (state.ValueSize() > 0)
-    control.RestoreFormControlState(state);
+  if (state.ValueSize() <= 0)
+    return;
+  // If a user already edited the control, we should not overwrite it.
+  if (IsDirtyControl(control))
+    return;
+  control.RestoreFormControlState(state);
 }
 
 void FormController::RestoreControlStateIn(HTMLFormElement& form) {
+  if (kRestoreOnLoad && !document_->HasFinishedParsing())
+    return;
   EventQueueScope scope;
   const ListedElement::List& elements = form.ListedElements();
   for (const auto& control : elements) {
@@ -568,10 +600,13 @@
     if (OwnerFormForState(*control) != &form)
       continue;
     FormControlState state = TakeStateForFormElement(*control);
-    if (state.ValueSize() > 0) {
-      // restoreFormControlState might dispatch input/change events.
-      control->RestoreFormControlState(state);
-    }
+    if (state.ValueSize() <= 0)
+      continue;
+    // If a user already edited the control, we should not overwrite it.
+    if (IsDirtyControl(*control))
+      continue;
+    // RestoreFormControlState might dispatch input/change events.
+    control->RestoreFormControlState(state);
   }
 }
 
@@ -584,6 +619,29 @@
     control.RestoreFormControlState(state);
 }
 
+void FormController::ScheduleRestore() {
+  if (!kRestoreOnLoad)
+    return;
+  document_->GetTaskRunner(TaskType::kInternalLoading)
+      ->PostTask(FROM_HERE,
+                 WTF::Bind(&FormController::RestoreAllControlsInDocumentOrder,
+                           WrapPersistent(this)));
+}
+
+void FormController::RestoreAllControlsInDocumentOrder() {
+  if (!document_->IsActive())
+    return;
+  HeapHashSet<Member<HTMLFormElement>> finished_forms;
+  EventQueueScope scope;
+  for (auto& control : document_state_->ControlList()) {
+    auto* owner = OwnerFormForState(*control);
+    if (!owner)
+      RestoreControlStateFor(*control);
+    else if (finished_forms.insert(owner).is_new_entry)
+      RestoreControlStateIn(*owner);
+  }
+}
+
 Vector<String> FormController::GetReferencedFilePaths(
     const Vector<String>& state_vector) {
   Vector<String> to_return;
diff --git a/third_party/blink/renderer/core/html/forms/form_controller.h b/third_party/blink/renderer/core/html/forms/form_controller.h
index 540ccf7..345d0be4b8 100644
--- a/third_party/blink/renderer/core/html/forms/form_controller.h
+++ b/third_party/blink/renderer/core/html/forms/form_controller.h
@@ -83,12 +83,15 @@
   DocumentState(Document& document);
   void Trace(Visitor*);
 
+  // TODO(tkent): Rename this to ControlList or something. The current
+  // name is confusing because it doesn't contain <form> elements.
+  using FormElementList = HeapVector<Member<ListedElement>, 64>;
   void InvalidateControlList();
+  const FormElementList& ControlList();
   Vector<String> ToStateVector();
 
  private:
   Member<Document> document_;
-  using FormElementList = HeapVector<Member<ListedElement>, 64>;
   FormElementList form_controls_;
   bool form_controls_dirty_ = true;
 };
@@ -114,6 +117,8 @@
   // For a upgraded form-associated custom element.
   void RestoreControlStateOnUpgrade(ListedElement&);
 
+  void ScheduleRestore();
+
   static Vector<String> GetReferencedFilePaths(
       const Vector<String>& state_vector);
 
@@ -121,7 +126,9 @@
   FormControlState TakeStateForFormElement(const ListedElement&);
   static void FormStatesFromStateVector(const Vector<String>&,
                                         SavedFormStateMap&);
+  void RestoreAllControlsInDocumentOrder();
 
+  Member<Document> document_;
   Member<DocumentState> document_state_;
   SavedFormStateMap saved_form_state_map_;
   Member<FormKeyGenerator> form_key_generator_;
diff --git a/third_party/blink/renderer/core/html/forms/resources/calendarPicker.css b/third_party/blink/renderer/core/html/forms/resources/calendarPicker.css
index f05e3a9..18ad0d5b 100644
--- a/third_party/blink/renderer/core/html/forms/resources/calendarPicker.css
+++ b/third_party/blink/renderer/core/html/forms/resources/calendarPicker.css
@@ -105,6 +105,7 @@
 .week-number-cell.disabled,
 .day-cell.disabled {
     background-color: #f5f5f5;
+    pointer-events: none;
 }
 
 .day-cell.current-month {
@@ -260,6 +261,7 @@
 
 .month-button[aria-disabled="true"] {
     color: GrayText;
+    pointer-events: none;
 }
 
 .scrubby-scroll-bar {
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index 8859907..01e87fe 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -172,8 +172,8 @@
     : HTMLElement(tag_name, document),
       content_frame_(nullptr),
       embedded_content_view_(nullptr),
-      should_lazy_load_children_(DoesParentAllowLazyLoadingChildren(document)) {
-}
+      should_lazy_load_children_(DoesParentAllowLazyLoadingChildren(document)),
+      is_swapping_frames_(false) {}
 
 LayoutEmbeddedContent* HTMLFrameOwnerElement::GetLayoutEmbeddedContent() const {
   // HTMLObjectElement and HTMLEmbedElement may return arbitrary layoutObjects
@@ -316,6 +316,7 @@
   // Don't notify about updates if ContentFrame() is null, for example when
   // the subframe hasn't been created yet.
   if (ContentFrame()) {
+    DCHECK(!is_swapping_frames_);
     GetDocument().GetFrame()->Client()->DidChangeFrameOwnerProperties(this);
   }
 }
@@ -368,7 +369,8 @@
     }
   }
 
-  FrameOwnerPropertiesChanged();
+  if (!is_swapping_frames_)
+    FrameOwnerPropertiesChanged();
 
   GetDocument().GetRootScrollerController().DidUpdateIFrameFrameView(*this);
 
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.h b/third_party/blink/renderer/core/html/html_frame_owner_element.h
index 14cb408..4201b02 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.h
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.h
@@ -46,7 +46,6 @@
 class CORE_EXPORT HTMLFrameOwnerElement : public HTMLElement,
                                           public FrameOwner {
   USING_GARBAGE_COLLECTED_MIXIN(HTMLFrameOwnerElement);
-
  public:
   ~HTMLFrameOwnerElement() override;
 
@@ -142,9 +141,9 @@
                               const AtomicString& frame_name,
                               bool replace_current_item);
   bool IsKeyboardFocusable() const override;
+  void FrameOwnerPropertiesChanged() override;
 
   void DisposePluginSoon(WebPluginContainerImpl*);
-  void FrameOwnerPropertiesChanged();
 
   // Return the origin which is to be used for feature policy container
   // policies, as "the origin of the URL in the frame's src attribute" (see
@@ -170,8 +169,10 @@
   // already HTMLFrameOwnerElement.
   bool IsLocal() const final { return true; }
   bool IsRemote() const final { return false; }
-
   bool IsFrameOwnerElement() const final { return true; }
+  void SetIsSwappingFrames(bool is_swapping) override {
+    is_swapping_frames_ = is_swapping;
+  }
 
   virtual network::mojom::ReferrerPolicy ReferrerPolicyAttribute() {
     return network::mojom::ReferrerPolicy::kDefault;
@@ -185,6 +186,7 @@
 
   Member<LazyLoadFrameObserver> lazy_load_frame_observer_;
   bool should_lazy_load_children_;
+  bool is_swapping_frames_;
 };
 
 class SubframeLoadingDisabler {
diff --git a/third_party/blink/renderer/core/html/media/video_wake_lock.cc b/third_party/blink/renderer/core/html/media/video_wake_lock.cc
index eba95c6c..eebb27c 100644
--- a/third_party/blink/renderer/core/html/media/video_wake_lock.cc
+++ b/third_party/blink/renderer/core/html/media/video_wake_lock.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/core/html/media/video_wake_lock.h"
 
-#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/mojom/wake_lock/wake_lock.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
@@ -115,7 +115,7 @@
       frame->GetTaskRunner(TaskType::kMediaElementEvent);
 
   mojo::Remote<blink::mojom::blink::WakeLockService> service;
-  frame->GetInterfaceProvider().GetInterface(
+  frame->GetBrowserInterfaceBroker().GetInterface(
       service.BindNewPipeAndPassReceiver(task_runner));
   service->GetWakeLock(device::mojom::WakeLockType::kPreventDisplaySleep,
                        device::mojom::blink::WakeLockReason::kVideoPlayback,
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index 8cb6e43..898ec97 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -6652,6 +6652,8 @@
       # We plan to make this the default, deprecate non-flattened mode,
       # and eventually retire it. See crbug.com/991325.
       optional boolean flatten
+      # Auto-attach to the targets created via window.open from current target.
+      experimental optional boolean windowOpen
 
   # Controls whether to discover available targets and notify via
   # `targetCreated/targetInfoChanged/targetDestroyed` events.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h
index 28d5f0c..0ae6630 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h
@@ -124,6 +124,10 @@
 
   LayoutObject* GetLayoutObject() const { return layout_object_; }
 
+  bool IsImage() const {
+    return GetLayoutObject() && GetLayoutObject()->IsLayoutImage();
+  }
+
   void SetOffset(unsigned start, unsigned end) {
     DCHECK_GE(end, start);
     start_offset_ = start;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
index 70dfcb6..ac44502a 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
@@ -39,6 +39,7 @@
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h"
 #include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_buffer.h"
 
 namespace blink {
 
@@ -479,7 +480,7 @@
   DCHECK(data);
   CollectInlines(data, previous_data.get(), dirty_lines);
   SegmentText(data);
-  ShapeText(data, &previous_data->text_content);
+  ShapeText(data, previous_data ? &previous_data->text_content : nullptr);
   ShapeTextForFirstLineIfNeeded(data);
   AssociateItemsWithInlines(data);
   DCHECK_EQ(data, MutableData());
@@ -1428,6 +1429,50 @@
   return !builder.ShouldAbort();
 }
 
+namespace {
+
+template <typename CharType>
+scoped_refptr<StringImpl> CreateTextContentForStickyImagesQuirk(
+    const CharType* text,
+    unsigned length,
+    base::span<const NGInlineItem> items) {
+  StringBuffer<CharType> buffer(length);
+  CharType* characters = buffer.Characters();
+  memcpy(characters, text, length * sizeof(CharType));
+  for (const NGInlineItem& item : items) {
+    if (item.Type() == NGInlineItem::kAtomicInline && item.IsImage()) {
+      DCHECK_EQ(characters[item.StartOffset()], kObjectReplacementCharacter);
+      characters[item.StartOffset()] = kNoBreakSpaceCharacter;
+    }
+  }
+  return buffer.Release();
+}
+
+}  // namespace
+
+// The stick images quirk changes the line breaking behavior around images. This
+// function returns a text content that has non-breaking spaces for images, so
+// that no changes are needed in the line breaking logic.
+// https://quirks.spec.whatwg.org/#the-table-cell-width-calculation-quirk
+// static
+String NGInlineNode::TextContentForStickyImagesQuirk(
+    const NGInlineItemsData& items_data) {
+  const String& text_content = items_data.text_content;
+  for (const NGInlineItem& item : items_data.items) {
+    if (item.Type() == NGInlineItem::kAtomicInline && item.IsImage()) {
+      if (text_content.Is8Bit()) {
+        return CreateTextContentForStickyImagesQuirk(
+            text_content.Characters8(), text_content.length(),
+            base::span<const NGInlineItem>(&item, items_data.items.end()));
+      }
+      return CreateTextContentForStickyImagesQuirk(
+          text_content.Characters16(), text_content.length(),
+          base::span<const NGInlineItem>(&item, items_data.items.end()));
+    }
+  }
+  return text_content;
+}
+
 static LayoutUnit ComputeContentSize(
     NGInlineNode node,
     WritingMode container_writing_mode,
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h
index 5fe5cfb..318a6cefa 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h
@@ -72,6 +72,11 @@
     return Data().ItemsData(is_first_line);
   }
 
+  // Returns the text content to use for content sizing. This is normally the
+  // same as |items_data.text_content|, except when sticky images quirk is
+  // needed.
+  String TextContentForContentSize(const NGInlineItemsData& items_data) const;
+
   // Clear associated fragments for LayoutObjects.
   // They are associated when NGPaintFragment is constructed, but when clearing,
   // NGInlineItem provides easier and faster logic.
@@ -155,6 +160,8 @@
   }
   const NGInlineNodeData& EnsureData();
 
+  static String TextContentForStickyImagesQuirk(const NGInlineItemsData&);
+
   static void ComputeOffsetMapping(LayoutBlockFlow* layout_block_flow,
                                    NGInlineNodeData* data);
 
@@ -162,6 +169,30 @@
   friend class NGInlineNodeLegacy;
 };
 
+inline String NGInlineNode::TextContentForContentSize(
+    const NGInlineItemsData& items_data) const {
+  const String& text_content = items_data.text_content;
+  if (UNLIKELY(text_content.IsEmpty()))
+    return text_content;
+
+  // There's a special intrinsic size measure quirk for images that are direct
+  // children of table cells that have auto inline-size: When measuring
+  // intrinsic min/max inline sizes, we pretend that it's not possible to break
+  // between images, or between text and images. Note that this only applies
+  // when measuring. During actual layout, on the other hand, standard breaking
+  // rules are to be followed.
+  // See https://quirks.spec.whatwg.org/#the-table-cell-width-calculation-quirk
+  if (UNLIKELY(GetDocument().InQuirksMode())) {
+    const ComputedStyle& style = Style();
+    if (UNLIKELY(style.Display() == EDisplay::kTableCell &&
+                 style.LogicalWidth().IsIntrinsicOrAuto())) {
+      return TextContentForStickyImagesQuirk(items_data);
+    }
+  }
+
+  return text_content;
+}
+
 template <>
 struct DowncastTraits<NGInlineNode> {
   static bool AllowFrom(const NGLayoutInputNode& node) {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
index ade17fa5..90d29a1 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
@@ -43,13 +43,6 @@
   return !item_results.IsEmpty() && item_results.back().has_unpositioned_floats;
 }
 
-bool IsImage(const NGInlineItem& item) {
-  if (!item.GetLayoutObject() || !item.GetLayoutObject()->IsLayoutImage())
-    return false;
-  DCHECK(item.Type() == NGInlineItem::kAtomicInline);
-  return true;
-}
-
 LayoutUnit ComputeInlineEndSize(const NGConstraintSpace& space,
                                 const ComputedStyle* style) {
   DCHECK(style);
@@ -130,26 +123,6 @@
   return builder.ToTextFragment();
 }
 
-void PreventBreakBeforeStickyImage(
-    NGLineBreaker::WhitespaceState trailing_whitespace,
-    const String& text,
-    NGLineInfo* line_info) {
-  if (trailing_whitespace != NGLineBreaker::WhitespaceState::kNone &&
-      trailing_whitespace != NGLineBreaker::WhitespaceState::kUnknown)
-    return;
-
-  NGInlineItemResults* results = line_info->MutableResults();
-  if (results->IsEmpty())
-    return;
-
-  // If this image follows a <wbr> the image isn't sticky.
-  NGInlineItemResult* last = &results->back();
-  if (text[last->start_offset] == kZeroWidthSpaceCharacter)
-    return;
-
-  last->can_break_after = false;
-}
-
 inline void ClearNeedsLayout(const NGInlineItem& item) {
   LayoutObject* layout_object = item.GetLayoutObject();
   if (layout_object->NeedsLayout())
@@ -176,6 +149,7 @@
                              NGExclusionSpace* exclusion_space)
     : line_opportunity_(line_opportunity),
       node_(node),
+      mode_(mode),
       is_first_formatted_line_((!break_token || (!break_token->ItemIndex() &&
                                                  !break_token->TextOffset())) &&
                                node.CanContainFirstFormattedLine()),
@@ -183,13 +157,15 @@
                             node.UseFirstLineStyle()),
       in_line_height_quirks_mode_(node.InLineHeightQuirksMode()),
       items_data_(node.ItemsData(use_first_line_style_)),
-      mode_(mode),
+      text_content_(mode == NGLineBreakerMode::kContent
+                        ? items_data_.text_content
+                        : node.TextContentForContentSize(items_data_)),
       constraint_space_(space),
       exclusion_space_(exclusion_space),
       break_token_(break_token),
-      break_iterator_(items_data_.text_content),
-      shaper_(items_data_.text_content),
-      spacing_(items_data_.text_content),
+      break_iterator_(text_content_),
+      shaper_(text_content_),
+      spacing_(text_content_),
       leading_floats_(leading_floats),
       handled_leading_floats_index_(handled_leading_floats_index),
       base_direction_(node_.BaseDirection()) {
@@ -204,19 +180,6 @@
     is_after_forced_break_ = break_token->IsForcedBreak();
     items_data_.AssertOffset(item_index_, offset_);
   }
-
-  // There's a special intrinsic size measure quirk for images that are direct
-  // children of table cells that have auto inline-size: When measuring
-  // intrinsic min/max inline sizes, we pretend that it's not possible to break
-  // between images, or between text and images. Note that this only applies
-  // when measuring. During actual layout, on the other hand, standard breaking
-  // rules are to be followed.
-  // See https://quirks.spec.whatwg.org/#the-table-cell-width-calculation-quirk
-  if (node.GetDocument().InQuirksMode() &&
-      node.Style().Display() == EDisplay::kTableCell &&
-      node.Style().LogicalWidth().IsIntrinsicOrAuto() &&
-      mode != NGLineBreakerMode::kContent)
-    sticky_images_quirk_ = true;
 }
 
 // Define the destructor here, so that we can forward-declare more in the
@@ -416,13 +379,6 @@
         continue;
       return;
     }
-    if (item.Type() == NGInlineItem::kAtomicInline) {
-      if (HandleAtomicInline(item, percentage_resolution_block_size_for_min_max,
-                             line_info)) {
-        continue;
-      }
-      return;
-    }
     if (item.Type() == NGInlineItem::kCloseTag) {
       HandleCloseTag(item, line_info);
       continue;
@@ -448,6 +404,11 @@
       return;
     }
 
+    if (item.Type() == NGInlineItem::kAtomicInline) {
+      HandleAtomicInline(item, percentage_resolution_block_size_for_min_max,
+                         line_info);
+      continue;
+    }
     if (item.Type() == NGInlineItem::kOutOfFlowPositioned) {
       NGInlineItemResult* item_result = AddItem(item, line_info);
       ComputeCanBreakAfter(item_result, auto_wrap_, break_iterator_);
@@ -493,15 +454,19 @@
 // https://www.w3.org/TR/css-text-3/#line-break-details
 bool NGLineBreaker::IsAtomicInlineBeforeNoBreakSpace(
     const NGInlineItemResult& item_result) const {
+  DCHECK(auto_wrap_);
   DCHECK_EQ(item_result.item->Type(), NGInlineItem::kAtomicInline);
   const String& text = Text();
   DCHECK_GE(text.length(), item_result.end_offset);
   return text.length() > item_result.end_offset &&
-         text[item_result.end_offset] == kNoBreakSpaceCharacter;
+         text[item_result.end_offset] == kNoBreakSpaceCharacter &&
+         // Except when sticky images quirk was applied.
+         text[item_result.start_offset] != kNoBreakSpaceCharacter;
 }
 
 bool NGLineBreaker::IsAtomicInlineAfterNoBreakSpace(
     const NGInlineItemResult& item_result) const {
+  DCHECK(auto_wrap_);
   DCHECK_EQ(item_result.item->Type(), NGInlineItem::kText);
   const String& text = Text();
   DCHECK_GE(text.length(), item_result.end_offset);
@@ -515,8 +480,12 @@
   for (const NGInlineItem* item = std::next(item_result.item);
        item != items.end(); ++item) {
     DCHECK_EQ(item->StartOffset(), item_result.end_offset);
-    if (item->Type() == NGInlineItem::kAtomicInline)
+    if (item->Type() == NGInlineItem::kAtomicInline) {
+      // Except when sticky images quirk was applied.
+      if (UNLIKELY(text[item->StartOffset()] == kNoBreakSpaceCharacter))
+        return false;
       return true;
+    }
     if (item->EndOffset() > item_result.end_offset)
       break;
   }
@@ -1229,30 +1198,13 @@
   MoveToNextOf(item);
 }
 
-bool NGLineBreaker::HandleAtomicInline(
+void NGLineBreaker::HandleAtomicInline(
     const NGInlineItem& item,
     LayoutUnit percentage_resolution_block_size_for_min_max,
     NGLineInfo* line_info) {
   DCHECK_EQ(item.Type(), NGInlineItem::kAtomicInline);
   DCHECK(item.Style());
   const ComputedStyle& style = *item.Style();
-  const NGInlineItemResults& item_results = line_info->Results();
-
-  // If the sticky images quirk is enabled, and this is an image that
-  // follows text that doesn't end with something breakable, we cannot break
-  // between the two items.
-  bool is_sticky_image = sticky_images_quirk_ && IsImage(item);
-  if (UNLIKELY(is_sticky_image)) {
-    PreventBreakBeforeStickyImage(trailing_whitespace_, Text(), line_info);
-  }
-
-  // Atomic inline is handled as if it is trailable, because it can prevent
-  // break-before. Check if the line should break before this item, after the
-  // last item's |can_break_after| is finalized for the quirk above.
-  if (state_ == LineBreakState::kTrailing && CanBreakAfterLast(item_results)) {
-    line_info->SetIsLastLine(false);
-    return false;
-  }
 
   NGInlineItemResult* item_result = AddItem(item, line_info);
   item_result->should_create_line_box = true;
@@ -1301,24 +1253,11 @@
   trailing_whitespace_ = WhitespaceState::kNone;
   position_ += item_result->inline_size;
   ComputeCanBreakAfter(item_result, auto_wrap_, break_iterator_);
-  if (!item_result->can_break_after &&
+  if (!item_result->can_break_after && auto_wrap_ &&
       IsAtomicInlineBeforeNoBreakSpace(*item_result))
     item_result->can_break_after = true;
 
-  if (UNLIKELY(is_sticky_image)) {
-    const auto& items = Items();
-    if (item_index_ + 1 < items.size()) {
-      DCHECK_EQ(&item, &items[item_index_]);
-      const auto& next_item = items[item_index_ + 1];
-      // This is an image, and we don't want to break after it, unless what
-      // comes after provides a break opportunity. Look ahead. We only want to
-      // break if the next item is an atomic inline that's not an image.
-      if (next_item.Type() != NGInlineItem::kAtomicInline || IsImage(next_item))
-        item_result->can_break_after = false;
-    }
-  }
   MoveToNextOf(item);
-  return true;
 }
 
 // Performs layout and positions a float.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
index f01a802..8da18af6 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
@@ -93,9 +93,11 @@
   }
 
  private:
-  const String& Text() const { return items_data_.text_content; }
+  const String& Text() const { return text_content_; }
   const Vector<NGInlineItem>& Items() const { return items_data_.items; }
 
+  String TextContentForLineBreak() const;
+
   NGInlineItemResult* AddItem(const NGInlineItem&,
                               unsigned end_offset,
                               NGLineInfo*);
@@ -159,7 +161,7 @@
 
   void HandleControlItem(const NGInlineItem&, NGLineInfo*);
   void HandleBidiControlItem(const NGInlineItem&, NGLineInfo*);
-  bool HandleAtomicInline(
+  void HandleAtomicInline(
       const NGInlineItem&,
       LayoutUnit percentage_resolution_block_size_for_min_max,
       NGLineInfo*);
@@ -212,6 +214,8 @@
 
   NGInlineNode node_;
 
+  NGLineBreakerMode mode_;
+
   // True if this line is the "first formatted line".
   // https://www.w3.org/TR/CSS22/selector.html#first-formatted-line
   bool is_first_formatted_line_ = false;
@@ -244,13 +248,13 @@
   // the next line.
   bool is_after_forced_break_ = false;
 
-  // Set in quirks mode when we're not supposed to break inside table cells
-  // between images, and between text and images.
-  bool sticky_images_quirk_ = false;
-
   const NGInlineItemsData& items_data_;
 
-  NGLineBreakerMode mode_;
+  // The text content of this node. This is same as |items_data_.text_content|
+  // except when sticky images quirk is needed. See
+  // |NGInlineNode::TextContentForContentSize|.
+  String text_content_;
+
   const NGConstraintSpace& constraint_space_;
   NGExclusionSpace* exclusion_space_;
   scoped_refptr<const NGInlineBreakToken> break_token_;
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index bdd2600..69927b5 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -876,8 +876,9 @@
   // TODO(dgozman): figure out the better place for this check
   // to cancel lazy load both on start and commit. Perhaps
   // CancelProvisionalLoaderForNewNavigation() is a good one.
-  if (HTMLFrameOwnerElement* element = frame_->DeprecatedLocalOwner())
-    element->CancelPendingLazyLoad();
+  HTMLFrameOwnerElement* frame_owner = frame_->DeprecatedLocalOwner();
+  if (frame_owner)
+    frame_owner->CancelPendingLazyLoad();
 
   navigation_params->frame_load_type = DetermineFrameLoadType(
       navigation_params->url, navigation_params->http_method,
@@ -928,6 +929,8 @@
       frame_, navigation_type, std::move(navigation_params),
       std::move(extra_data));
 
+  FrameSwapScope frame_swap_scope(frame_owner);
+
   {
     base::AutoReset<bool> scoped_committing(&committing_navigation_, true);
     if (is_javascript_url)
diff --git a/third_party/blink/renderer/devtools/front_end/audits/lighthouse/report.css b/third_party/blink/renderer/devtools/front_end/audits/lighthouse/report.css
index 880c39b1..e6d40ba 100644
--- a/third_party/blink/renderer/devtools/front_end/audits/lighthouse/report.css
+++ b/third_party/blink/renderer/devtools/front_end/audits/lighthouse/report.css
@@ -262,6 +262,10 @@
 .lh-devtools.lh-root {
   height: 100%;
 }
+.lh-devtools.lh-root img {
+  /* Override devtools default 'min-width: 0' so svg without size in a flexbox isn't collapsed. */
+  min-width: auto;
+}
 .lh-devtools .lh-container {
   overflow-y: scroll;
   height: calc(100% - var(--topbar-height));
@@ -1161,11 +1165,6 @@
   width: var(--gauge-wrapper-width);
 }
 
-.lh-scores-header .lh-gauge--pwa__wrapper {
-  /* Can remove when this bug is resolved: https://bugs.chromium.org/p/chromium/issues/detail?id=942097 */
-  will-change: transform;
-}
-
 .lh-scorescale {
   display: inline-flex;
   margin: 12px auto 0 auto;
@@ -1391,6 +1390,11 @@
   display: block;
 }
 
+.lh-unknown pre {
+  overflow: scroll;
+  border: solid 1px var(--color-gray-200);
+}
+
 .lh-text__url > a {
   color: inherit;
   text-decoration: none;
diff --git a/third_party/blink/renderer/devtools/front_end/audits/lighthouse/report.js b/third_party/blink/renderer/devtools/front_end/audits/lighthouse/report.js
index 0dff909..3c83016 100644
--- a/third_party/blink/renderer/devtools/front_end/audits/lighthouse/report.js
+++ b/third_party/blink/renderer/devtools/front_end/audits/lighthouse/report.js
@@ -192,7 +192,7 @@
    */
   static formatNumber(number, granularity = 0.1) {
     const coarseValue = Math.round(number / granularity) * granularity;
-    return coarseValue.toLocaleString(Util.numberDateLocale);
+    return Util.numberFormatter.format(coarseValue);
   }
 
   /**
@@ -201,8 +201,7 @@
    * @return {string}
    */
   static formatBytesToKB(size, granularity = 0.1) {
-    const kbs = (Math.round(size / 1024 / granularity) * granularity)
-      .toLocaleString(Util.numberDateLocale);
+    const kbs = Util.numberFormatter.format(Math.round(size / 1024 / granularity) * granularity);
     return `${kbs}${NBSP}KB`;
   }
 
@@ -213,7 +212,7 @@
    */
   static formatMilliseconds(ms, granularity = 10) {
     const coarseTime = Math.round(ms / granularity) * granularity;
-    return `${coarseTime.toLocaleString(Util.numberDateLocale)}${NBSP}ms`;
+    return `${Util.numberFormatter.format(coarseTime)}${NBSP}ms`;
   }
 
   /**
@@ -223,7 +222,7 @@
    */
   static formatSeconds(ms, granularity = 0.1) {
     const coarseTime = Math.round(ms / 1000 / granularity) * granularity;
-    return `${coarseTime.toLocaleString(Util.numberDateLocale)}${NBSP}s`;
+    return `${Util.numberFormatter.format(coarseTime)}${NBSP}s`;
   }
 
   /**
@@ -281,6 +280,73 @@
   }
 
   /**
+   * Split a string by markdown code spans (enclosed in `backticks`), splitting
+   * into segments that were enclosed in backticks (marked as `isCode === true`)
+   * and those that outside the backticks (`isCode === false`).
+   * @param {string} text
+   * @return {Array<{isCode: true, text: string}|{isCode: false, text: string}>}
+   */
+  static splitMarkdownCodeSpans(text) {
+    /** @type {Array<{isCode: true, text: string}|{isCode: false, text: string}>} */
+    const segments = [];
+
+    // Split on backticked code spans.
+    const parts = text.split(/`(.*?)`/g);
+    for (let i = 0; i < parts.length; i ++) {
+      const text = parts[i];
+
+      // Empty strings are an artifact of splitting, not meaningful.
+      if (!text) continue;
+
+      // Alternates between plain text and code segments.
+      const isCode = i % 2 !== 0;
+      segments.push({
+        isCode,
+        text,
+      });
+    }
+
+    return segments;
+  }
+
+  /**
+   * Split a string on markdown links (e.g. [some link](https://...)) into
+   * segments of plain text that weren't part of a link (marked as
+   * `isLink === false`), and segments with text content and a URL that did make
+   * up a link (marked as `isLink === true`).
+   * @param {string} text
+   * @return {Array<{isLink: true, text: string, linkHref: string}|{isLink: false, text: string}>}
+   */
+  static splitMarkdownLink(text) {
+    /** @type {Array<{isLink: true, text: string, linkHref: string}|{isLink: false, text: string}>} */
+    const segments = [];
+
+    const parts = text.split(/\[([^\]]+?)\]\((https?:\/\/.*?)\)/g);
+    while (parts.length) {
+      // Shift off the same number of elements as the pre-split and capture groups.
+      const [preambleText, linkText, linkHref] = parts.splice(0, 3);
+
+      if (preambleText) { // Skip empty text as it's an artifact of splitting, not meaningful.
+        segments.push({
+          isLink: false,
+          text: preambleText,
+        });
+      }
+
+      // Append link if there are any.
+      if (linkText && linkHref) {
+        segments.push({
+          isLink: true,
+          text: linkText,
+          linkHref,
+        });
+      }
+    }
+
+    return segments;
+  }
+
+  /**
    * @param {URL} parsedUrl
    * @param {{numPathParts?: number, preserveQuery?: boolean, preserveHost?: boolean}=} options
    * @return {string}
@@ -489,10 +555,11 @@
    * @param {LH.Locale} locale
    */
   static setNumberDateLocale(locale) {
-    Util.numberDateLocale = locale;
-
     // When testing, use a locale with more exciting numeric formatting
-    if (Util.numberDateLocale === 'en-XA') Util.numberDateLocale = 'de';
+    if (locale === 'en-XA') locale = 'de';
+
+    Util.numberDateLocale = locale;
+    Util.numberFormatter = new Intl.NumberFormat(locale);
   }
 
   /**
@@ -552,6 +619,12 @@
 Util.numberDateLocale = 'en';
 
 /**
+ * This value stays in sync with Util.numberDateLocale.
+ * @type {Intl.NumberFormat}
+ */
+Util.numberFormatter = new Intl.NumberFormat(Util.numberDateLocale);
+
+/**
  * Report-renderer-specific strings.
  * @type {LH.I18NRendererStrings}
  */
@@ -626,7 +699,7 @@
  */
 'use strict';
 
-/* globals URL self */
+/* globals URL self Util */
 
 /** @typedef {HTMLElementTagNameMap & {[id: string]: HTMLElement}} HTMLElementByTagName */
 
@@ -727,52 +800,47 @@
   convertMarkdownLinkSnippets(text) {
     const element = this.createElement('span');
 
-    // Split on markdown links (e.g. [some link](https://...)).
-    const parts = text.split(/\[([^\]]*?)\]\((https?:\/\/.*?)\)/g);
-
-    while (parts.length) {
-      // Pop off the same number of elements as there are capture groups.
-      const [preambleText, linkText, linkHref] = parts.splice(0, 3);
-      element.appendChild(this._document.createTextNode(preambleText));
-
-      // Append link if there are any.
-      if (linkText && linkHref) {
-        const url = new URL(linkHref);
-
-        const DEVELOPERS_GOOGLE_ORIGIN = 'https://developers.google.com';
-        if (url.origin === DEVELOPERS_GOOGLE_ORIGIN) {
-          url.searchParams.set('utm_source', 'lighthouse');
-          url.searchParams.set('utm_medium', this._lighthouseChannel);
-        }
-
-        const a = this.createElement('a');
-        a.rel = 'noopener';
-        a.target = '_blank';
-        a.textContent = linkText;
-        a.href = url.href;
-        element.appendChild(a);
+    for (const segment of Util.splitMarkdownLink(text)) {
+      if (!segment.isLink) {
+        // Plain text segment.
+        element.appendChild(this._document.createTextNode(segment.text));
+        continue;
       }
+
+      // Otherwise, append any links found.
+      const url = new URL(segment.linkHref);
+
+      const DOCS_ORIGINS = ['https://developers.google.com', 'https://web.dev'];
+      if (DOCS_ORIGINS.includes(url.origin)) {
+        url.searchParams.set('utm_source', 'lighthouse');
+        url.searchParams.set('utm_medium', this._lighthouseChannel);
+      }
+
+      const a = this.createElement('a');
+      a.rel = 'noopener';
+      a.target = '_blank';
+      a.textContent = segment.text;
+      a.href = url.href;
+      element.appendChild(a);
     }
 
     return element;
   }
 
   /**
-   * @param {string} text
+   * @param {string} markdownText
    * @return {Element}
    */
-  convertMarkdownCodeSnippets(text) {
+  convertMarkdownCodeSnippets(markdownText) {
     const element = this.createElement('span');
 
-    const parts = text.split(/`(.*?)`/g); // Split on markdown code slashes
-    while (parts.length) {
-      // Pop off the same number of elements as there are capture groups.
-      const [preambleText, codeText] = parts.splice(0, 2);
-      element.appendChild(this._document.createTextNode(preambleText));
-      if (codeText) {
+    for (const segment of Util.splitMarkdownCodeSpans(markdownText)) {
+      if (segment.isCode) {
         const pre = this.createElement('code');
-        pre.textContent = codeText;
+        pre.textContent = segment.text;
         element.appendChild(pre);
+      } else {
+        element.appendChild(this._document.createTextNode(segment.text));
       }
     }
 
@@ -836,27 +904,18 @@
 }
 ;
 /*
-Details Element Polyfill 2.2.0
-Copyright © 2018 Javan Makhmali
+Details Element Polyfill 2.4.0
+Copyright © 2019 Javan Makhmali
  */
 (function() {
   "use strict";
   var element = document.createElement("details");
-  element.innerHTML = "<summary>a</summary>b";
-  element.setAttribute("style", "position: absolute; left: -9999px");
+  var elementIsNative = typeof HTMLDetailsElement != "undefined" && element instanceof HTMLDetailsElement;
   var support = {
-    open: "open" in element && elementExpands(),
+    open: "open" in element || elementIsNative,
     toggle: "ontoggle" in element
   };
-  function elementExpands() {
-    (document.body || document.documentElement).appendChild(element);
-    var closedHeight = element.offsetHeight;
-    element.open = true;
-    var openedHeight = element.offsetHeight;
-    element.parentNode.removeChild(element);
-    return closedHeight != openedHeight;
-  }
-  var styles = '\ndetails, summary {\n  display: block;\n}\ndetails:not([open]) > *:not(summary) {\n  display: none;\n}\ndetails > summary::before {\n  content: "â–º";\n  padding-right: 0.3rem;\n  font-size: 0.6rem;\n  cursor: default;\n}\ndetails[open] > summary::before {\n  content: "â–¼";\n}\n';
+  var styles = '\ndetails, summary {\n  display: block;\n}\ndetails:not([open]) > *:not(summary) {\n  display: none;\n}\nsummary::before {\n  content: "â–º";\n  padding-right: 0.3rem;\n  font-size: 0.6rem;\n  cursor: default;\n}\n[open] > summary::before {\n  content: "â–¼";\n}\n';
   var _ref = [], forEach = _ref.forEach, slice = _ref.slice;
   if (!support.open) {
     polyfillStyles();
@@ -946,7 +1005,7 @@
         forEach.call(mutations, function(mutation) {
           var target = mutation.target, attributeName = mutation.attributeName;
           if (target.tagName == "DETAILS" && attributeName == "open") {
-            triggerToggle(toggle);
+            triggerToggle(target);
           }
         });
       }).observe(document.documentElement, {
@@ -1018,7 +1077,7 @@
   }
   function triggerToggle(element) {
     var event = document.createEvent("Event");
-    event.initEvent("toggle", true, false);
+    event.initEvent("toggle", false, false);
     element.dispatchEvent(event);
   }
   function findElementsWithTagName(root, tagName) {
@@ -1104,9 +1163,9 @@
         return null;
 
       default: {
-        // @ts-ignore tsc thinks this unreachable, but ts-ignore for error message just in case.
-        const detailsType = details.type;
-        throw new Error(`Unknown type: ${detailsType}`);
+        // @ts-ignore tsc thinks this is unreachable, but be forward compatible
+        // with new unexpected detail types.
+        return this._renderUnknown(details.type, details);
       }
     }
   }
@@ -1230,6 +1289,22 @@
   }
 
   /**
+   * @param {string} type
+   * @param {*} value
+   */
+  _renderUnknown(type, value) {
+    // eslint-disable-next-line no-console
+    console.error(`Unknown details type: ${type}`, value);
+    const element = this._dom.createElement('details', 'lh-unknown');
+    this._dom.createChildOf(element, 'summary').textContent =
+      `We don't know how to render audit details of type \`${type}\`. ` +
+      'The Lighthouse version that collected this data is likely newer than the Lighthouse ' +
+      'version of the report renderer. Expand for the raw JSON.';
+    this._dom.createChildOf(element, 'pre').textContent = JSON.stringify(value, null, 2);
+    return element;
+  }
+
+  /**
    * Render a details item value for embedding in a table. Renders the value
    * based on the heading's valueType, unless the value itself has a `type`
    * property to override it.
@@ -1259,7 +1334,7 @@
           return this.renderTextURL(value.value);
         }
         default: {
-          throw new Error(`Unknown valueType: ${value.type}`);
+          return this._renderUnknown(value.type, value);
         }
       }
     }
@@ -1308,7 +1383,7 @@
         }
       }
       default: {
-        throw new Error(`Unknown valueType: ${heading.valueType}`);
+        return this._renderUnknown(heading.valueType, value);
       }
     }
   }
@@ -2201,11 +2276,11 @@
     this._document = this._dom.document();
     /** @type {ParentNode} */
     this._templateContext = this._dom.document();
+    /** @type {DropDown} */
+    this._dropDown = new DropDown(this._dom);
     /** @type {boolean} */
     this._copyAttempt = false;
     /** @type {HTMLElement} */
-    this.toolsButton; // eslint-disable-line no-unused-expressions
-    /** @type {HTMLElement} */
     this.topbarEl; // eslint-disable-line no-unused-expressions
     /** @type {HTMLElement} */
     this.scoreScaleEl; // eslint-disable-line no-unused-expressions
@@ -2216,9 +2291,7 @@
 
     this.onMediaQueryChange = this.onMediaQueryChange.bind(this);
     this.onCopy = this.onCopy.bind(this);
-    this.onToolsButtonClick = this.onToolsButtonClick.bind(this);
-    this.onToolAction = this.onToolAction.bind(this);
-    this.onKeyDown = this.onKeyDown.bind(this);
+    this.onDropDownMenuClick = this.onDropDownMenuClick.bind(this);
     this.onKeyUp = this.onKeyUp.bind(this);
     this.onChevronClick = this.onChevronClick.bind(this);
     this.collapseAllDetails = this.collapseAllDetails.bind(this);
@@ -2236,7 +2309,7 @@
     this.json = report;
 
     this._setupMediaQueryListeners();
-    this._setupToolsButton();
+    this._dropDown.setup(this.onDropDownMenuClick);
     this._setupThirdPartyFilter();
     this._setUpCollapseDetailsAfterPrinting();
     this._resetUIState();
@@ -2360,14 +2433,6 @@
     root.classList.toggle('lh-narrow', mql.matches);
   }
 
-  _setupToolsButton() {
-    this.toolsButton = this._dom.find('.lh-tools__button', this._document);
-    this.toolsButton.addEventListener('click', this.onToolsButtonClick);
-
-    const dropdown = this._dom.find('.lh-tools__dropdown', this._document);
-    dropdown.addEventListener('click', this.onToolAction);
-  }
-
   _setupThirdPartyFilter() {
     // Some audits should not display the third party filter option.
     const thirdPartyFilterAuditExclusions = [
@@ -2536,27 +2601,13 @@
     }
   }
 
-  closeToolsDropdown() {
-    this.toolsButton.classList.remove('active');
-  }
-
-  /**
-   * Click handler for tools button.
-   * @param {Event} e
-   */
-  onToolsButtonClick(e) {
-    e.preventDefault();
-    this.toolsButton.classList.toggle('active');
-    this._document.addEventListener('keydown', this.onKeyDown);
-  }
-
   /**
    * Resets the state of page before capturing the page for export.
    * When the user opens the exported HTML page, certain UI elements should
    * be in their closed state (not opened) and the templates should be unstamped.
    */
   _resetUIState() {
-    this.closeToolsDropdown();
+    this._dropDown.close();
     this._dom.resetTemplates();
   }
 
@@ -2564,7 +2615,7 @@
    * Handler for tool button.
    * @param {Event} e
    */
-  onToolAction(e) {
+  onDropDownMenuClick(e) {
     e.preventDefault();
 
     const el = /** @type {?Element} */ (e.target);
@@ -2579,12 +2630,10 @@
         break;
       case 'print-summary':
         this.collapseAllDetails();
-        this.closeToolsDropdown();
         this._print();
         break;
       case 'print-expanded':
         this.expandAllDetails();
-        this.closeToolsDropdown();
         this._print();
         break;
       case 'save-json': {
@@ -2618,8 +2667,7 @@
       }
     }
 
-    this.closeToolsDropdown();
-    this._document.removeEventListener('keydown', this.onKeyDown);
+    this._dropDown.close();
   }
 
   _print() {
@@ -2627,23 +2675,13 @@
   }
 
   /**
-   * Keydown handler for the document.
-   * @param {KeyboardEvent} e
-   */
-  onKeyDown(e) {
-    if (e.keyCode === 27) { // ESC
-      this.closeToolsDropdown();
-    }
-  }
-
-  /**
    * Keyup handler for the document.
    * @param {KeyboardEvent} e
    */
   onKeyUp(e) {
     // Ctrl+P - Expands audit details when user prints via keyboard shortcut.
     if ((e.ctrlKey || e.metaKey) && e.keyCode === 80) {
-      this.closeToolsDropdown();
+      this._dropDown.close();
     }
   }
 
@@ -2807,6 +2845,196 @@
   }
 }
 
+class DropDown {
+  /**
+   * @param {DOM} dom
+   */
+  constructor(dom) {
+    /** @type {DOM} */
+    this._dom = dom;
+    /** @type {HTMLElement} */
+    this._toggleEl; // eslint-disable-line no-unused-expressions
+    /** @type {HTMLElement} */
+    this._menuEl; // eslint-disable-line no-unused-expressions
+
+    this.onDocumentKeyDown = this.onDocumentKeyDown.bind(this);
+    this.onToggleClick = this.onToggleClick.bind(this);
+    this.onToggleKeydown = this.onToggleKeydown.bind(this);
+    this.onMenuKeydown = this.onMenuKeydown.bind(this);
+
+    this._getNextMenuItem = this._getNextMenuItem.bind(this);
+    this._getNextSelectableNode = this._getNextSelectableNode.bind(this);
+    this._getPreviousMenuItem = this._getPreviousMenuItem.bind(this);
+  }
+
+  /**
+   * @param {function(MouseEvent): any} menuClickHandler
+   */
+  setup(menuClickHandler) {
+    this._toggleEl = this._dom.find('.lh-tools__button', this._dom.document());
+    this._toggleEl.addEventListener('click', this.onToggleClick);
+    this._toggleEl.addEventListener('keydown', this.onToggleKeydown);
+
+    this._menuEl = this._dom.find('.lh-tools__dropdown', this._dom.document());
+    this._menuEl.addEventListener('keydown', this.onMenuKeydown);
+    this._menuEl.addEventListener('click', menuClickHandler);
+  }
+
+  close() {
+    this._toggleEl.classList.remove('active');
+    this._toggleEl.setAttribute('aria-expanded', 'false');
+    if (this._menuEl.contains(this._dom.document().activeElement)) {
+      // Refocus on the tools button if the drop down last had focus
+      this._toggleEl.focus();
+    }
+    this._dom.document().removeEventListener('keydown', this.onDocumentKeyDown);
+  }
+
+  /**
+   * @param {HTMLElement} firstFocusElement
+   */
+  open(firstFocusElement) {
+    if (this._toggleEl.classList.contains('active')) {
+      // If the drop down is already open focus on the element
+      firstFocusElement.focus();
+    } else {
+      // Wait for drop down transition to complete so options are focusable.
+      this._menuEl.addEventListener('transitionend', () => {
+        firstFocusElement.focus();
+      }, {once: true});
+    }
+
+    this._toggleEl.classList.add('active');
+    this._toggleEl.setAttribute('aria-expanded', 'true');
+    this._dom.document().addEventListener('keydown', this.onDocumentKeyDown);
+  }
+
+  /**
+   * Click handler for tools button.
+   * @param {Event} e
+   */
+  onToggleClick(e) {
+    e.preventDefault();
+    e.stopImmediatePropagation();
+
+    if (this._toggleEl.classList.contains('active')) {
+      this.close();
+    } else {
+      this.open(this._getNextMenuItem());
+    }
+  }
+
+  /**
+   * Handler for tool button.
+   * @param {KeyboardEvent} e
+   */
+  onToggleKeydown(e) {
+    switch (e.code) {
+      case 'ArrowUp':
+        e.preventDefault();
+        this.open(this._getPreviousMenuItem());
+        break;
+      case 'ArrowDown':
+      case 'Enter':
+      case ' ':
+        e.preventDefault();
+        this.open(this._getNextMenuItem());
+        break;
+      default:
+       // no op
+    }
+  }
+
+  /**
+   * Handler for tool DropDown.
+   * @param {KeyboardEvent} e
+   */
+  onMenuKeydown(e) {
+    const el = /** @type {?HTMLElement} */ (e.target);
+
+    switch (e.code) {
+      case 'ArrowUp':
+        e.preventDefault();
+        this._getPreviousMenuItem(el).focus();
+        break;
+      case 'ArrowDown':
+        e.preventDefault();
+        this._getNextMenuItem(el).focus();
+        break;
+      case 'Home':
+        e.preventDefault();
+        this._getNextMenuItem().focus();
+        break;
+      case 'End':
+        e.preventDefault();
+        this._getPreviousMenuItem().focus();
+        break;
+      default:
+       // no op
+    }
+  }
+
+  /**
+   * Keydown handler for the document.
+   * @param {KeyboardEvent} e
+   */
+  onDocumentKeyDown(e) {
+    if (e.keyCode === 27) { // ESC
+      this.close();
+    }
+  }
+
+  /**
+   * @param {Array<Node>} allNodes
+   * @param {?Node=} startNode
+   * @returns {Node}
+   */
+  _getNextSelectableNode(allNodes, startNode) {
+    const nodes = allNodes.filter((node) => {
+      if (!(node instanceof HTMLElement)) {
+        return false;
+      }
+
+      // 'Save as Gist' option may be disabled.
+      if (node.hasAttribute('disabled')) {
+        return false;
+      }
+
+      // 'Save as Gist' option may have display none.
+      if (window.getComputedStyle(node).display === 'none') {
+        return false;
+      }
+
+      return true;
+    });
+
+    let nextIndex = startNode ? (nodes.indexOf(startNode) + 1) : 0;
+    if (nextIndex >= nodes.length) {
+      nextIndex = 0;
+    }
+
+    return nodes[nextIndex];
+  }
+
+  /**
+   * @param {?Element=} startEl
+   * @returns {HTMLElement}
+   */
+  _getNextMenuItem(startEl) {
+    const nodes = Array.from(this._menuEl.childNodes);
+    return /** @type {HTMLElement} */ (this._getNextSelectableNode(nodes, startEl));
+  }
+
+  /**
+   * @param {?Element=} startEl
+   * @returns {HTMLElement}
+   */
+  _getPreviousMenuItem(startEl) {
+    const nodes = Array.from(this._menuEl.childNodes).reverse();
+    return /** @type {HTMLElement} */ (this._getNextSelectableNode(nodes, startEl));
+  }
+}
+
 if (typeof module !== 'undefined' && module.exports) {
   module.exports = ReportUIFeatures;
 } else {
@@ -3410,8 +3638,8 @@
 
     // Metric descriptions toggle.
     const toggleTmpl = this.dom.cloneTemplate('#tmpl-lh-metrics-toggle', this.templateContext);
-    const toggleEl = this.dom.find('.lh-metrics-toggle', toggleTmpl);
-    metricAuditsEl.append(...toggleEl.childNodes);
+    const _toggleEl = this.dom.find('.lh-metrics-toggle', toggleTmpl);
+    metricAuditsEl.append(..._toggleEl.childNodes);
 
     const metricAudits = category.auditRefs.filter(audit => audit.group === 'metrics');
     const keyMetrics = metricAudits.filter(a => a.weight >= 3);
@@ -3810,6 +4038,7 @@
     const el = this._dom.cloneTemplate('#tmpl-lh-topbar', this._templateContext);
     const metadataUrl = /** @type {HTMLAnchorElement} */ (this._dom.find('.lh-topbar__url', el));
     metadataUrl.href = metadataUrl.textContent = report.finalUrl;
+    metadataUrl.title = report.finalUrl;
     return el;
   }
 
diff --git a/third_party/blink/renderer/devtools/front_end/audits/lighthouse/template.html b/third_party/blink/renderer/devtools/front_end/audits/lighthouse/template.html
index 491925c6..e5950a40 100644
--- a/third_party/blink/renderer/devtools/front_end/audits/lighthouse/template.html
+++ b/third_party/blink/renderer/devtools/front_end/audits/lighthouse/template.html
@@ -19,7 +19,7 @@
 <head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
-  <link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAADjklEQVR4AWI08P/HQEvAQrxSQKvlECfLFYXx75xCY2qmh89GbNvOMjb3v9jOOlxnFWxj206ebQ3b7q6q+z1rNagu8/zvPSZACAABpeUAA0miMgU7SA7JjCraFGwZwECOwvL75dWjsKgWBKtx0jvWo+vkBAFbACCkByMP6nMn48+AVgXB2fzSCwsv22/lMGlUhmJ0AE7BH8dyUUDbUEgN6RzJRSeaPxhdRYR0Inel+7Hd5lBiFpkMAxACc0394//9C4voFHDiAAGLpuOXebdfdHfctgwJKaZRLRKy6ItrSis6RBnVBgGtbHyKTEmJHQoEXoBCE5BCrDeA2ogMUIGDAKEBDEhUqwgMqBYDjW4DQzmuffVdqff42/ZQYYqVcMXGZsMPyCsH3lyJSetxvEaxAQXdjR1HjfwCdIS7lo2DZke26Qe+MXO12OWkGT0O6oE7vMGkMnkYw4aN1KQgMKExhXqswfiov4+a7MQ11XPnbr/5qpKlgACAAQj94Lu271bN9DUecQasIZlNzG72llRAAKJiAi+/BSHrSFjRvQhg3DEKEqJh08tsmLTx597+f6enr4cc2Zpk57pihfX24dW7RHcOLLUbJYhJSl0ErQCI9BVXH/XrO97QasuvQQSiECa0BrQCIIJp6X9T/r8QG6L71WYSqCoIIGo2BZDUBnS/D9EA9Nun1iYvbM0MFExIDQRoKFatc1Z6zrm5uWeObJotq0BGV9FuQBWq5a4Fw3PPz848rZHstZSuA5FWAFSMP2nOppOOGpl6qh9PCSg0IFyHKjSQyDNQHTru2t75NOEe0fsf246oAmFkI6vCdnWvbQFQFCKx8vCswV8TrDLiDLgH4Nr7RAtNsrC9d8sfk7b8ls4igdNy8CQKAISlsB0FjZfd3Lfp155tf8fKI4BxZZIj/oTdVEAIAcJFOCmzauHG71I7/rdreUAgAqpDP05fDARCAQQARwEIBQSVxq0FyaLvZZtevpHa8WHw8cft6cpxlq8eAJtIhnSbWDf951yx3y13OqUuu5qyGgkxCgGFh9cDihDGbTa6BqvT1lWmrav3bmt2ZMJ4mU6TGgIC4DBzcv/JqAau1WhzSt3x9Ixk/4Jk/8J4ZrrViFMA4W6A7+WK8xcVjvyrOmVD0FbAXokcT48r+xVqLKvuJYbmpNadnlp3mpufJHOe/GXktM+r09bT8kEdq9BRYAbGSgzP7ll82U71Mc+ZFooXgwAAAABJRU5ErkJggg==">
+  <link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAEhklEQVR4AWJxL/BhIAesev1U5tcflpncgNrKIsqNIwzC9feMpDUzs70kOczMzMzJJcxwCTMzncPMnOwtzBwzMzPb0vRfeZPp0VhPS5I39V5fdiXV1/VD+9QC7OVn9BsyH1XIoEI1PfmJvLFowVV564+34DFUHudbmfDh4kVXh//7XwE+WjS/YfXZe3yr4j2rqj1AIhSB7hZ8ZtPZu/zw8cK523U4wE1/rvPfWrz4zs0m9ZdC9yUJAlASdBAgocRegfF/f3/h/PuaFsxMdwjAR0vm1+06eMMfIrhLqTWqdH4EumU2SPfMhigJAlRQbZrgrRsl9U+Y2DYDFCz3ILC9kiAiqSrMwbWT0nceEnR+9Kggc2zjOJCASDENkg0a5HfZZgDP81CM3CrQs2Z1+o7DJ6ePr8sK0AOCHv5Jjdt3evyYSaZ351VIStIxPRAUtrBYbxC6w+BZ0ivVSBKkIhJhemSyZpfB00EiPO2VjzYkxhcqXQqCWCShGplvi3y0QxqbuBurMjyJeWnkHZuAEgIQGsUBqwrfjZ+IlBgKyRJzVVYF8O6qFWdh86YzQzMrZigYmxAyfvHgLZQ/LC1CbeniW2Hkqr/PH16SgvGuf2/uzNMBwJA/njxizGPtSyAf7EziJCMGRDRdhoAC4PL1A/SrKQMAAQkEfpJAcRQdrBJ7gNwjSpJsdwK+CANBkqa1LgQB4IicV9nYUct7gaxuDJUErQIiEAiMxLVOFlKzIktPpT0ggpdpC/8YAHnxbgkUY4tAAFSR7AAXNyAAWHJrA/kHGjzg5nleuwFO7Nd/IoDw4Pm58+4jNLmYG0wRA5bErc2Mr3Y+dXTDW1VvwqbJkzMCHQ4S1GTCBOIgUHJrGdEwqzR+jAp/o2qAZelUDoQnruEEdDclJI6576AlNVfc+22XN/+Y1vnJD0Yind6UpEEvn/Hqq15EYjCW7jZCJEpnNvDgkyelDjs106kuux2AAXCSobULOWP8mLhYlpoDMK4qAFXJGk+grtH8YXVz5KJblqaG1+VUdTc0I290bmUQAriGITRbdQnom0aoFj8kx1+wMD2ifncAXUQE4SkDqN1hE0jEophs1SUwZAOhUAiMCLwRtamtTZtbbmZErSAUHbSysaoEmnrsakiMiUAURi283gN6wans9oX8rOCrj7/JP35DFD+iQ7Au/K2KE1jzx6ujjUnXFH9KjEq6ZlhsTBICrNLJf47Pv/pkHzvup1w4dmUbEei0+bcXRqJuh5kVARQ8byyYxOwNGr7A87xh1tp8sGT+uMInrwi++Xj7TQz2d27NvwEkrOflAFQGIDA5khASBCGdO2/Z/MnLPwYfv5TFhjW7QhVKAB6afwe2LpFlFsCnlQEosgQgDsdOG1/LKeNqJS4JCSPJ/i+TakwEARor7gER1Iva5JmPOJK0RUqmoPnnlzFCtmIAhAAQEIQRgDaiYPIauNXcnDlRIrWNFY3hm7PG9YRqr7IV7HrCgAC17befjEvRq2nGhAHtBqDpOuI/I1diUUAMYIxEdyejBJqLnNoszGZtfiX/CztGv2mq+sdaAAAAAElFTkSuQmCC">
   <title>Lighthouse Report</title>
   <style>/*%%LIGHTHOUSE_CSS%%*/</style>
 </head>
diff --git a/third_party/blink/renderer/devtools/front_end/audits/lighthouse/templates.html b/third_party/blink/renderer/devtools/front_end/audits/lighthouse/templates.html
index a418bf9..3cd8cde 100644
--- a/third_party/blink/renderer/devtools/front_end/audits/lighthouse/templates.html
+++ b/third_party/blink/renderer/devtools/front_end/audits/lighthouse/templates.html
@@ -245,6 +245,7 @@
       width: var(--topbar-logo-size);
       height: var(--topbar-logo-size);
       user-select: none;
+      flex: none;
     }
     .lh-topbar__logo .shape {
       fill: var(--report-text-color);
@@ -254,6 +255,9 @@
       margin: var(--topbar-padding);
       text-decoration: none;
       color: var(--report-text-color);
+      text-overflow: ellipsis;
+      overflow: hidden;
+      white-space: nowrap;
     }
 
     .lh-tools {
@@ -366,22 +370,22 @@
     <a href="" class="lh-topbar__url" target="_blank" rel="noopener"></a>
 
     <div class="lh-tools">
-      <button class="report-icon report-icon--share lh-tools__button" title="Tools menu" aria-label="Toggle report tools menu">
+      <button id="lh-tools-button" class="report-icon report-icon--share lh-tools__button" title="Tools menu" aria-label="Toggle report tools menu" aria-haspopup="menu" aria-expanded="false" aria-controls="lh-tools-dropdown">
         <svg width="100%" height="100%" viewBox="0 0 24 24">
             <path d="M0 0h24v24H0z" fill="none"/>
             <path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
         </svg>
       </button>
-      <div class="lh-tools__dropdown">
+      <div id="lh-tools-dropdown" role="menu" class="lh-tools__dropdown" aria-labelledby="lh-tools-button">
          <!-- TODO(i18n): localize tools dropdown -->
-        <a href="#" class="report-icon report-icon--print" data-action="print-summary">Print Summary</a>
-        <a href="#" class="report-icon report-icon--print" data-action="print-expanded">Print Expanded</a>
-        <a href="#" class="report-icon report-icon--copy" data-action="copy">Copy JSON</a>
-        <a href="#" class="report-icon report-icon--download" data-action="save-html">Save as HTML</a>
-        <a href="#" class="report-icon report-icon--download" data-action="save-json">Save as JSON</a>
-        <a href="#" class="report-icon report-icon--open lh-tools--viewer" data-action="open-viewer">Open in Viewer</a>
-        <a href="#" class="report-icon report-icon--open lh-tools--gist" data-action="save-gist">Save as Gist</a>
-        <a href="#" class="report-icon report-icon--dark" data-action="toggle-dark">Toggle Dark Theme</a>
+        <a role="menuitem" tabindex="-1" href="#" class="report-icon report-icon--print" data-action="print-summary">Print Summary</a>
+        <a role="menuitem" tabindex="-1" href="#" class="report-icon report-icon--print" data-action="print-expanded">Print Expanded</a>
+        <a role="menuitem" tabindex="-1" href="#" class="report-icon report-icon--copy" data-action="copy">Copy JSON</a>
+        <a role="menuitem" tabindex="-1" href="#" class="report-icon report-icon--download" data-action="save-html">Save as HTML</a>
+        <a role="menuitem" tabindex="-1" href="#" class="report-icon report-icon--download" data-action="save-json">Save as JSON</a>
+        <a role="menuitem" tabindex="-1" href="#" class="report-icon report-icon--open lh-tools--viewer" data-action="open-viewer">Open in Viewer</a>
+        <a role="menuitem" tabindex="-1" href="#" class="report-icon report-icon--open lh-tools--gist" data-action="save-gist">Save as Gist</a>
+        <a role="menuitem" tabindex="-1" href="#" class="report-icon report-icon--dark" data-action="toggle-dark">Toggle Dark Theme</a>
       </div>
     </div>
   </div>
diff --git a/third_party/blink/renderer/devtools/front_end/audits_worker/lighthouse/lighthouse-dt-bundle.js b/third_party/blink/renderer/devtools/front_end/audits_worker/lighthouse/lighthouse-dt-bundle.js
index 954aadb..3672e48 100644
--- a/third_party/blink/renderer/devtools/front_end/audits_worker/lighthouse/lighthouse-dt-bundle.js
+++ b/third_party/blink/renderer/devtools/front_end/audits_worker/lighthouse/lighthouse-dt-bundle.js
@@ -1,4 +1,4 @@
-// lighthouse, browserified. 5.2.0 (651028676d93ff0089970a8d37f8b4b4904d18de)
+// lighthouse, browserified. 5.4.0 (7e0ea3eedc4625ea2e80fe97e03bae11a80e74db)
 require=function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a;}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r);},p,p.exports,r,e,n,t);}return n[i].exports;}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o;}return r;}()({"../audits/accessibility/accesskeys":[function(require,module,exports){
 (function(__filename){
 
@@ -24,7 +24,7 @@
 
 description:'Access keys let users quickly focus a part of the page. For proper '+
 'navigation, each access key must be unique. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/accesskeys?application=lighthouse).'};
+'[Learn more](https://web.dev/accesskeys/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -48,7 +48,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/accesskeys.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/aria-allowed-attr":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/aria-allowed-attr":[function(require,module,exports){
 (function(__filename){
 
 
@@ -73,7 +73,7 @@
 
 description:'Each ARIA `role` supports a specific subset of `aria-*` attributes. '+
 'Mismatching these invalidates the `aria-*` attributes. [Learn '+
-'more](https://dequeuniversity.com/rules/axe/3.1/aria-allowed-attr?application=lighthouse).'};
+'more](https://web.dev/aria-allowed-attr/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -97,7 +97,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/aria-allowed-attr.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/aria-required-attr":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/aria-required-attr":[function(require,module,exports){
 (function(__filename){
 
 
@@ -121,7 +121,7 @@
 failureTitle:'`[role]`s do not have all required `[aria-*]` attributes',
 
 description:'Some ARIA roles have required attributes that describe the state '+
-'of the element to screen readers. [Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-required-attr?application=lighthouse).'};
+'of the element to screen readers. [Learn more](https://web.dev/aria-required-attr/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -145,7 +145,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/aria-required-attr.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/aria-required-children":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/aria-required-children":[function(require,module,exports){
 (function(__filename){
 
 
@@ -165,14 +165,15 @@
 
 const UIStrings={
 
-title:'Elements with `[role]` that require specific children `[role]`s, are present',
+title:'Elements with an ARIA `[role]` that require children to contain a specific '+
+'`[role]` have all required children.',
 
-failureTitle:'Elements with `[role]` that require specific children `[role]`s, '+
-'are missing.',
+failureTitle:'Elements with an ARIA `[role]` that require children to contain a specific '+
+'`[role]` are missing some or all of those required children.',
 
 description:'Some ARIA parent roles must contain specific child roles to perform '+
 'their intended accessibility functions. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-required-children?application=lighthouse).'};
+'[Learn more](https://web.dev/aria-required-children/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -196,7 +197,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/aria-required-children.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/aria-required-parent":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/aria-required-parent":[function(require,module,exports){
 (function(__filename){
 
 
@@ -222,7 +223,7 @@
 
 description:'Some ARIA child roles must be contained by specific parent roles to '+
 'properly perform their intended accessibility functions. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-required-parent?application=lighthouse).'};
+'[Learn more](https://web.dev/aria-required-parent/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -246,7 +247,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/aria-required-parent.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/aria-roles":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/aria-roles":[function(require,module,exports){
 (function(__filename){
 
 
@@ -271,7 +272,7 @@
 
 description:'ARIA roles must have valid values in order to perform their '+
 'intended accessibility functions. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-roles?application=lighthouse).'};
+'[Learn more](https://web.dev/aria-roles/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -295,7 +296,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/aria-roles.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/aria-valid-attr-value":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/aria-valid-attr-value":[function(require,module,exports){
 (function(__filename){
 
 
@@ -320,7 +321,7 @@
 
 description:'Assistive technologies, like screen readers, can\'t interpret ARIA '+
 'attributes with invalid values. [Learn '+
-'more](https://dequeuniversity.com/rules/axe/3.1/aria-valid-attr-value?application=lighthouse).'};
+'more](https://web.dev/aria-valid-attr-value/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -344,7 +345,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/aria-valid-attr-value.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/aria-valid-attr":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/aria-valid-attr":[function(require,module,exports){
 (function(__filename){
 
 
@@ -369,7 +370,7 @@
 
 description:'Assistive technologies, like screen readers, can\'t interpret ARIA '+
 'attributes with invalid names. [Learn '+
-'more](https://dequeuniversity.com/rules/axe/3.1/aria-valid-attr?application=lighthouse).'};
+'more](https://web.dev/aria-valid-attr/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -393,7 +394,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/aria-valid-attr.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/audio-caption":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/audio-caption":[function(require,module,exports){
 (function(__filename){
 
 
@@ -420,7 +421,7 @@
 description:'Captions make audio elements usable for deaf or hearing-impaired users, '+
 'providing critical information such as who is talking, what they\'re saying, '+
 'and other non-speech information. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/audio-caption?application=lighthouse).'};
+'[Learn more](https://web.dev/audio-caption/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -444,7 +445,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/audio-caption.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/button-name":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/button-name":[function(require,module,exports){
 (function(__filename){
 
 
@@ -469,7 +470,7 @@
 
 description:'When a button doesn\'t have an accessible name, screen readers announce it '+
 'as "button", making it unusable for users who rely on screen readers. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/button-name?application=lighthouse).'};
+'[Learn more](https://web.dev/button-name/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -493,7 +494,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/button-name.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/bypass":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/bypass":[function(require,module,exports){
 (function(__filename){
 
 
@@ -519,7 +520,7 @@
 
 description:'Adding ways to bypass repetitive content lets keyboard users navigate the '+
 'page more efficiently. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/bypass?application=lighthouse).'};
+'[Learn more](https://web.dev/bypass/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -543,7 +544,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/bypass.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/color-contrast":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/color-contrast":[function(require,module,exports){
 (function(__filename){
 
 
@@ -569,7 +570,7 @@
 'sufficient contrast ratio.',
 
 description:'Low-contrast text is difficult or impossible for many users to read. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/color-contrast?application=lighthouse).'};
+'[Learn more](https://web.dev/color-contrast/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -593,7 +594,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/color-contrast.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/definition-list":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/definition-list":[function(require,module,exports){
 (function(__filename){
 
 
@@ -620,7 +621,7 @@
 
 description:'When definition lists are not properly marked up, screen readers may produce '+
 'confusing or inaccurate output. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/definition-list?application=lighthouse).'};
+'[Learn more](https://web.dev/definition-list/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -644,7 +645,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/definition-list.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/dlitem":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/dlitem":[function(require,module,exports){
 (function(__filename){
 
 
@@ -669,7 +670,7 @@
 
 description:'Definition list items (`<dt>` and `<dd>`) must be wrapped in a '+
 'parent `<dl>` element to ensure that screen readers can properly announce them. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/dlitem?application=lighthouse).'};
+'[Learn more](https://web.dev/dlitem/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -693,7 +694,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/dlitem.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/document-title":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/document-title":[function(require,module,exports){
 (function(__filename){
 
 
@@ -718,7 +719,7 @@
 
 description:'The title gives screen reader users an overview of the page, and search '+
 'engine users rely on it heavily to determine if a page is relevant to their search. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/title).'};
+'[Learn more](https://web.dev/document-title/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -742,7 +743,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/document-title.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/duplicate-id":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/duplicate-id":[function(require,module,exports){
 (function(__filename){
 
 
@@ -767,7 +768,7 @@
 
 description:'The value of an id attribute must be unique to prevent '+
 'other instances from being overlooked by assistive technologies. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/duplicate-id?application=lighthouse).'};
+'[Learn more](https://web.dev/duplicate-id/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -791,7 +792,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/duplicate-id.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/frame-title":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/frame-title":[function(require,module,exports){
 (function(__filename){
 
 
@@ -815,7 +816,7 @@
 failureTitle:'`<frame>` or `<iframe>` elements do not have a title',
 
 description:'Screen reader users rely on frame titles to describe the contents of frames. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/frame-title?application=lighthouse).'};
+'[Learn more](https://web.dev/frame-title/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -839,7 +840,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/frame-title.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/html-has-lang":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/html-has-lang":[function(require,module,exports){
 (function(__filename){
 
 
@@ -866,7 +867,7 @@
 'that the page is in the default language that the user chose when setting up the '+
 'screen reader. If the page isn\'t actually in the default language, then the screen '+
 'reader might not announce the page\'s text correctly. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/html-has-lang?application=lighthouse).'};
+'[Learn more](https://web.dev/html-has-lang/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -890,7 +891,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/html-has-lang.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/html-lang-valid":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/html-lang-valid":[function(require,module,exports){
 (function(__filename){
 
 
@@ -916,7 +917,7 @@
 
 description:'Specifying a valid [BCP 47 language](https://www.w3.org/International/questions/qa-choosing-language-tags#question) '+
 'helps screen readers announce text properly. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/valid-lang?application=lighthouse).'};
+'[Learn more](https://web.dev/html-lang-valid/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -940,7 +941,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/html-lang-valid.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/image-alt":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/image-alt":[function(require,module,exports){
 (function(__filename){
 
 
@@ -965,7 +966,7 @@
 
 description:'Informative elements should aim for short, descriptive alternate text. '+
 'Decorative elements can be ignored with an empty alt attribute. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/image-alt?application=lighthouse).'};
+'[Learn more](https://web.dev/image-alt/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -989,7 +990,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/image-alt.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/input-image-alt":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/input-image-alt":[function(require,module,exports){
 (function(__filename){
 
 
@@ -1014,7 +1015,7 @@
 
 description:'When an image is being used as an `<input>` button, providing alternative '+
 'text can help screen reader users understand the purpose of the button. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/input-image-alt?application=lighthouse).'};
+'[Learn more](https://web.dev/input-image-alt/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -1038,7 +1039,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/input-image-alt.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/label":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/label":[function(require,module,exports){
 (function(__filename){
 
 
@@ -1063,7 +1064,7 @@
 
 description:'Labels ensure that form controls are announced properly by assistive '+
 'technologies, like screen readers. [Learn '+
-'more](https://dequeuniversity.com/rules/axe/3.1/label?application=lighthouse).'};
+'more](https://web.dev/label/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -1087,7 +1088,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/label.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/layout-table":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/layout-table":[function(require,module,exports){
 (function(__filename){
 
 
@@ -1116,7 +1117,7 @@
 description:'A table being used for layout purposes should not include data elements, '+
 'such as the th or caption elements or the summary attribute, because this can '+
 'create a confusing experience for screen reader users. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/layout-table?application=lighthouse).'};
+'[Learn more](https://web.dev/layout-table/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -1140,7 +1141,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/layout-table.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/link-name":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/link-name":[function(require,module,exports){
 (function(__filename){
 
 
@@ -1166,7 +1167,7 @@
 description:'Link text (and alternate text for images, when used as links) that is '+
 'discernible, unique, and focusable improves the navigation experience for '+
 'screen reader users. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/link-name?application=lighthouse).'};
+'[Learn more](https://web.dev/link-name/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -1190,7 +1191,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/link-name.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/listitem":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/listitem":[function(require,module,exports){
 (function(__filename){
 
 
@@ -1216,7 +1217,7 @@
 
 description:'Screen readers require list items (`<li>`) to be contained within a '+
 'parent `<ul>` or `<ol>` to be announced properly. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/listitem?application=lighthouse).'};
+'[Learn more](https://web.dev/listitem/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -1240,7 +1241,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/listitem.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/list":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/list":[function(require,module,exports){
 (function(__filename){
 
 
@@ -1267,7 +1268,7 @@
 
 description:'Screen readers have a specific way of announcing lists. Ensuring proper list '+
 'structure aids screen reader output. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/list?application=lighthouse).'};
+'[Learn more](https://web.dev/list/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -1291,7 +1292,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/list.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/manual/custom-controls-labels":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/manual/custom-controls-labels":[function(require,module,exports){
 
 
 
@@ -1313,7 +1314,7 @@
 static get meta(){
 return Object.assign({
 id:'custom-controls-labels',
-description:'Custom interactive controls have associated labels, provided by aria-label or aria-labelledby. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).',
+description:'Custom interactive controls have associated labels, provided by aria-label or aria-labelledby. [Learn more](https://web.dev/custom-controls-labels/).',
 title:'Custom controls have associated labels'},
 super.partialMeta);
 }}
@@ -1343,7 +1344,7 @@
 static get meta(){
 return Object.assign({
 id:'custom-controls-roles',
-description:'Custom interactive controls have appropriate ARIA roles. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).',
+description:'Custom interactive controls have appropriate ARIA roles. [Learn more](https://web.dev/custom-control-roles/).',
 title:'Custom controls have ARIA roles'},
 super.partialMeta);
 }}
@@ -1373,7 +1374,7 @@
 static get meta(){
 return Object.assign({
 id:'focus-traps',
-description:'A user can tab into and out of any control or region without accidentally trapping their focus. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).',
+description:'A user can tab into and out of any control or region without accidentally trapping their focus. [Learn more](https://web.dev/focus-traps/).',
 title:'User focus is not accidentally trapped in a region'},
 super.partialMeta);
 }}
@@ -1403,7 +1404,7 @@
 static get meta(){
 return Object.assign({
 id:'focusable-controls',
-description:'Custom interactive controls are keyboard focusable and display a focus indicator. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).',
+description:'Custom interactive controls are keyboard focusable and display a focus indicator. [Learn more](https://web.dev/focusable-controls/).',
 title:'Interactive controls are keyboard focusable'},
 super.partialMeta);
 }}
@@ -1433,7 +1434,7 @@
 static get meta(){
 return Object.assign({
 id:'heading-levels',
-description:'Headings are used to create an outline for the page and heading levels are not skipped. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#take_advantage_of_headings_and_landmarks).',
+description:'Headings are used to create an outline for the page and heading levels are not skipped. [Learn more](https://web.dev/heading-levels/).',
 title:'Headings don\'t skip levels'},
 super.partialMeta);
 }}
@@ -1463,7 +1464,7 @@
 static get meta(){
 return Object.assign({
 id:'interactive-element-affordance',
-description:'Interactive elements, such as links and buttons, should indicate their state and be distinguishable from non-interactive elements. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#interactive_elements_like_links_and_buttons_should_indicate_their_purpose_and_state).',
+description:'Interactive elements, such as links and buttons, should indicate their state and be distinguishable from non-interactive elements. [Learn more](https://web.dev/interactive-element-affordance/).',
 title:'Interactive elements indicate their purpose and state'},
 super.partialMeta);
 }}
@@ -1493,7 +1494,7 @@
 static get meta(){
 return Object.assign({
 id:'logical-tab-order',
-description:'Tabbing through the page follows the visual layout. Users cannot focus elements that are offscreen. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).',
+description:'Tabbing through the page follows the visual layout. Users cannot focus elements that are offscreen. [Learn more](https://web.dev/logical-tab-order/).',
 title:'The page has a logical tab order'},
 super.partialMeta);
 }}
@@ -1523,7 +1524,7 @@
 static get meta(){
 return Object.assign({
 id:'managed-focus',
-description:'If new content, such as a dialog, is added to the page, the user\'s focus is directed to it. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).',
+description:'If new content, such as a dialog, is added to the page, the user\'s focus is directed to it. [Learn more](https://web.dev/managed-focus/).',
 title:'The user\'s focus is directed to new content added to the page'},
 super.partialMeta);
 }}
@@ -1554,7 +1555,7 @@
 static get meta(){
 return Object.assign({
 id:'offscreen-content-hidden',
-description:'Offscreen content is hidden with display: none or aria-hidden=true. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).',
+description:'Offscreen content is hidden with display: none or aria-hidden=true. [Learn more](https://web.dev/offscreen-content-hidden/).',
 title:'Offscreen content is hidden from assistive technology'},
 super.partialMeta);
 }}
@@ -1584,7 +1585,7 @@
 static get meta(){
 return Object.assign({
 id:'use-landmarks',
-description:'Landmark elements (<main>, <nav>, etc.) are used to improve the keyboard navigation of the page for assistive technology. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#take_advantage_of_headings_and_landmarks).',
+description:'Landmark elements (<main>, <nav>, etc.) are used to improve the keyboard navigation of the page for assistive technology. [Learn more](https://web.dev/use-landmarks/).',
 title:'HTML5 landmark elements are used to improve navigation'},
 super.partialMeta);
 }}
@@ -1614,7 +1615,7 @@
 static get meta(){
 return Object.assign({
 id:'visual-order-follows-dom',
-description:'DOM order matches the visual order, improving navigation for assistive technology. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).',
+description:'DOM order matches the visual order, improving navigation for assistive technology. [Learn more](https://web.dev/visual-order-follows-dom/).',
 title:'Visual order on the page follows DOM order'},
 super.partialMeta);
 }}
@@ -1648,7 +1649,7 @@
 description:'Users do not expect a page to refresh automatically, and doing so will move '+
 'focus back to the top of the page. This may create a frustrating or '+
 'confusing experience. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/meta-refresh?application=lighthouse).'};
+'[Learn more](https://web.dev/meta-refresh/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -1672,7 +1673,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/meta-refresh.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/meta-viewport":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/meta-viewport":[function(require,module,exports){
 (function(__filename){
 
 
@@ -1699,7 +1700,7 @@
 
 description:'Disabling zooming is problematic for users with low vision who rely on '+
 'screen magnification to properly see the contents of a web page. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/meta-viewport?application=lighthouse).'};
+'[Learn more](https://web.dev/meta-viewport/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -1723,7 +1724,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/meta-viewport.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/object-alt":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/object-alt":[function(require,module,exports){
 (function(__filename){
 
 
@@ -1748,7 +1749,7 @@
 
 description:'Screen readers cannot translate non-text content. Adding alt text to '+
 '`<object>` elements helps screen readers convey meaning to users. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/object-alt?application=lighthouse).'};
+'[Learn more](https://web.dev/object-alt/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -1772,7 +1773,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/object-alt.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/tabindex":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/tabindex":[function(require,module,exports){
 (function(__filename){
 
 
@@ -1797,7 +1798,7 @@
 
 description:'A value greater than 0 implies an explicit navigation ordering. '+
 'Although technically valid, this often creates frustrating experiences '+
-'for users who rely on assistive technologies. [Learn more](https://dequeuniversity.com/rules/axe/3.1/tabindex?application=lighthouse).'};
+'for users who rely on assistive technologies. [Learn more](https://web.dev/tabindex/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -1821,7 +1822,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/tabindex.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/td-headers-attr":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/td-headers-attr":[function(require,module,exports){
 (function(__filename){
 
 
@@ -1841,16 +1842,16 @@
 
 const UIStrings={
 
-title:'Cells in a `<table>` element that use the `[headers]` attribute only refer '+
-'to other cells of that same table.',
+title:'Cells in a `<table>` element that use the `[headers]` attribute refer '+
+'to table cells within the same table.',
 
-failureTitle:'Cells in a `<table>` element that use the `[headers]` '+
-'attribute refers to other cells of that same table.',
+failureTitle:'Cells in a `<table>` element that use the `[headers]` attribute refer '+
+'to an element `id` not found within the same table.',
 
 description:'Screen readers have features to make navigating tables easier. Ensuring '+
 '`<td>` cells using the `[headers]` attribute only refer to other cells in the same '+
 'table may improve the experience for screen reader users. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/td-headers-attr?application=lighthouse).'};
+'[Learn more](https://web.dev/td-headers-attr/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -1874,7 +1875,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/td-headers-attr.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/th-has-data-cells":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/th-has-data-cells":[function(require,module,exports){
 (function(__filename){
 
 
@@ -1902,7 +1903,7 @@
 description:'Screen readers have features to make navigating tables easier. Ensuring '+
 'table headers always refer to some set of cells may improve the experience for screen '+
 'reader users. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/th-has-data-cells?application=lighthouse).'};
+'[Learn more](https://web.dev/th-has-data-cells/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -1926,7 +1927,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/th-has-data-cells.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/valid-lang":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/valid-lang":[function(require,module,exports){
 (function(__filename){
 
 
@@ -1951,7 +1952,7 @@
 
 description:'Specifying a valid [BCP 47 language](https://www.w3.org/International/questions/qa-choosing-language-tags#question) '+
 'on elements helps ensure that text is pronounced correctly by a screen reader. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/valid-lang?application=lighthouse).'};
+'[Learn more](https://web.dev/valid-lang/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -1975,7 +1976,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/valid-lang.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/video-caption":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/video-caption":[function(require,module,exports){
 (function(__filename){
 
 
@@ -2001,7 +2002,7 @@
 
 description:'When a video provides a caption it is easier for deaf and hearing impaired '+
 'users to access its information. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/video-caption?application=lighthouse).'};
+'[Learn more](https://web.dev/video-caption/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -2025,7 +2026,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/video-caption.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/accessibility/video-description":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/accessibility/video-description":[function(require,module,exports){
 (function(__filename){
 
 
@@ -2051,7 +2052,7 @@
 
 description:'Audio descriptions provide relevant information for videos that dialogue '+
 'cannot, such as facial expressions and scenes. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/3.1/video-description?application=lighthouse).'};
+'[Learn more](https://web.dev/video-description/).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -2075,7 +2076,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/video-description.js");
-},{"../../lib/i18n/i18n.js":68,"./axe-audit.js":2}],"../audits/apple-touch-icon":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./axe-audit.js":2}],"../audits/apple-touch-icon":[function(require,module,exports){
 (function(__filename){
 
 
@@ -2097,9 +2098,9 @@
 
 failureTitle:'Does not provide a valid `apple-touch-icon`',
 
-description:'For ideal appearance on iOS when users add to the home screen, define an '+
-'apple-touch-icon. It must point to a non-transparent 192px (or 180px) square PNG. '+
-'[Learn More](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/).',
+description:'For ideal appearance on iOS when users add a progressive web app to the home '+
+'screen, define an `apple-touch-icon`. It must point to a non-transparent 192px (or 180px) '+
+'square PNG. [Learn More](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/).',
 
 precomposedWarning:'`apple-touch-icon-precomposed` is out of date; '+
 '`apple-touch-icon` is preferred.'};
@@ -2150,7 +2151,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/apple-touch-icon.js");
-},{"../lib/i18n/i18n.js":68,"./audit.js":3}],"../audits/bootup-time":[function(require,module,exports){
+},{"../lib/i18n/i18n.js":67,"./audit.js":3}],"../audits/bootup-time":[function(require,module,exports){
 (function(__filename){
 
 
@@ -2354,7 +2355,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/bootup-time.js");
-},{"../computed/main-thread-tasks.js":12,"../computed/network-records.js":33,"../lib/i18n/i18n.js":68,"../lib/network-request.js":76,"../lib/tracehouse/task-groups.js":85,"./audit.js":3}],"../audits/byte-efficiency/efficient-animated-content":[function(require,module,exports){
+},{"../computed/main-thread-tasks.js":12,"../computed/network-records.js":33,"../lib/i18n/i18n.js":67,"../lib/network-request.js":75,"../lib/tracehouse/task-groups.js":84,"./audit.js":3}],"../audits/byte-efficiency/efficient-animated-content":[function(require,module,exports){
 (function(__filename){
 
 
@@ -2450,7 +2451,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js");
-},{"../../lib/i18n/i18n.js":68,"../../lib/network-request.js":76,"./byte-efficiency-audit.js":4}],"../audits/byte-efficiency/offscreen-images":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../../lib/network-request.js":75,"./byte-efficiency-audit.js":4}],"../audits/byte-efficiency/offscreen-images":[function(require,module,exports){
 (function(__filename){
 
 
@@ -2697,7 +2698,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/byte-efficiency/offscreen-images.js");
-},{"../../computed/metrics/interactive.js":18,"../../computed/trace-of-tab.js":38,"../../lib/i18n/i18n.js":68,"../../lib/sentry.js":79,"../../lib/url-shim.js":"url","./byte-efficiency-audit.js":4}],"../audits/byte-efficiency/render-blocking-resources":[function(require,module,exports){
+},{"../../computed/metrics/interactive.js":18,"../../computed/trace-of-tab.js":38,"../../lib/i18n/i18n.js":67,"../../lib/sentry.js":78,"../../lib/url-shim.js":"url","./byte-efficiency-audit.js":4}],"../audits/byte-efficiency/render-blocking-resources":[function(require,module,exports){
 (function(__filename){
 
 
@@ -2934,7 +2935,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js");
-},{"../../computed/load-simulator.js":10,"../../computed/metrics/first-contentful-paint.js":15,"../../computed/trace-of-tab.js":38,"../../lib/dependency-graph/base-node.js":57,"../../lib/i18n/i18n.js":68,"../../lib/network-request.js":76,"../audit.js":3,"./byte-efficiency-audit.js":4,"./unused-css-rules.js":"../audits/byte-efficiency/unused-css-rules"}],"../audits/byte-efficiency/total-byte-weight":[function(require,module,exports){
+},{"../../computed/load-simulator.js":10,"../../computed/metrics/first-contentful-paint.js":15,"../../computed/trace-of-tab.js":38,"../../lib/dependency-graph/base-node.js":57,"../../lib/i18n/i18n.js":67,"../../lib/network-request.js":75,"../audit.js":3,"./byte-efficiency-audit.js":4,"./unused-css-rules.js":"../audits/byte-efficiency/unused-css-rules"}],"../audits/byte-efficiency/total-byte-weight":[function(require,module,exports){
 (function(__filename){
 
 
@@ -3054,7 +3055,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/byte-efficiency/total-byte-weight.js");
-},{"../../computed/network-records.js":33,"../../lib/i18n/i18n.js":68,"./byte-efficiency-audit.js":4}],"../audits/byte-efficiency/unminified-css":[function(require,module,exports){
+},{"../../computed/network-records.js":33,"../../lib/i18n/i18n.js":67,"./byte-efficiency-audit.js":4}],"../audits/byte-efficiency/unminified-css":[function(require,module,exports){
 (function(__filename){
 
 
@@ -3175,7 +3176,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/byte-efficiency/unminified-css.js");
-},{"../../lib/i18n/i18n.js":68,"../../lib/minification-estimator.js":74,"./byte-efficiency-audit.js":4,"./unused-css-rules.js":"../audits/byte-efficiency/unused-css-rules"}],"../audits/byte-efficiency/unminified-javascript":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../../lib/minification-estimator.js":73,"./byte-efficiency-audit.js":4,"./unused-css-rules.js":"../audits/byte-efficiency/unused-css-rules"}],"../audits/byte-efficiency/unminified-javascript":[function(require,module,exports){
 (function(__filename){
 
 
@@ -3297,7 +3298,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/byte-efficiency/unminified-javascript.js");
-},{"../../lib/i18n/i18n.js":68,"../../lib/minification-estimator.js":74,"./byte-efficiency-audit.js":4}],"../audits/byte-efficiency/unused-css-rules":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../../lib/minification-estimator.js":73,"./byte-efficiency-audit.js":4}],"../audits/byte-efficiency/unused-css-rules":[function(require,module,exports){
 (function(__filename){
 
 
@@ -3320,7 +3321,9 @@
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
 
-const IGNORE_THRESHOLD_IN_BYTES=2048;
+
+
+const IGNORE_THRESHOLD_IN_BYTES=10*1024;
 const PREVIEW_LENGTH=100;
 
 
@@ -3492,7 +3495,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/byte-efficiency/unused-css-rules.js");
-},{"../../lib/i18n/i18n.js":68,"./byte-efficiency-audit.js":4}],"../audits/byte-efficiency/unused-javascript":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./byte-efficiency-audit.js":4}],"../audits/byte-efficiency/unused-javascript":[function(require,module,exports){
 (function(__filename){
 
 
@@ -3630,7 +3633,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/byte-efficiency/unused-javascript.js");
-},{"../../lib/i18n/i18n.js":68,"./byte-efficiency-audit.js":4}],"../audits/byte-efficiency/uses-long-cache-ttl":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"./byte-efficiency-audit.js":4}],"../audits/byte-efficiency/uses-long-cache-ttl":[function(require,module,exports){
 (function(__filename){
 
 
@@ -3937,7 +3940,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js");
-},{"../../computed/network-records.js":33,"../../lib/i18n/i18n.js":68,"../../lib/network-request.js":76,"../../lib/statistics.js":82,"../../lib/url-shim.js":"url","../audit.js":3,"assert":92,"parse-cache-control":142}],"../audits/byte-efficiency/uses-optimized-images":[function(require,module,exports){
+},{"../../computed/network-records.js":33,"../../lib/i18n/i18n.js":67,"../../lib/network-request.js":75,"../../lib/statistics.js":81,"../../lib/url-shim.js":"url","../audit.js":3,"assert":91,"parse-cache-control":138}],"../audits/byte-efficiency/uses-optimized-images":[function(require,module,exports){
 (function(__filename){
 
 
@@ -4076,7 +4079,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js");
-},{"../../lib/i18n/i18n.js":68,"../../lib/url-shim.js":"url","./byte-efficiency-audit.js":4}],"../audits/byte-efficiency/uses-responsive-images":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../../lib/url-shim.js":"url","./byte-efficiency-audit.js":4}],"../audits/byte-efficiency/uses-responsive-images":[function(require,module,exports){
 (function(__filename){
 
 
@@ -4222,7 +4225,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js");
-},{"../../lib/i18n/i18n.js":68,"../../lib/sentry.js":79,"../../lib/url-shim.js":"url","./byte-efficiency-audit.js":4}],"../audits/byte-efficiency/uses-text-compression":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../../lib/sentry.js":78,"../../lib/url-shim.js":"url","./byte-efficiency-audit.js":4}],"../audits/byte-efficiency/uses-text-compression":[function(require,module,exports){
 (function(__filename){
 
 
@@ -4326,7 +4329,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/byte-efficiency/uses-text-compression.js");
-},{"../../lib/i18n/i18n.js":68,"../../lib/url-shim.js":"url","./byte-efficiency-audit.js":4}],"../audits/byte-efficiency/uses-webp-images":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../../lib/url-shim.js":"url","./byte-efficiency-audit.js":4}],"../audits/byte-efficiency/uses-webp-images":[function(require,module,exports){
 (function(__filename){
 
 
@@ -4464,7 +4467,8 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/byte-efficiency/uses-webp-images.js");
-},{"../../lib/i18n/i18n.js":68,"../../lib/url-shim.js":"url","./byte-efficiency-audit.js":4}],"../audits/content-width":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../../lib/url-shim.js":"url","./byte-efficiency-audit.js":4}],"../audits/content-width":[function(require,module,exports){
+(function(__filename){
 
 
 
@@ -4473,6 +4477,27 @@
 'use strict';
 
 const Audit=require('./audit.js');
+const i18n=require('../lib/i18n/i18n.js');
+
+const UIStrings={
+
+title:'Content is sized correctly for the viewport',
+
+failureTitle:'Content is not sized correctly for the viewport',
+
+description:'If the width of your app\'s content doesn\'t match the width '+
+'of the viewport, your app might not be optimized for mobile screens. '+
+'[Learn more](https://web.dev/content-width).',
+
+
+
+
+
+explanation:'The viewport size of {innerWidth}px does not match the window '+
+'size of {outerWidth}px.'};
+
+
+const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
 
 class ContentWidth extends Audit{
 
@@ -4481,11 +4506,9 @@
 static get meta(){
 return{
 id:'content-width',
-title:'Content is sized correctly for the viewport',
-failureTitle:'Content is not sized correctly for the viewport',
-description:'If the width of your app\'s content doesn\'t match the width '+
-'of the viewport, your app might not be optimized for mobile screens. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/content-sized-correctly-for-viewport).',
+title:str_(UIStrings.title),
+failureTitle:str_(UIStrings.failureTitle),
+description:str_(UIStrings.description),
 requiredArtifacts:['ViewportDimensions','TestedAsMobileDevice']};
 
 }
@@ -4500,37 +4523,32 @@
 const windowWidth=artifacts.ViewportDimensions.outerWidth;
 const widthsMatch=viewportWidth===windowWidth;
 
-if(IsMobile){
-return{
-score:Number(widthsMatch),
-explanation:this.createExplanation(widthsMatch,artifacts.ViewportDimensions)};
-
-}else{
+if(!IsMobile){
 return{
 score:1,
 notApplicable:true};
 
 }
+
+let explanation='';
+if(!widthsMatch){
+explanation=str_(UIStrings.explanation,
+{innerWidth:artifacts.ViewportDimensions.innerWidth,
+outerWidth:artifacts.ViewportDimensions.outerWidth});
 }
 
+return{
+score:Number(widthsMatch),
+explanation};
 
-
-
-
-
-static createExplanation(match,artifact){
-if(match){
-return'';
-}
-
-return'The viewport size is '+artifact.innerWidth+'px, '+
-'whereas the window size is '+artifact.outerWidth+'px.';
 }}
 
 
 module.exports=ContentWidth;
+module.exports.UIStrings=UIStrings;
 
-},{"./audit.js":3}],"../audits/critical-request-chains":[function(require,module,exports){
+}).call(this,"/lighthouse-core/audits/content-width.js");
+},{"../lib/i18n/i18n.js":67,"./audit.js":3}],"../audits/critical-request-chains":[function(require,module,exports){
 (function(__filename){
 
 
@@ -4758,7 +4776,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/critical-request-chains.js");
-},{"../computed/critical-request-chains.js":9,"../lib/i18n/i18n.js":68,"./audit.js":3}],"../audits/deprecations":[function(require,module,exports){
+},{"../computed/critical-request-chains.js":9,"../lib/i18n/i18n.js":67,"./audit.js":3}],"../audits/deprecations":[function(require,module,exports){
 (function(__filename){
 
 
@@ -4783,7 +4801,7 @@
 failureTitle:'Uses deprecated APIs',
 
 description:'Deprecated APIs will eventually be removed from the browser. '+
-'[Learn more](https://www.chromestatus.com/features#deprecated).',
+'[Learn more](https://web.dev/deprecations).',
 
 displayValue:`{itemCount, plural,
     =1 {1 warning found}
@@ -4829,7 +4847,7 @@
 
 
 const headings=[
-{key:'value',itemType:'code',text:str_(UIStrings.columnDeprecate)},
+{key:'value',itemType:'text',text:str_(UIStrings.columnDeprecate)},
 {key:'url',itemType:'url',text:str_(i18n.UIStrings.columnURL)},
 {key:'lineNumber',itemType:'text',text:str_(UIStrings.columnLine)}];
 
@@ -4855,7 +4873,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/deprecations.js");
-},{"../lib/i18n/i18n.js":68,"./audit.js":3}],"../audits/diagnostics":[function(require,module,exports){
+},{"../lib/i18n/i18n.js":67,"./audit.js":3}],"../audits/diagnostics":[function(require,module,exports){
 
 
 
@@ -4959,7 +4977,10 @@
 failureTitle:'Uses Application Cache',
 
 description:'Application Cache is deprecated. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/appcache).',
+'[Learn more](https://web.dev/appcache-manifest).',
+
+
+
 
 displayValue:'Found "{AppCacheManifest}"'};
 
@@ -4985,13 +5006,16 @@
 
 
 static audit(artifacts){
-const usingAppcache=artifacts.AppCacheManifest!==null;
-const displayValue=usingAppcache?
-str_(UIStrings.displayValue,{AppCacheManifest:artifacts.AppCacheManifest}):'';
+
+if(artifacts.AppCacheManifest!==null){
+return{
+score:0,
+displayValue:str_(UIStrings.displayValue,{AppCacheManifest:artifacts.AppCacheManifest})};
+
+}
 
 return{
-score:usingAppcache?0:1,
-displayValue};
+score:1};
 
 }}
 
@@ -5000,7 +5024,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/dobetterweb/appcache-manifest.js");
-},{"../../lib/i18n/i18n.js":68,"../audit.js":3}],"../audits/dobetterweb/doctype":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../audit.js":3}],"../audits/dobetterweb/doctype":[function(require,module,exports){
 (function(__filename){
 
 
@@ -5019,8 +5043,8 @@
 failureTitle:'Page lacks the HTML doctype, thus triggering quirks-mode',
 
 description:'Specifying a doctype prevents the browser '+
-'from switching to quirks-mode. Read more on the '+
-'[MDN Web Docs page](https://developer.mozilla.org/en-US/docs/Glossary/Doctype)',
+'from switching to quirks-mode. '+
+'[Learn more](https://web.dev/doctype).',
 
 explanationNoDoctype:'Document must contain a doctype',
 
@@ -5098,7 +5122,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/dobetterweb/doctype.js");
-},{"../../lib/i18n/i18n.js":68,"../audit.js":3}],"../audits/dobetterweb/dom-size":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../audit.js":3}],"../audits/dobetterweb/dom-size":[function(require,module,exports){
 (function(__filename){
 
 
@@ -5133,7 +5157,7 @@
 `depth < ${MAX_DOM_TREE_DEPTH} elements and fewer than ${MAX_DOM_TREE_WIDTH} `+
 'children/parent element. A large DOM can increase memory usage, cause longer '+
 '[style calculations](https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations), '+
-'and produce costly [layout reflows](https://developers.google.com/speed/articles/reflow). [Learn more](https://developers.google.com/web/tools/lighthouse/audits/dom-size).',
+'and produce costly [layout reflows](https://developers.google.com/speed/articles/reflow). [Learn more](https://web.dev/dom-size).',
 
 columnStatistic:'Statistic',
 
@@ -5251,7 +5275,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/dobetterweb/dom-size.js");
-},{"../../lib/i18n/i18n.js":68,"../../report/html/renderer/util.js":88,"../audit.js":3}],"../audits/dobetterweb/external-anchors-use-rel-noopener":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../../report/html/renderer/util.js":87,"../audit.js":3}],"../audits/dobetterweb/external-anchors-use-rel-noopener":[function(require,module,exports){
 (function(__filename){
 
 
@@ -5272,7 +5296,10 @@
 
 description:'Add `rel="noopener"` or `rel="noreferrer"` to any external links to improve '+
 'performance and prevent security vulnerabilities. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/noopener).',
+'[Learn more](https://web.dev/external-anchors-use-rel-noopener).',
+
+
+
 
 warning:'Unable to determine the destination for anchor ({anchorHTML}). '+
 'If not used as a hyperlink, consider removing target=_blank.',
@@ -5354,7 +5381,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/dobetterweb/external-anchors-use-rel-noopener.js");
-},{"../../lib/i18n/i18n.js":68,"../../lib/url-shim.js":"url","../audit.js":3}],"../audits/dobetterweb/geolocation-on-start":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../../lib/url-shim.js":"url","../audit.js":3}],"../audits/dobetterweb/geolocation-on-start":[function(require,module,exports){
 (function(__filename){
 
 
@@ -5380,7 +5407,7 @@
 
 description:'Users are mistrustful of or confused by sites that request their '+
 'location without context. Consider tying the request to a user action instead. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/geolocation-on-load).'};
+'[Learn more](https://web.dev/geolocation-on-start).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -5430,7 +5457,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/dobetterweb/geolocation-on-start.js");
-},{"../../lib/i18n/i18n.js":68,"../violation-audit.js":7}],"../audits/dobetterweb/js-libraries":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../violation-audit.js":7}],"../audits/dobetterweb/js-libraries":[function(require,module,exports){
 (function(__filename){
 
 
@@ -5451,9 +5478,7 @@
 
 title:'Detected JavaScript libraries',
 
-description:'All front-end JavaScript libraries detected on the page.',
-
-columnName:'Name',
+description:'All front-end JavaScript libraries detected on the page. [Learn more](https://web.dev/js-libraries).',
 
 columnVersion:'Version'};
 
@@ -5488,7 +5513,7 @@
 
 
 const headings=[
-{key:'name',itemType:'text',text:str_(UIStrings.columnName)},
+{key:'name',itemType:'text',text:str_(i18n.UIStrings.columnName)},
 {key:'version',itemType:'text',text:str_(UIStrings.columnVersion)}];
 
 const details=Audit.makeTableDetails(headings,libDetails,{});
@@ -5504,7 +5529,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/dobetterweb/js-libraries.js");
-},{"../../lib/i18n/i18n.js":68,"../audit.js":3}],"../audits/dobetterweb/no-document-write":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../audit.js":3}],"../audits/dobetterweb/no-document-write":[function(require,module,exports){
 (function(__filename){
 
 
@@ -5516,6 +5541,24 @@
 
 
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 'use strict';
 
 const ViolationAudit=require('../violation-audit.js');
@@ -5529,7 +5572,7 @@
 
 description:'For users on slow connections, external scripts dynamically injected via '+
 '`document.write()` can delay page load by tens of seconds. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/document-write).'};
+'[Learn more](https://web.dev/no-document-write).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -5577,7 +5620,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/dobetterweb/no-document-write.js");
-},{"../../lib/i18n/i18n.js":68,"../violation-audit.js":7}],"../audits/dobetterweb/no-vulnerable-libraries":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../violation-audit.js":7}],"../audits/dobetterweb/no-vulnerable-libraries":[function(require,module,exports){
 (function(__filename){
 
 
@@ -5609,7 +5652,7 @@
 
 description:'Some third-party scripts may contain known security vulnerabilities '+
 'that are easily identified and exploited by attackers. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/vulnerabilities).',
+'[Learn more](https://web.dev/no-vulnerable-libraries).',
 
 displayValue:`{itemCount, plural,
     =1 {1 vulnerability detected}
@@ -5824,7 +5867,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js");
-},{"../../../third-party/snyk/snapshot.json":182,"../../lib/i18n/i18n.js":68,"../../lib/sentry.js":79,"../audit.js":3,"semver":166}],"../audits/dobetterweb/notification-on-start":[function(require,module,exports){
+},{"../../../third-party/snyk/snapshot.json":178,"../../lib/i18n/i18n.js":67,"../../lib/sentry.js":78,"../audit.js":3,"semver":162}],"../audits/dobetterweb/notification-on-start":[function(require,module,exports){
 (function(__filename){
 
 
@@ -5850,7 +5893,7 @@
 
 description:'Users are mistrustful of or confused by sites that request to send '+
 'notifications without context. Consider tying the request to user gestures '+
-'instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/notifications-on-load).'};
+'instead. [Learn more](https://web.dev/notification-on-start).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -5898,7 +5941,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/dobetterweb/notification-on-start.js");
-},{"../../lib/i18n/i18n.js":68,"../violation-audit.js":7}],"../audits/dobetterweb/password-inputs-can-be-pasted-into":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../violation-audit.js":7}],"../audits/dobetterweb/password-inputs-can-be-pasted-into":[function(require,module,exports){
 (function(__filename){
 
 
@@ -5917,7 +5960,7 @@
 failureTitle:'Prevents users to paste into password fields',
 
 description:'Preventing password pasting undermines good security policy. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/password-pasting).',
+'[Learn more](https://web.dev/password-inputs-can-be-pasted-into).',
 
 columnFailingElem:'Failing Elements'};
 
@@ -5972,7 +6015,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/dobetterweb/password-inputs-can-be-pasted-into.js");
-},{"../../lib/i18n/i18n.js":68,"../audit.js":3}],"../audits/dobetterweb/uses-http2":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../audit.js":3}],"../audits/dobetterweb/uses-http2":[function(require,module,exports){
 (function(__filename){
 
 
@@ -5999,7 +6042,7 @@
 failureTitle:'Does not use HTTP/2 for all of its resources',
 
 description:'HTTP/2 offers many benefits over HTTP/1.1, including binary headers, '+
-'multiplexing, and server push. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http2).',
+'multiplexing, and server push. [Learn more](https://web.dev/uses-http2).',
 
 displayValue:`{itemCount, plural,
     =1 {1 request not served via HTTP/2}
@@ -6087,7 +6130,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/dobetterweb/uses-http2.js");
-},{"../../computed/network-records.js":33,"../../lib/i18n/i18n.js":68,"../../lib/url-shim.js":"url","../audit.js":3}],"../audits/dobetterweb/uses-passive-event-listeners":[function(require,module,exports){
+},{"../../computed/network-records.js":33,"../../lib/i18n/i18n.js":67,"../../lib/url-shim.js":"url","../audit.js":3}],"../audits/dobetterweb/uses-passive-event-listeners":[function(require,module,exports){
 (function(__filename){
 
 
@@ -6113,7 +6156,7 @@
 
 description:'Consider marking your touch and wheel event listeners as `passive` '+
 'to improve your page\'s scroll performance. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/passive-event-listeners).'};
+'[Learn more](https://web.dev/uses-passive-event-listeners).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -6161,7 +6204,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js");
-},{"../../lib/i18n/i18n.js":68,"../violation-audit.js":7}],"../audits/errors-in-console":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../violation-audit.js":7}],"../audits/errors-in-console":[function(require,module,exports){
 (function(__filename){
 
 
@@ -6185,7 +6228,8 @@
 failureTitle:'Browser errors were logged to the console',
 
 description:'Errors logged to the console indicate unresolved problems. '+
-'They can come from network request failures and other browser concerns.',
+'They can come from network request failures and other browser concerns. '+
+'[Learn more](https://web.dev/errors-in-console)',
 
 columnDesc:'Description'};
 
@@ -6260,7 +6304,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/errors-in-console.js");
-},{"../lib/i18n/i18n.js":68,"./audit.js":3}],"../audits/final-screenshot":[function(require,module,exports){
+},{"../lib/i18n/i18n.js":67,"./audit.js":3}],"../audits/final-screenshot":[function(require,module,exports){
 
 
 
@@ -6317,7 +6361,7 @@
 
 module.exports=FinalScreenshot;
 
-},{"../computed/screenshots.js":36,"../computed/trace-of-tab.js":38,"../lib/lh-error.js":72,"./audit.js":3}],"../audits/font-display":[function(require,module,exports){
+},{"../computed/screenshots.js":36,"../computed/trace-of-tab.js":38,"../lib/lh-error.js":71,"./audit.js":3}],"../audits/font-display":[function(require,module,exports){
 (function(__filename){
 
 
@@ -6346,6 +6390,9 @@
 'webfonts are loading. '+
 '[Learn more](https://developers.google.com/web/updates/2016/02/font-display).',
 
+
+
+
 undeclaredFontURLWarning:'Lighthouse was unable to automatically check the font-display value '+
 'for the following URL: {fontURL}.'};
 
@@ -6482,7 +6529,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/font-display.js");
-},{"../computed/network-records.js":33,"../lib/i18n/i18n.js":68,"../lib/sentry.js":79,"../lib/url-shim.js":"url","./audit.js":3}],"../audits/image-aspect-ratio":[function(require,module,exports){
+},{"../computed/network-records.js":33,"../lib/i18n/i18n.js":67,"../lib/sentry.js":78,"../lib/url-shim.js":"url","./audit.js":3}],"../audits/image-aspect-ratio":[function(require,module,exports){
 (function(__filename){
 
 
@@ -6508,7 +6555,10 @@
 failureTitle:'Displays images with incorrect aspect ratio',
 
 description:'Image display dimensions should match natural aspect ratio. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/aspect-ratio).',
+'[Learn more](https://web.dev/image-aspect-ratio).',
+
+
+
 
 warningCompute:'Invalid image sizing information {url}',
 
@@ -6620,7 +6670,8 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/image-aspect-ratio.js");
-},{"../lib/i18n/i18n.js":68,"../lib/url-shim.js":"url","./audit.js":3}],"../audits/installable-manifest":[function(require,module,exports){
+},{"../lib/i18n/i18n.js":67,"../lib/url-shim.js":"url","./audit.js":3}],"../audits/installable-manifest":[function(require,module,exports){
+(function(__filename){
 
 
 
@@ -6630,6 +6681,20 @@
 
 const MultiCheckAudit=require('./multi-check-audit.js');
 const ManifestValues=require('../computed/manifest-values.js');
+const i18n=require('../lib/i18n/i18n.js');
+
+const UIStrings={
+
+title:'Web app manifest meets the installability requirements',
+
+failureTitle:'Web app manifest does not meet the installability requirements',
+
+description:'Browsers can proactively prompt users to add your app to their homescreen, '+
+'which can lead to higher engagement. '+
+'[Learn more](https://web.dev/installable-manifest).'};
+
+
+const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
 
 
 
@@ -6652,11 +6717,9 @@
 static get meta(){
 return{
 id:'installable-manifest',
-title:'Web app manifest meets the installability requirements',
-failureTitle:'Web app manifest does not meet the installability requirements',
-description:'Browsers can proactively prompt users to add your app to their homescreen, '+
-'which can lead to higher engagement. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/install-prompt).',
+title:str_(UIStrings.title),
+failureTitle:str_(UIStrings.failureTitle),
+description:str_(UIStrings.description),
 requiredArtifacts:['URL','WebAppManifest']};
 
 }
@@ -6715,8 +6778,10 @@
 
 
 module.exports=InstallableManifest;
+module.exports.UIStrings=UIStrings;
 
-},{"../computed/manifest-values.js":13,"./multi-check-audit.js":6}],"../audits/is-on-https":[function(require,module,exports){
+}).call(this,"/lighthouse-core/audits/installable-manifest.js");
+},{"../computed/manifest-values.js":13,"../lib/i18n/i18n.js":67,"./multi-check-audit.js":6}],"../audits/is-on-https":[function(require,module,exports){
 (function(__filename){
 
 
@@ -6740,7 +6805,7 @@
 'sensitive data. HTTPS prevents intruders from tampering with or passively listening '+
 'in on the communications between your app and your users, and is a prerequisite for '+
 'HTTP/2 and many new web platform APIs. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/https).',
+'[Learn more](https://web.dev/is-on-https).',
 
 displayValue:`{itemCount, plural,
     =1 {1 insecure request found}
@@ -6819,7 +6884,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/is-on-https.js");
-},{"../computed/network-records.js":33,"../lib/i18n/i18n.js":68,"../lib/url-shim.js":"url","./audit.js":3}],"../audits/load-fast-enough-for-pwa":[function(require,module,exports){
+},{"../computed/network-records.js":33,"../lib/i18n/i18n.js":67,"../lib/url-shim.js":"url","./audit.js":3}],"../audits/load-fast-enough-for-pwa":[function(require,module,exports){
 (function(__filename){
 
 
@@ -6850,12 +6915,16 @@
 
 failureTitle:'Page load is not fast enough on mobile networks',
 
-description:'A fast page load over a cellular network ensures a good mobile user experience. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/fast-3g).',
+description:'A fast page load over a cellular network ensures a good mobile user experience. [Learn more](https://web.dev/load-fast-enough-for-pwa).',
 
 displayValueText:'Interactive at {timeInMs, number, seconds}\xa0s',
 
 displayValueTextWithOverride:'Interactive on simulated mobile network at '+
-'{timeInMs, number, seconds}\xa0s'};
+'{timeInMs, number, seconds}\xa0s',
+
+explanationLoadSlow:'Your page loads too slowly and is not interactive within 10 seconds. '+
+'Look at the opportunities and diagnostics in the "Performance" section to learn how to '+
+'improve.'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -6909,9 +6978,7 @@
 let explanation;
 if(!score){
 displayValue=str_(displayValueTemplate,{timeInMs:tti.timing});
-explanation='Your page loads too slowly and is not interactive within 10 seconds. '+
-'Look at the opportunities and diagnostics in the "Performance" section to learn how to '+
-'improve.';
+explanation=str_(UIStrings.explanationLoadSlow);
 }
 
 return{
@@ -6927,7 +6994,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/load-fast-enough-for-pwa.js");
-},{"../computed/metrics/interactive.js":18,"../config/constants.js":45,"../lib/i18n/i18n.js":68,"./audit.js":3,"lodash.isequal":129}],"../audits/main-thread-tasks":[function(require,module,exports){
+},{"../computed/metrics/interactive.js":18,"../config/constants.js":45,"../lib/i18n/i18n.js":67,"./audit.js":3,"lodash.isequal":125}],"../audits/main-thread-tasks":[function(require,module,exports){
 
 
 
@@ -7126,7 +7193,8 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/mainthread-work-breakdown.js");
-},{"../computed/main-thread-tasks.js":12,"../lib/i18n/i18n.js":68,"../lib/tracehouse/task-groups.js":85,"./audit.js":3}],"../audits/manual/pwa-cross-browser":[function(require,module,exports){
+},{"../computed/main-thread-tasks.js":12,"../lib/i18n/i18n.js":67,"../lib/tracehouse/task-groups.js":84,"./audit.js":3}],"../audits/manual/pwa-cross-browser":[function(require,module,exports){
+(function(__filename){
 
 
 
@@ -7136,6 +7204,17 @@
 'use strict';
 
 const ManualAudit=require('./manual-audit.js');
+const i18n=require('../../lib/i18n/i18n.js');
+
+const UIStrings={
+
+title:'Site works cross-browser',
+
+description:'To reach the most number of users, sites should work across '+
+'every major browser. [Learn more](https://web.dev/pwa-cross-browser).'};
+
+
+const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
 
 
 
@@ -7148,16 +7227,19 @@
 static get meta(){
 return Object.assign({
 id:'pwa-cross-browser',
-description:'To reach the most number of users, sites should work across '+
-'every major browser. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#site-works-cross-browser).',
-title:'Site works cross-browser'},
+title:str_(UIStrings.title),
+description:str_(UIStrings.description)},
 super.partialMeta);
 }}
 
 
 module.exports=PWACrossBrowser;
+module.exports.UIStrings=UIStrings;
 
-},{"./manual-audit.js":5}],"../audits/manual/pwa-each-page-has-url":[function(require,module,exports){
+
+}).call(this,"/lighthouse-core/audits/manual/pwa-cross-browser.js");
+},{"../../lib/i18n/i18n.js":67,"./manual-audit.js":5}],"../audits/manual/pwa-each-page-has-url":[function(require,module,exports){
+(function(__filename){
 
 
 
@@ -7166,6 +7248,17 @@
 'use strict';
 
 const ManualAudit=require('./manual-audit.js');
+const i18n=require('../../lib/i18n/i18n.js');
+
+const UIStrings={
+
+title:'Each page has a URL',
+
+description:'Ensure individual pages are deep linkable via URL and that URLs are '+
+'unique for the purpose of shareability on social media. [Learn more](https://web.dev/pwa-each-page-has-url).'};
+
+
+const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
 
 
 
@@ -7178,16 +7271,18 @@
 static get meta(){
 return Object.assign({
 id:'pwa-each-page-has-url',
-description:'Ensure individual pages are deep linkable via the URLs and that URLs are '+
-'unique for the purpose of shareability on social media. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#each-page-has-a-url).',
-title:'Each page has a URL'},
+title:str_(UIStrings.title),
+description:str_(UIStrings.description)},
 super.partialMeta);
 }}
 
 
 module.exports=PWAEachPageHasURL;
+module.exports.UIStrings=UIStrings;
 
-},{"./manual-audit.js":5}],"../audits/manual/pwa-page-transitions":[function(require,module,exports){
+}).call(this,"/lighthouse-core/audits/manual/pwa-each-page-has-url.js");
+},{"../../lib/i18n/i18n.js":67,"./manual-audit.js":5}],"../audits/manual/pwa-page-transitions":[function(require,module,exports){
+(function(__filename){
 
 
 
@@ -7196,6 +7291,17 @@
 'use strict';
 
 const ManualAudit=require('./manual-audit.js');
+const i18n=require('../../lib/i18n/i18n.js');
+
+const UIStrings={
+
+title:'Page transitions don\'t feel like they block on the network',
+
+description:'Transitions should feel snappy as you tap around, even on a slow network. '+
+'This experience is key to a user\'s perception of performance. [Learn more](https://web.dev/pwa-page-transitions).'};
+
+
+const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
 
 
 
@@ -7208,16 +7314,17 @@
 static get meta(){
 return Object.assign({
 id:'pwa-page-transitions',
-description:'Transitions should feel snappy as you tap around, even on a slow network, a '+
-'key to perceived performance. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#page-transitions-dont-feel-like-they-block-on-the-network).',
-title:'Page transitions don\'t feel like they block on the network'},
+title:str_(UIStrings.title),
+description:str_(UIStrings.description)},
 super.partialMeta);
 }}
 
 
 module.exports=PWAPageTransitions;
+module.exports.UIStrings=UIStrings;
 
-},{"./manual-audit.js":5}],"../audits/metrics/estimated-input-latency":[function(require,module,exports){
+}).call(this,"/lighthouse-core/audits/manual/pwa-page-transitions.js");
+},{"../../lib/i18n/i18n.js":67,"./manual-audit.js":5}],"../audits/metrics/estimated-input-latency":[function(require,module,exports){
 (function(__filename){
 
 
@@ -7297,7 +7404,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/metrics/estimated-input-latency.js");
-},{"../../computed/metrics/estimated-input-latency.js":14,"../../lib/i18n/i18n.js":68,"../audit.js":3}],"../audits/metrics/first-contentful-paint-3g":[function(require,module,exports){
+},{"../../computed/metrics/estimated-input-latency.js":14,"../../lib/i18n/i18n.js":67,"../audit.js":3}],"../audits/metrics/first-contentful-paint-3g":[function(require,module,exports){
 
 
 
@@ -7440,7 +7547,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/metrics/first-contentful-paint.js");
-},{"../../computed/metrics/first-contentful-paint.js":15,"../../lib/i18n/i18n.js":68,"../audit.js":3}],"../audits/metrics/first-cpu-idle":[function(require,module,exports){
+},{"../../computed/metrics/first-contentful-paint.js":15,"../../lib/i18n/i18n.js":67,"../audit.js":3}],"../audits/metrics/first-cpu-idle":[function(require,module,exports){
 (function(__filename){
 
 
@@ -7521,7 +7628,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/metrics/first-cpu-idle.js");
-},{"../../computed/metrics/first-cpu-idle.js":16,"../../lib/i18n/i18n.js":68,"../audit.js":3}],"../audits/metrics/first-meaningful-paint":[function(require,module,exports){
+},{"../../computed/metrics/first-cpu-idle.js":16,"../../lib/i18n/i18n.js":67,"../audit.js":3}],"../audits/metrics/first-meaningful-paint":[function(require,module,exports){
 (function(__filename){
 
 
@@ -7601,7 +7708,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/metrics/first-meaningful-paint.js");
-},{"../../computed/metrics/first-meaningful-paint.js":17,"../../lib/i18n/i18n.js":68,"../audit.js":3}],"../audits/metrics/interactive":[function(require,module,exports){
+},{"../../computed/metrics/first-meaningful-paint.js":17,"../../lib/i18n/i18n.js":67,"../audit.js":3}],"../audits/metrics/interactive":[function(require,module,exports){
 (function(__filename){
 
 
@@ -7696,7 +7803,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/metrics/interactive.js");
-},{"../../computed/metrics/interactive.js":18,"../../lib/i18n/i18n.js":68,"../audit.js":3}],"../audits/metrics/max-potential-fid":[function(require,module,exports){
+},{"../../computed/metrics/interactive.js":18,"../../lib/i18n/i18n.js":67,"../audit.js":3}],"../audits/metrics/max-potential-fid":[function(require,module,exports){
 (function(__filename){
 
 
@@ -7776,7 +7883,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/metrics/max-potential-fid.js");
-},{"../../computed/metrics/max-potential-fid.js":28,"../../lib/i18n/i18n.js":68,"../audit.js":3}],"../audits/metrics/speed-index":[function(require,module,exports){
+},{"../../computed/metrics/max-potential-fid.js":28,"../../lib/i18n/i18n.js":67,"../audit.js":3}],"../audits/metrics/speed-index":[function(require,module,exports){
 (function(__filename){
 
 
@@ -7855,7 +7962,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/metrics/speed-index.js");
-},{"../../computed/metrics/speed-index.js":30,"../../lib/i18n/i18n.js":68,"../audit.js":3}],"../audits/metrics/total-blocking-time":[function(require,module,exports){
+},{"../../computed/metrics/speed-index.js":30,"../../lib/i18n/i18n.js":67,"../audit.js":3}],"../audits/metrics/total-blocking-time":[function(require,module,exports){
 (function(__filename){
 
 
@@ -7942,7 +8049,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/metrics/total-blocking-time.js");
-},{"../../computed/metrics/total-blocking-time.js":31,"../../lib/i18n/i18n.js":68,"../audit.js":3}],"../audits/metrics":[function(require,module,exports){
+},{"../../computed/metrics/total-blocking-time.js":31,"../../lib/i18n/i18n.js":67,"../audit.js":3}],"../audits/metrics":[function(require,module,exports){
 
 
 
@@ -8262,7 +8369,7 @@
 
 module.exports=MixedContent;
 
-},{"../computed/network-records.js":33,"../lib/url-shim.js":"url","../report/html/renderer/util.js":88,"./audit.js":3}],"../audits/network-requests":[function(require,module,exports){
+},{"../computed/network-records.js":33,"../lib/url-shim.js":"url","../report/html/renderer/util.js":87,"./audit.js":3}],"../audits/network-requests":[function(require,module,exports){
 
 
 
@@ -8448,7 +8555,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/network-rtt.js");
-},{"../computed/network-analysis.js":32,"../lib/i18n/i18n.js":68,"./audit.js":3}],"../audits/network-server-latency":[function(require,module,exports){
+},{"../computed/network-analysis.js":32,"../lib/i18n/i18n.js":67,"./audit.js":3}],"../audits/network-server-latency":[function(require,module,exports){
 (function(__filename){
 
 
@@ -8531,7 +8638,8 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/network-server-latency.js");
-},{"../computed/network-analysis.js":32,"../lib/i18n/i18n.js":68,"./audit.js":3}],"../audits/offline-start-url":[function(require,module,exports){
+},{"../computed/network-analysis.js":32,"../lib/i18n/i18n.js":67,"./audit.js":3}],"../audits/offline-start-url":[function(require,module,exports){
+(function(__filename){
 
 
 
@@ -8540,6 +8648,25 @@
 'use strict';
 
 const Audit=require('./audit.js');
+const i18n=require('../lib/i18n/i18n.js');
+
+const UIStrings={
+
+title:'`start_url` responds with a 200 when offline',
+
+failureTitle:'`start_url` does not respond with a 200 when offline',
+
+description:'A service worker enables your web app to be reliable in unpredictable '+
+'network conditions. [Learn more](https://web.dev/offline-start-url).',
+
+
+
+
+warningCantStart:'Lighthouse couldn\'t read the `start_url` from the manifest. As a result, '+
+'the `start_url` was assumed to be the document\'s URL. Error message: \'{manifestWarning}\'.'};
+
+
+const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
 
 class OfflineStartUrl extends Audit{
 
@@ -8548,9 +8675,9 @@
 static get meta(){
 return{
 id:'offline-start-url',
-title:'start_url responds with a 200 when offline',
-failureTitle:'start_url does not respond with a 200 when offline',
-description:'A service worker enables your web app to be reliable in unpredictable network conditions. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline).',
+title:str_(UIStrings.title),
+failureTitle:str_(UIStrings.failureTitle),
+description:str_(UIStrings.description),
 requiredArtifacts:['WebAppManifest','StartUrl']};
 
 }
@@ -8566,8 +8693,7 @@
 const manifest=artifacts.WebAppManifest;
 if(manifest&&manifest.value&&manifest.value.start_url.warning){
 const manifestWarning=manifest.value.start_url.warning;
-warnings.push('We couldn\'t read the start_url from the manifest. As a result, the '+
-`start_url was assumed to be the document's URL. Error message: '${manifestWarning}'.`);
+warnings.push(str_(UIStrings.warningCantStart,{manifestWarning}));
 }
 
 const hasOfflineStartUrl=artifacts.StartUrl.statusCode===200;
@@ -8581,8 +8707,10 @@
 
 
 module.exports=OfflineStartUrl;
+module.exports.UIStrings=UIStrings;
 
-},{"./audit.js":3}],"../audits/performance-budget":[function(require,module,exports){
+}).call(this,"/lighthouse-core/audits/offline-start-url.js");
+},{"../lib/i18n/i18n.js":67,"./audit.js":3}],"../audits/performance-budget":[function(require,module,exports){
 (function(__filename){
 
 
@@ -8593,6 +8721,8 @@
 
 const Audit=require('./audit.js');
 const ResourceSummary=require('../computed/resource-summary.js');
+const MainResource=require('../computed/main-resource.js');
+const Budget=require('../config/budget.js');
 const i18n=require('../lib/i18n/i18n.js');
 
 const UIStrings={
@@ -8707,7 +8837,14 @@
 static async audit(artifacts,context){
 const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
 const summary=await ResourceSummary.request({devtoolsLog,URL:artifacts.URL},context);
-const budget=context.settings.budgets?context.settings.budgets[0]:undefined;
+const mainResource=await MainResource.request({URL:artifacts.URL,devtoolsLog},context);
+
+
+const budgets=Array.from(context.settings.budgets||[]);
+
+const budget=budgets?budgets.reverse().find(b=>{
+return Budget.urlMatchesPattern(mainResource.url,b.path);
+}):undefined;
 
 if(!budget){
 return{
@@ -8736,7 +8873,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/performance-budget.js");
-},{"../computed/resource-summary.js":35,"../lib/i18n/i18n.js":68,"./audit.js":3}],"../audits/predictive-perf":[function(require,module,exports){
+},{"../computed/main-resource.js":11,"../computed/resource-summary.js":35,"../config/budget.js":41,"../lib/i18n/i18n.js":67,"./audit.js":3}],"../audits/predictive-perf":[function(require,module,exports){
 
 
 
@@ -8840,7 +8977,8 @@
 
 module.exports=PredictivePerf;
 
-},{"../computed/metrics/lantern-estimated-input-latency.js":19,"../computed/metrics/lantern-first-contentful-paint.js":20,"../computed/metrics/lantern-first-cpu-idle.js":21,"../computed/metrics/lantern-first-meaningful-paint.js":22,"../computed/metrics/lantern-interactive.js":23,"../computed/metrics/lantern-speed-index.js":26,"../report/html/renderer/util.js":88,"./audit.js":3}],"../audits/redirects-http":[function(require,module,exports){
+},{"../computed/metrics/lantern-estimated-input-latency.js":19,"../computed/metrics/lantern-first-contentful-paint.js":20,"../computed/metrics/lantern-first-cpu-idle.js":21,"../computed/metrics/lantern-first-meaningful-paint.js":22,"../computed/metrics/lantern-interactive.js":23,"../computed/metrics/lantern-speed-index.js":26,"../report/html/renderer/util.js":87,"./audit.js":3}],"../audits/redirects-http":[function(require,module,exports){
+(function(__filename){
 
 
 
@@ -8849,6 +8987,19 @@
 'use strict';
 
 const Audit=require('./audit.js');
+const i18n=require('../lib/i18n/i18n.js');
+
+const UIStrings={
+
+title:'Redirects HTTP traffic to HTTPS',
+
+failureTitle:'Does not redirect HTTP traffic to HTTPS',
+
+description:'If you\'ve already set up HTTPS, make sure that you redirect all HTTP '+
+'traffic to HTTPS in order to enable secure web features for all your users. [Learn more](https://web.dev/redirects-http).'};
+
+
+const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
 
 class RedirectsHTTP extends Audit{
 
@@ -8857,10 +9008,9 @@
 static get meta(){
 return{
 id:'redirects-http',
-title:'Redirects HTTP traffic to HTTPS',
-failureTitle:'Does not redirect HTTP traffic to HTTPS',
-description:'If you\'ve already set up HTTPS, make sure that you redirect all HTTP '+
-'traffic to HTTPS. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https).',
+title:str_(UIStrings.title),
+failureTitle:str_(UIStrings.failureTitle),
+description:str_(UIStrings.description),
 requiredArtifacts:['HTTPRedirect']};
 
 }
@@ -8877,8 +9027,10 @@
 
 
 module.exports=RedirectsHTTP;
+module.exports.UIStrings=UIStrings;
 
-},{"./audit.js":3}],"../audits/redirects":[function(require,module,exports){
+}).call(this,"/lighthouse-core/audits/redirects-http.js");
+},{"../lib/i18n/i18n.js":67,"./audit.js":3}],"../audits/redirects":[function(require,module,exports){
 (function(__filename){
 
 
@@ -9008,7 +9160,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/redirects.js");
-},{"../computed/main-resource.js":11,"../computed/metrics/lantern-interactive.js":23,"../computed/network-records.js":33,"../computed/trace-of-tab.js":38,"../lib/i18n/i18n.js":68,"./audit.js":3,"./byte-efficiency/byte-efficiency-audit.js":4}],"../audits/resource-summary":[function(require,module,exports){
+},{"../computed/main-resource.js":11,"../computed/metrics/lantern-interactive.js":23,"../computed/network-records.js":33,"../computed/trace-of-tab.js":38,"../lib/i18n/i18n.js":67,"./audit.js":3,"./byte-efficiency/byte-efficiency-audit.js":4}],"../audits/resource-summary":[function(require,module,exports){
 (function(__filename){
 
 
@@ -9028,8 +9180,9 @@
 description:'To set budgets for the quantity and size of page resources,'+
 ' add a budget.json file. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).',
 
-displayValue:`{requestCount, plural, =1 {1 request} other {# requests}}`+
-` • { byteCount, number, bytes } KB`};
+displayValue:`{requestCount, plural, `+
+`=1 {1 request • {byteCount, number, bytes} KB} `+
+`other {# requests • {byteCount, number, bytes} KB}}`};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -9115,7 +9268,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/resource-summary.js");
-},{"../computed/resource-summary.js":35,"../lib/i18n/i18n.js":68,"./audit.js":3}],"../audits/screenshot-thumbnails":[function(require,module,exports){
+},{"../computed/resource-summary.js":35,"../lib/i18n/i18n.js":67,"./audit.js":3}],"../audits/screenshot-thumbnails":[function(require,module,exports){
 
 
 
@@ -9265,7 +9418,7 @@
 
 module.exports=ScreenshotThumbnails;
 
-},{"../computed/metrics/interactive.js":18,"../computed/speedline.js":37,"../lib/lh-error.js":72,"./audit.js":3,"jpeg-js":125}],"../audits/seo/canonical":[function(require,module,exports){
+},{"../computed/metrics/interactive.js":18,"../computed/speedline.js":37,"../lib/lh-error.js":71,"./audit.js":3,"jpeg-js":121}],"../audits/seo/canonical":[function(require,module,exports){
 (function(__filename){
 
 
@@ -9286,16 +9439,31 @@
 failureTitle:'Document does not have a valid `rel=canonical`',
 
 description:'Canonical links suggest which URL to show in search results. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/canonical).',
+'[Learn more](https://web.dev/canonical).',
+
+
+
 
 explanationConflict:'Multiple conflicting URLs ({urlList})',
 
+
+
+
 explanationInvalid:'Invalid URL ({url})',
 
+
+
+
 explanationRelative:'Relative URL ({url})',
 
+
+
+
 explanationPointsElsewhere:'Points to another `hreflang` location ({url})',
 
+
+
+
 explanationDifferentDomain:'Points to a different domain ({url})',
 
 explanationRoot:'Points to the domain\'s root URL (the homepage), '+
@@ -9432,7 +9600,7 @@
 if(!URL.rootDomainsMatch(canonicalURL,baseURL)){
 return{
 score:0,
-explanation:str_(UIStrings.explanationDifferentDomain,{url:canonicalURL})};
+explanation:str_(UIStrings.explanationDifferentDomain,{url:canonicalURL.href})};
 
 }
 
@@ -9485,7 +9653,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/seo/canonical.js");
-},{"../../computed/main-resource.js":11,"../../lib/i18n/i18n.js":68,"../../lib/url-shim.js":"url","../audit.js":3}],"../audits/seo/font-size":[function(require,module,exports){
+},{"../../computed/main-resource.js":11,"../../lib/i18n/i18n.js":67,"../../lib/url-shim.js":"url","../audit.js":3}],"../audits/seo/font-size":[function(require,module,exports){
 (function(__filename){
 
 
@@ -9508,7 +9676,7 @@
 
 failureTitle:'Document doesn\'t use legible font sizes',
 
-description:'Font sizes less than 12px are too small to be legible and require mobile visitors to “pinch to zoom” in order to read. Strive to have >60% of page text ≥12px. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/font-sizes).',
+description:'Font sizes less than 12px are too small to be legible and require mobile visitors to “pinch to zoom” in order to read. Strive to have >60% of page text ≥12px. [Learn more](https://web.dev/font-size).',
 
 displayValue:'{decimalProportion, number, extendedPercent} legible text',
 
@@ -9831,7 +9999,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/seo/font-size.js");
-},{"../../computed/viewport-meta.js":40,"../../lib/i18n/i18n.js":68,"../../lib/url-shim.js":"url","../audit.js":3}],"../audits/seo/hreflang":[function(require,module,exports){
+},{"../../computed/viewport-meta.js":40,"../../lib/i18n/i18n.js":67,"../../lib/url-shim.js":"url","../audit.js":3}],"../audits/seo/hreflang":[function(require,module,exports){
 (function(global,__filename){
 
 
@@ -9853,7 +10021,7 @@
 
 description:'hreflang links tell search engines what version of a page they should '+
 'list in search results for a given language or region. [Learn more]'+
-'(https://developers.google.com/web/tools/lighthouse/audits/hreflang).'};
+'(https://web.dev/hreflang).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -9951,7 +10119,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{},"/lighthouse-core/audits/seo/hreflang.js");
-},{"../../lib/i18n/i18n.js":68,"../audit.js":3,"axe-core/lib/core/utils/valid-langs.js":96}],"../audits/seo/http-status-code":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../audit.js":3,"axe-core/lib/core/utils/valid-langs.js":95}],"../audits/seo/http-status-code":[function(require,module,exports){
 (function(__filename){
 
 
@@ -9973,8 +10141,7 @@
 failureTitle:'Page has unsuccessful HTTP status code',
 
 description:'Pages with unsuccessful HTTP status codes may not be indexed properly. '+
-'[Learn more]'+
-'(https://developers.google.com/web/tools/lighthouse/audits/successful-http-code).'};
+'[Learn more](https://web.dev/http-status-code).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -10025,7 +10192,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/seo/http-status-code.js");
-},{"../../computed/main-resource.js":11,"../../lib/i18n/i18n.js":68,"../audit.js":3}],"../audits/seo/is-crawlable":[function(require,module,exports){
+},{"../../computed/main-resource.js":11,"../../lib/i18n/i18n.js":67,"../audit.js":3}],"../audits/seo/is-crawlable":[function(require,module,exports){
 (function(__filename){
 
 
@@ -10053,8 +10220,7 @@
 failureTitle:'Page is blocked from indexing',
 
 description:'Search engines are unable to include your pages in search results '+
-'if they don\'t have permission to crawl them. [Learn '+
-'more](https://developers.google.com/web/tools/lighthouse/audits/indexing).'};
+'if they don\'t have permission to crawl them. [Learn more](https://web.dev/is-crawable).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -10179,7 +10345,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/seo/is-crawlable.js");
-},{"../../computed/main-resource.js":11,"../../lib/i18n/i18n.js":68,"../../lib/url-shim.js":"url","../audit.js":3,"robots-parser":164}],"../audits/seo/link-text":[function(require,module,exports){
+},{"../../computed/main-resource.js":11,"../../lib/i18n/i18n.js":67,"../../lib/url-shim.js":"url","../audit.js":3,"robots-parser":160}],"../audits/seo/link-text":[function(require,module,exports){
 (function(__filename){
 
 
@@ -10226,7 +10392,16 @@
 'este',
 'enlace',
 'este enlace',
-'empezar']);
+'empezar',
+
+'clique aqui',
+'inicio',
+'início',
+'ir',
+'mais informação',
+'mais informações',
+'mais',
+'veja mais']);
 
 const i18n=require('../../lib/i18n/i18n.js');
 
@@ -10237,7 +10412,7 @@
 failureTitle:'Links do not have descriptive text',
 
 description:'Descriptive link text helps search engines understand your content. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/descriptive-link-text).',
+'[Learn more](https://web.dev/link-text).',
 
 displayValue:`{itemCount, plural,
     =1 {1 link found}
@@ -10312,7 +10487,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/seo/link-text.js");
-},{"../../lib/i18n/i18n.js":68,"../../lib/url-shim.js":"url","../audit.js":3}],"../audits/seo/manual/structured-data":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../../lib/url-shim.js":"url","../audit.js":3}],"../audits/seo/manual/structured-data":[function(require,module,exports){
 (function(__filename){
 
 
@@ -10326,7 +10501,7 @@
 
 const UIStrings={
 
-description:'Run the [Structured Data Testing Tool](https://search.google.com/structured-data/testing-tool/) and the [Structured Data Linter](http://linter.structured-data.org/) to validate structured data. [Learn more](https://developers.google.com/search/docs/guides/mark-up-content).',
+description:'Run the [Structured Data Testing Tool](https://search.google.com/structured-data/testing-tool/) and the [Structured Data Linter](http://linter.structured-data.org/) to validate structured data. [Learn more](https://web.dev/structured-data).',
 
 title:'Structured data is valid'};
 
@@ -10354,7 +10529,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/seo/manual/structured-data.js");
-},{"../../../lib/i18n/i18n.js":68,"../../manual/manual-audit.js":5}],"../audits/seo/meta-description":[function(require,module,exports){
+},{"../../../lib/i18n/i18n.js":67,"../../manual/manual-audit.js":5}],"../audits/seo/meta-description":[function(require,module,exports){
 (function(__filename){
 
 
@@ -10374,7 +10549,7 @@
 
 description:'Meta descriptions may be included in search results to concisely summarize '+
 'page content. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/description).',
+'[Learn more](https://web.dev/meta-description).',
 
 explanation:'Description text is empty.'};
 
@@ -10425,7 +10600,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/seo/meta-description.js");
-},{"../../lib/i18n/i18n.js":68,"../audit.js":3}],"../audits/seo/plugins":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../audit.js":3}],"../audits/seo/plugins":[function(require,module,exports){
 (function(__filename){
 
 
@@ -10470,7 +10645,7 @@
 
 description:'Search engines can\'t index plugin content, and '+
 'many devices restrict plugins or don\'t support them. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/plugins).'};
+'[Learn more](https://web.dev/plugins).'};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
@@ -10599,7 +10774,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/seo/plugins.js");
-},{"../../lib/i18n/i18n.js":68,"../../lib/url-shim.js":"url","../audit.js":3}],"../audits/seo/robots-txt":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../../lib/url-shim.js":"url","../audit.js":3}],"../audits/seo/robots-txt":[function(require,module,exports){
 (function(__filename){
 
 
@@ -10643,9 +10818,12 @@
 failureTitle:'robots.txt is not valid',
 
 description:'If your robots.txt file is malformed, crawlers may not be able to understand '+
-'how you want your website to be crawled or indexed.',
+'how you want your website to be crawled or indexed. [Learn more](https://web.dev/robots-txt).',
 
-displayValueHttpBadCode:'request for robots.txt returned HTTP status: {statusCode}',
+
+
+
+displayValueHttpBadCode:'Request for robots.txt returned HTTP status: {statusCode}',
 
 displayValueValidationError:`{itemCount, plural,
     =1 {1 error found}
@@ -10856,7 +11034,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/seo/robots-txt.js");
-},{"../../lib/i18n/i18n.js":68,"../../lib/url-shim.js":"url","../audit.js":3}],"../audits/seo/tap-targets":[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../../lib/url-shim.js":"url","../audit.js":3}],"../audits/seo/tap-targets":[function(require,module,exports){
 (function(__filename){
 
 
@@ -10888,12 +11066,10 @@
 
 failureTitle:'Tap targets are not sized appropriately',
 
-description:'Interactive elements like buttons and links should be large enough (48x48px), and have enough space around them, to be easy enough to tap without overlapping onto other elements. [Learn more](https://developers.google.com/web/fundamentals/accessibility/accessible-styles#multi-device_responsive_design).',
+description:'Interactive elements like buttons and links should be large enough (48x48px), and have enough space around them, to be easy enough to tap without overlapping onto other elements. [Learn more](https://web.dev/tap-targets).',
 
 tapTargetHeader:'Tap Target',
 
-sizeHeader:'Size',
-
 overlappingTargetHeader:'Overlapping Target',
 
 
@@ -11163,7 +11339,7 @@
 
 const headings=[
 {key:'tapTarget',itemType:'node',text:str_(UIStrings.tapTargetHeader)},
-{key:'size',itemType:'text',text:str_(UIStrings.sizeHeader)},
+{key:'size',itemType:'text',text:str_(i18n.UIStrings.columnSize)},
 {key:'overlappingTarget',itemType:'node',text:str_(UIStrings.overlappingTargetHeader)}];
 
 
@@ -11228,7 +11404,8 @@
 
 
 }).call(this,"/lighthouse-core/audits/seo/tap-targets.js");
-},{"../../computed/viewport-meta.js":40,"../../lib/i18n/i18n.js":68,"../../lib/rect-helpers.js":78,"../../lib/tappable-rects.js":83,"../audit.js":3}],"../audits/service-worker":[function(require,module,exports){
+},{"../../computed/viewport-meta.js":40,"../../lib/i18n/i18n.js":67,"../../lib/rect-helpers.js":77,"../../lib/tappable-rects.js":82,"../audit.js":3}],"../audits/service-worker":[function(require,module,exports){
+(function(__filename){
 
 
 
@@ -11238,6 +11415,39 @@
 
 const URL=require('../lib/url-shim.js');
 const Audit=require('./audit.js');
+const i18n=require('../lib/i18n/i18n.js');
+
+const UIStrings={
+
+title:'Registers a service worker that controls page and `start_url`',
+
+failureTitle:'Does not register a service worker that controls page and `start_url`',
+
+description:'The service worker is the technology that enables your app to use many '+
+'Progressive Web App features, such as offline, add to homescreen, and push '+
+'notifications. [Learn more](https://web.dev/service-worker).',
+
+
+
+
+explanationOutOfScope:'This origin has one or more service workers, however the page '+
+'({pageUrl}) is not in scope.',
+
+explanationNoManifest:'This page is controlled by a service worker, however '+
+'no `start_url` was found because no manifest was fetched.',
+
+explanationBadManifest:'This page is controlled by a service worker, however '+
+'no `start_url` was found because manifest failed to parse as valid JSON',
+
+
+
+
+
+explanationBadStartUrl:'This page is controlled by a service worker, however '+
+'the `start_url` ({startUrl}) is not in the service worker\'s scope ({scopeUrl})'};
+
+
+const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
 
 class ServiceWorker extends Audit{
 
@@ -11246,11 +11456,9 @@
 static get meta(){
 return{
 id:'service-worker',
-title:'Registers a service worker that controls page and start_url',
-failureTitle:'Does not register a service worker that controls page and start_url',
-description:'The service worker is the technology that enables your app to use many '+
-'Progressive Web App features, such as offline, add to homescreen, and push '+
-'notifications. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/registered-service-worker).',
+title:str_(UIStrings.title),
+failureTitle:str_(UIStrings.failureTitle),
+description:str_(UIStrings.description),
 requiredArtifacts:['URL','ServiceWorker','WebAppManifest']};
 
 }
@@ -11301,15 +11509,15 @@
 
 static checkStartUrl(manifest,scopeUrl){
 if(!manifest){
-return'no start_url was found because no manifest was fetched';
+return str_(UIStrings.explanationNoManifest);
 }
 if(!manifest.value){
-return'no start_url was found because manifest failed to parse as valid JSON';
+return str_(UIStrings.explanationBadManifest);
 }
 
 const startUrl=manifest.value.start_url.value;
 if(!startUrl.startsWith(scopeUrl)){
-return`the start_url ("${startUrl}") is not in the service worker's scope ("${scopeUrl}")`;
+return str_(UIStrings.explanationBadStartUrl,{startUrl,scopeUrl});
 }
 }
 
@@ -11334,7 +11542,7 @@
 if(!controllingScopeUrl){
 return{
 score:0,
-explanation:`This origin has one or more service workers, however the page ("${pageUrl.href}") is not in scope.`};
+explanation:str_(UIStrings.explanationOutOfScope,{pageUrl:pageUrl.href})};
 
 }
 
@@ -11343,7 +11551,7 @@
 if(startUrlFailure){
 return{
 score:0,
-explanation:`This page is controlled by a service worker, however ${startUrlFailure}.`};
+explanation:startUrlFailure};
 
 }
 
@@ -11355,8 +11563,11 @@
 
 
 module.exports=ServiceWorker;
+module.exports.UIStrings=UIStrings;
 
-},{"../lib/url-shim.js":"url","./audit.js":3}],"../audits/splash-screen":[function(require,module,exports){
+}).call(this,"/lighthouse-core/audits/service-worker.js");
+},{"../lib/i18n/i18n.js":67,"../lib/url-shim.js":"url","./audit.js":3}],"../audits/splash-screen":[function(require,module,exports){
+(function(__filename){
 
 
 
@@ -11366,6 +11577,20 @@
 
 const MultiCheckAudit=require('./multi-check-audit.js');
 const ManifestValues=require('../computed/manifest-values.js');
+const i18n=require('../lib/i18n/i18n.js');
+
+const UIStrings={
+
+title:'Configured for a custom splash screen',
+
+failureTitle:'Is not configured for a custom splash screen',
+
+description:'A themed splash screen ensures a high-quality experience when '+
+'users launch your app from their homescreens. [Learn '+
+'more](https://web.dev/splash-screen).'};
+
+
+const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
 
 
 
@@ -11387,11 +11612,9 @@
 static get meta(){
 return{
 id:'splash-screen',
-title:'Configured for a custom splash screen',
-failureTitle:'Is not configured for a custom splash screen',
-description:'A themed splash screen ensures a high-quality experience when '+
-'users launch your app from their homescreens. [Learn '+
-'more](https://developers.google.com/web/tools/lighthouse/audits/custom-splash-screen).',
+title:str_(UIStrings.title),
+failureTitle:str_(UIStrings.failureTitle),
+description:str_(UIStrings.description),
 requiredArtifacts:['WebAppManifest']};
 
 }
@@ -11442,8 +11665,11 @@
 
 
 module.exports=SplashScreen;
+module.exports.UIStrings=UIStrings;
 
-},{"../computed/manifest-values.js":13,"./multi-check-audit.js":6}],"../audits/themed-omnibox":[function(require,module,exports){
+}).call(this,"/lighthouse-core/audits/splash-screen.js");
+},{"../computed/manifest-values.js":13,"../lib/i18n/i18n.js":67,"./multi-check-audit.js":6}],"../audits/themed-omnibox":[function(require,module,exports){
+(function(__filename){
 
 
 
@@ -11454,6 +11680,19 @@
 const MultiCheckAudit=require('./multi-check-audit.js');
 const ManifestValues=require('../computed/manifest-values.js');
 const cssParsers=require('cssstyle/lib/parsers');
+const i18n=require('../lib/i18n/i18n.js');
+
+const UIStrings={
+
+title:'Sets a theme color for the address bar.',
+
+failureTitle:'Does not set a theme color for the address bar.',
+
+description:'The browser address bar can be themed to match your site. '+
+'[Learn more](https://web.dev/themed-omnibox).'};
+
+
+const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
 
 
 
@@ -11472,10 +11711,9 @@
 static get meta(){
 return{
 id:'themed-omnibox',
-title:'Sets an address-bar theme color',
-failureTitle:'Does not set an address-bar theme color',
-description:'The browser address bar can be themed to match your site. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/address-bar).',
+title:str_(UIStrings.title),
+failureTitle:str_(UIStrings.failureTitle),
+description:str_(UIStrings.description),
 requiredArtifacts:['WebAppManifest','MetaElements']};
 
 }
@@ -11494,6 +11732,7 @@
 
 static assessMetaThemecolor(themeColorMeta,failures){
 if(!themeColorMeta){
+
 failures.push('No `<meta name="theme-color">` tag found');
 }else if(!ThemedOmnibox.isValidColor(themeColorMeta.content||'')){
 failures.push('The theme-color meta tag did not contain a valid CSS color');
@@ -11539,8 +11778,10 @@
 
 
 module.exports=ThemedOmnibox;
+module.exports.UIStrings=UIStrings;
 
-},{"../computed/manifest-values.js":13,"./multi-check-audit.js":6,"cssstyle/lib/parsers":105}],"../audits/third-party-summary":[function(require,module,exports){
+}).call(this,"/lighthouse-core/audits/themed-omnibox.js");
+},{"../computed/manifest-values.js":13,"../lib/i18n/i18n.js":67,"./multi-check-audit.js":6,"cssstyle/lib/parsers":104}],"../audits/third-party-summary":[function(require,module,exports){
 (function(__filename){
 
 
@@ -11559,7 +11800,9 @@
 
 const UIStrings={
 
-title:'Third-Party Usage',
+title:'Third-Party usage',
+
+failureTitle:'Reduce the impact of third-party code',
 
 description:'Third-party code can significantly impact load performance. '+
 'Limit the number of redundant third-party providers and try to load third-party code after '+
@@ -11567,17 +11810,18 @@
 
 columnThirdParty:'Third-Party',
 
-columnMainThreadTime:'Main Thread Time',
+columnBlockingTime:'Main-Thread Blocking Time',
 
-displayValue:`{itemCount, plural,
-    =1 {1 Third-Party Found}
-    other {# Third-Parties Found}
-  }`};
+displayValue:'Third-party code blocked the main thread for '+
+`{timeInMs, number, milliseconds}\xa0ms`};
 
 
 const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
 
 
+const PASS_THRESHOLD_IN_MS=250;
+
+
 
 class ThirdPartySummary extends Audit{
 
@@ -11587,8 +11831,8 @@
 return{
 id:'third-party-summary',
 title:str_(UIStrings.title),
+failureTitle:str_(UIStrings.failureTitle),
 description:str_(UIStrings.description),
-scoreDisplayMode:Audit.SCORING_MODES.INFORMATIVE,
 requiredArtifacts:['traces','devtoolsLogs']};
 
 }
@@ -11618,12 +11862,13 @@
 static getSummaryByEntity(networkRecords,mainThreadTasks,cpuMultiplier){
 
 const entities=new Map();
+const defaultEntityStat={mainThreadTime:0,blockingTime:0,transferSize:0};
 
 for(const request of networkRecords){
 const entity=ThirdPartySummary.getEntitySafe(request.url);
 if(!entity)continue;
 
-const entityStats=entities.get(entity)||{mainThreadTime:0,transferSize:0};
+const entityStats=entities.get(entity)||{...defaultEntityStat};
 entityStats.transferSize+=request.transferSize;
 entities.set(entity,entityStats);
 }
@@ -11635,8 +11880,14 @@
 const entity=ThirdPartySummary.getEntitySafe(attributeableURL);
 if(!entity)continue;
 
-const entityStats=entities.get(entity)||{mainThreadTime:0,transferSize:0};
-entityStats.mainThreadTime+=task.selfTime*cpuMultiplier;
+const entityStats=entities.get(entity)||{...defaultEntityStat};
+const taskDuration=task.selfTime*cpuMultiplier;
+
+entityStats.mainThreadTime+=taskDuration;
+
+
+
+entityStats.blockingTime+=Math.max(taskDuration-50,0);
 entities.set(entity,entityStats);
 }
 
@@ -11661,15 +11912,10 @@
 
 const summary={wastedBytes:0,wastedMs:0};
 
-
-
-
-const computeSortValue=stats=>stats.transferSize/1024+stats.mainThreadTime;
-
 const results=Array.from(summaryByEntity.entries()).
 map(([entity,stats])=>{
 summary.wastedBytes+=stats.transferSize;
-summary.wastedMs+=stats.mainThreadTime;
+summary.wastedMs+=stats.blockingTime;
 
 return{
 entity:{
@@ -11678,18 +11924,20 @@
 url:entity.homepage||''},
 
 transferSize:stats.transferSize,
-mainThreadTime:stats.mainThreadTime};
+mainThreadTime:stats.mainThreadTime,
+blockingTime:stats.blockingTime};
 
 }).
-sort((a,b)=>computeSortValue(b)-computeSortValue(a));
+
+sort((a,b)=>b.blockingTime-a.blockingTime||b.transferSize-a.transferSize);
 
 
 const headings=[
 {key:'entity',itemType:'link',text:str_(UIStrings.columnThirdParty)},
 {key:'transferSize',granularity:1,itemType:'bytes',
 text:str_(i18n.UIStrings.columnSize)},
-{key:'mainThreadTime',granularity:1,itemType:'ms',
-text:str_(UIStrings.columnMainThreadTime)}];
+{key:'blockingTime',granularity:1,itemType:'ms',
+text:str_(UIStrings.columnBlockingTime)}];
 
 
 if(!results.length){
@@ -11700,8 +11948,10 @@
 }
 
 return{
-score:Number(results.length===0),
-displayValue:str_(UIStrings.displayValue,{itemCount:results.length}),
+score:Number(summary.wastedMs<=PASS_THRESHOLD_IN_MS),
+displayValue:str_(UIStrings.displayValue,{
+timeInMs:summary.wastedMs}),
+
 details:Audit.makeTableDetails(headings,results,summary)};
 
 }}
@@ -11711,7 +11961,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/third-party-summary.js");
-},{"../computed/main-thread-tasks.js":12,"../computed/network-records.js":33,"../lib/i18n/i18n.js":68,"./audit.js":3,"./bootup-time.js":"../audits/bootup-time","third-party-web/httparchive-nostats-subset":172}],"../audits/time-to-first-byte":[function(require,module,exports){
+},{"../computed/main-thread-tasks.js":12,"../computed/network-records.js":33,"../lib/i18n/i18n.js":67,"./audit.js":3,"./bootup-time.js":"../audits/bootup-time","third-party-web/httparchive-nostats-subset":168}],"../audits/time-to-first-byte":[function(require,module,exports){
 (function(__filename){
 
 
@@ -11801,7 +12051,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/time-to-first-byte.js");
-},{"../computed/main-resource.js":11,"../lib/i18n/i18n.js":68,"./audit.js":3}],"../audits/user-timings":[function(require,module,exports){
+},{"../computed/main-resource.js":11,"../lib/i18n/i18n.js":67,"./audit.js":3}],"../audits/user-timings":[function(require,module,exports){
 (function(__filename){
 
 
@@ -11827,8 +12077,6 @@
     other {# user timings}
     }`,
 
-columnName:'Name',
-
 columnType:'Type',
 
 columnStartTime:'Start Time',
@@ -11901,7 +12149,7 @@
 
 
 const headings=[
-{key:'name',itemType:'text',text:str_(UIStrings.columnName)},
+{key:'name',itemType:'text',text:str_(i18n.UIStrings.columnName)},
 {key:'timingType',itemType:'text',text:str_(UIStrings.columnType)},
 {key:'startTime',itemType:'ms',granularity:0.01,
 text:str_(UIStrings.columnStartTime)},
@@ -11934,7 +12182,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/user-timings.js");
-},{"../computed/user-timings.js":39,"../lib/i18n/i18n.js":68,"./audit.js":3}],"../audits/uses-rel-preconnect":[function(require,module,exports){
+},{"../computed/user-timings.js":39,"../lib/i18n/i18n.js":67,"./audit.js":3}],"../audits/uses-rel-preconnect":[function(require,module,exports){
 (function(__filename){
 
 
@@ -11965,9 +12213,12 @@
 title:'Preconnect to required origins',
 
 description:
-'Consider adding preconnect or dns-prefetch resource hints to establish early '+
+'Consider adding `preconnect` or `dns-prefetch` resource hints to establish early '+
 `connections to important third-party origins. [Learn more](https://developers.google.com/web/fundamentals/performance/resource-prioritization#preconnect).`,
 
+
+
+
 crossoriginWarning:'A preconnect <link> was found for "{securityOrigin}" but was not used '+
 'by the browser. Check that you are using the `crossorigin` attribute properly.'};
 
@@ -12143,7 +12394,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/uses-rel-preconnect.js");
-},{"../computed/load-simulator.js":10,"../computed/main-resource.js":11,"../computed/network-records.js":33,"../lib/i18n/i18n.js":68,"../lib/url-shim.js":"url","./audit.js":3,"./byte-efficiency/byte-efficiency-audit.js":4}],"../audits/uses-rel-preload":[function(require,module,exports){
+},{"../computed/load-simulator.js":10,"../computed/main-resource.js":11,"../computed/network-records.js":33,"../lib/i18n/i18n.js":67,"../lib/url-shim.js":"url","./audit.js":3,"./byte-efficiency/byte-efficiency-audit.js":4}],"../audits/uses-rel-preload":[function(require,module,exports){
 (function(__filename){
 
 
@@ -12165,9 +12416,12 @@
 
 title:'Preload key requests',
 
-description:'Consider using <link rel=preload> to prioritize fetching resources that are '+
+description:'Consider using `<link rel=preload>` to prioritize fetching resources that are '+
 'currently requested later in page load. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/preload).',
 
+
+
+
 crossoriginWarning:'A preload <link> was found for "{preloadURL}" but was not used '+
 'by the browser. Check that you are using the `crossorigin` attribute properly.'};
 
@@ -12386,7 +12640,8 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/uses-rel-preload.js");
-},{"../computed/critical-request-chains.js":9,"../computed/load-simulator.js":10,"../computed/main-resource.js":11,"../computed/page-dependency-graph.js":34,"../lib/i18n/i18n.js":68,"../lib/url-shim.js":"url","./audit.js":3,"./byte-efficiency/byte-efficiency-audit.js":4}],"../audits/viewport":[function(require,module,exports){
+},{"../computed/critical-request-chains.js":9,"../computed/load-simulator.js":10,"../computed/main-resource.js":11,"../computed/page-dependency-graph.js":34,"../lib/i18n/i18n.js":67,"../lib/url-shim.js":"url","./audit.js":3,"./byte-efficiency/byte-efficiency-audit.js":4}],"../audits/viewport":[function(require,module,exports){
+(function(__filename){
 
 
 
@@ -12396,6 +12651,22 @@
 
 const Audit=require('./audit.js');
 const ComputedViewportMeta=require('../computed/viewport-meta.js');
+const i18n=require('../lib/i18n/i18n.js');
+
+const UIStrings={
+
+title:'Has a `<meta name="viewport">` tag with `width` or `initial-scale`',
+
+failureTitle:'Does not have a `<meta name="viewport">` tag with `width` '+
+'or `initial-scale`',
+
+description:'Add a `<meta name="viewport">` tag to optimize your app for mobile screens. '+
+'[Learn more](https://web.dev/viewport).',
+
+explanationNoTag:'No `<meta name="viewport">` tag found'};
+
+
+const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
 
 class Viewport extends Audit{
 
@@ -12404,11 +12675,9 @@
 static get meta(){
 return{
 id:'viewport',
-title:'Has a `<meta name="viewport">` tag with `width` or `initial-scale`',
-failureTitle:'Does not have a `<meta name="viewport">` tag with `width` '+
-'or `initial-scale`',
-description:'Add a viewport meta tag to optimize your app for mobile screens. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/has-viewport-meta-tag).',
+title:str_(UIStrings.title),
+failureTitle:str_(UIStrings.failureTitle),
+description:str_(UIStrings.description),
 requiredArtifacts:['MetaElements']};
 
 }
@@ -12424,7 +12693,7 @@
 if(!viewportMeta.hasViewportTag){
 return{
 score:0,
-explanation:'No viewport meta tag found'};
+explanation:str_(UIStrings.explanationNoTag)};
 
 }
 
@@ -12436,8 +12705,11 @@
 
 
 module.exports=Viewport;
+module.exports.UIStrings=UIStrings;
 
-},{"../computed/viewport-meta.js":40,"./audit.js":3}],"../audits/without-javascript":[function(require,module,exports){
+}).call(this,"/lighthouse-core/audits/viewport.js");
+},{"../computed/viewport-meta.js":40,"../lib/i18n/i18n.js":67,"./audit.js":3}],"../audits/without-javascript":[function(require,module,exports){
+(function(__filename){
 
 
 
@@ -12446,6 +12718,22 @@
 'use strict';
 
 const Audit=require('./audit.js');
+const i18n=require('../lib/i18n/i18n.js');
+
+const UIStrings={
+
+title:'Contains some content when JavaScript is not available',
+
+failureTitle:'Does not provide fallback content when JavaScript is not available',
+
+description:'Your app should display some content when JavaScript is disabled, even if '+
+'it\'s just a warning to the user that JavaScript is required to use the app. '+
+'[Learn more](https://web.dev/without-javascript).',
+
+explanation:'The page body should render some content if its scripts are not available.'};
+
+
+const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
 
 class WithoutJavaScript extends Audit{
 
@@ -12454,11 +12742,9 @@
 static get meta(){
 return{
 id:'without-javascript',
-title:'Contains some content when JavaScript is not available',
-failureTitle:'Does not provide fallback content when JavaScript is not available',
-description:'Your app should display some content when JavaScript is disabled, even if '+
-'it\'s just a warning to the user that JavaScript is required to use the app. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/no-js).',
+title:str_(UIStrings.title),
+failureTitle:str_(UIStrings.failureTitle),
+description:str_(UIStrings.description),
 requiredArtifacts:['HTMLWithoutJavaScript']};
 
 }
@@ -12474,7 +12760,7 @@
 if(artifact.bodyText.trim()===''&&!artifact.hasNoScript){
 return{
 score:0,
-explanation:'The page body should render some content if its scripts are not available.'};
+explanation:str_(UIStrings.explanation)};
 
 }
 
@@ -12485,8 +12771,11 @@
 
 
 module.exports=WithoutJavaScript;
+module.exports.UIStrings=UIStrings;
 
-},{"./audit.js":3}],"../audits/works-offline":[function(require,module,exports){
+}).call(this,"/lighthouse-core/audits/without-javascript.js");
+},{"../lib/i18n/i18n.js":67,"./audit.js":3}],"../audits/works-offline":[function(require,module,exports){
+(function(__filename){
 
 
 
@@ -12496,6 +12785,28 @@
 
 const URL=require('../lib/url-shim.js');
 const Audit=require('./audit.js');
+const i18n=require('../lib/i18n/i18n.js');
+
+const UIStrings={
+
+title:'Current page responds with a 200 when offline',
+
+failureTitle:'Current page does not respond with a 200 when offline',
+
+description:'If you\'re building a Progressive Web App, consider using a service worker '+
+'so that your app can work offline. '+
+'[Learn more](https://web.dev/works-offline).',
+
+
+
+
+
+warningNoLoad:'The page may not be loading offline because your test URL '+
+`({requested}) was redirected to "{final}". `+
+'Try testing the second URL directly.'};
+
+
+const str_=i18n.createMessageInstanceIdFn(__filename,UIStrings);
 
 class WorksOffline extends Audit{
 
@@ -12504,11 +12815,9 @@
 static get meta(){
 return{
 id:'works-offline',
-title:'Current page responds with a 200 when offline',
-failureTitle:'Current page does not respond with a 200 when offline',
-description:'If you\'re building a Progressive Web App, consider using a service worker '+
-'so that your app can work offline. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline).',
+title:str_(UIStrings.title),
+failureTitle:str_(UIStrings.failureTitle),
+description:str_(UIStrings.description),
 requiredArtifacts:['Offline','URL']};
 
 }
@@ -12522,9 +12831,8 @@
 const passed=artifacts.Offline===200;
 if(!passed&&
 !URL.equalWithExcludedFragments(artifacts.URL.requestedUrl,artifacts.URL.finalUrl)){
-warnings.push('You may be not loading offline because your test URL '+
-`(${artifacts.URL.requestedUrl}) was redirected to "${artifacts.URL.finalUrl}". `+
-'Try testing the second URL directly.');
+warnings.push(str_(UIStrings.warningNoLoad,
+{requested:artifacts.URL.requestedUrl,final:artifacts.URL.finalUrl}));
 }
 
 return{
@@ -12535,8 +12843,10 @@
 
 
 module.exports=WorksOffline;
+module.exports.UIStrings=UIStrings;
 
-},{"../lib/url-shim.js":"url","./audit.js":3}],"../gather/gatherers/accessibility":[function(require,module,exports){
+}).call(this,"/lighthouse-core/audits/works-offline.js");
+},{"../lib/i18n/i18n.js":67,"../lib/url-shim.js":"url","./audit.js":3}],"../gather/gatherers/accessibility":[function(require,module,exports){
 
 
 
@@ -12639,7 +12949,7 @@
 
 module.exports=Accessibility;
 
-},{"../../lib/page-functions.js":77,"./gatherer.js":53}],"../gather/gatherers/anchor-elements":[function(require,module,exports){
+},{"../../lib/page-functions.js":76,"./gatherer.js":53}],"../gather/gatherers/anchor-elements":[function(require,module,exports){
 
 
 
@@ -12721,7 +13031,7 @@
 
 module.exports=AnchorElements;
 
-},{"../../lib/page-functions.js":77,"./gatherer.js":53}],"../gather/gatherers/cache-contents":[function(require,module,exports){
+},{"../../lib/page-functions.js":76,"./gatherer.js":53}],"../gather/gatherers/cache-contents":[function(require,module,exports){
 
 
 
@@ -13127,7 +13437,7 @@
 
 module.exports=DOMStats;
 
-},{"../../../lib/page-functions.js":77,"../gatherer.js":53}],"../gather/gatherers/dobetterweb/optimized-images":[function(require,module,exports){
+},{"../../../lib/page-functions.js":76,"../gatherer.js":53}],"../gather/gatherers/dobetterweb/optimized-images":[function(require,module,exports){
 
 
 
@@ -13297,7 +13607,7 @@
 
 module.exports=OptimizedImages;
 
-},{"../../../lib/network-request.js":76,"../../../lib/sentry.js":79,"../../../lib/url-shim.js":"url","../../driver.js":51,"../gatherer.js":53}],"../gather/gatherers/dobetterweb/password-inputs-with-prevented-paste":[function(require,module,exports){
+},{"../../../lib/network-request.js":75,"../../../lib/sentry.js":78,"../../../lib/url-shim.js":"url","../../driver.js":51,"../gatherer.js":53}],"../gather/gatherers/dobetterweb/password-inputs-with-prevented-paste":[function(require,module,exports){
 
 
 
@@ -13344,7 +13654,7 @@
 
 module.exports=PasswordInputsWithPreventedPaste;
 
-},{"../../../lib/page-functions.js":77,"../gatherer.js":53}],"../gather/gatherers/dobetterweb/response-compression":[function(require,module,exports){
+},{"../../../lib/page-functions.js":76,"../gatherer.js":53}],"../gather/gatherers/dobetterweb/response-compression":[function(require,module,exports){
 (function(Buffer){
 
 
@@ -13470,7 +13780,7 @@
 module.exports=ResponseCompression;
 
 }).call(this,require("buffer").Buffer);
-},{"../../../lib/network-request.js":76,"../../../lib/sentry.js":79,"../../../lib/url-shim.js":"url","../gatherer.js":53,"buffer":102,"zlib":100}],"../gather/gatherers/dobetterweb/tags-blocking-first-paint":[function(require,module,exports){
+},{"../../../lib/network-request.js":75,"../../../lib/sentry.js":78,"../../../lib/url-shim.js":"url","../gatherer.js":53,"buffer":101,"zlib":99}],"../gather/gatherers/dobetterweb/tags-blocking-first-paint":[function(require,module,exports){
 
 
 
@@ -13768,7 +14078,64 @@
 
 module.exports=HTTPRedirect;
 
-},{"./gatherer.js":53}],"../gather/gatherers/image-elements":[function(require,module,exports){
+},{"./gatherer.js":53}],"../gather/gatherers/iframe-elements":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const Gatherer=require('./gatherer.js');
+const pageFunctions=require('../../lib/page-functions.js');
+
+
+
+
+
+
+
+function collectIFrameElements(){
+
+const iFrameElements=getElementsInDocument('iframe');
+return iFrameElements.map(node=>{
+const clientRect=node.getBoundingClientRect();
+const{top,bottom,left,right,width,height}=clientRect;
+return{
+id:node.id,
+src:node.src,
+clientRect:{top,bottom,left,right,width,height},
+
+isPositionFixed:isPositionFixed(node)};
+
+});
+}
+
+class IFrameElements extends Gatherer{
+
+
+
+
+
+async afterPass(passContext){
+const driver=passContext.driver;
+
+const expression=`(() => {
+      ${pageFunctions.getOuterHTMLSnippetString};
+      ${pageFunctions.getElementsInDocumentString};
+      ${pageFunctions.isPositionFixedString};
+      return (${collectIFrameElements})();
+    })()`;
+
+
+const iframeElements=await driver.evaluateAsync(expression,{useIsolation:true});
+return iframeElements;
+}}
+
+
+module.exports=IFrameElements;
+
+},{"../../lib/page-functions.js":76,"./gatherer.js":53}],"../gather/gatherers/image-elements":[function(require,module,exports){
 
 
 
@@ -13989,7 +14356,7 @@
 
 module.exports=ImageElements;
 
-},{"../../lib/page-functions.js":77,"../driver.js":51,"./gatherer.js":53}],"../gather/gatherers/js-usage":[function(require,module,exports){
+},{"../../lib/page-functions.js":76,"../driver.js":51,"./gatherer.js":53}],"../gather/gatherers/js-usage":[function(require,module,exports){
 
 
 
@@ -14150,7 +14517,7 @@
 
 module.exports=LinkElements;
 
-},{"../../lib/dependency-graph/simulator/network-analyzer.js":62,"../../lib/page-functions.js":77,"../../lib/url-shim.js":"url","./gatherer.js":53,"http-link-header":109}],"../gather/gatherers/meta-elements":[function(require,module,exports){
+},{"../../lib/dependency-graph/simulator/network-analyzer.js":62,"../../lib/page-functions.js":76,"../../lib/url-shim.js":"url","./gatherer.js":53,"http-link-header":108}],"../gather/gatherers/meta-elements":[function(require,module,exports){
 
 
 
@@ -14186,7 +14553,7 @@
 
 module.exports=MetaElements;
 
-},{"../../lib/page-functions.js":77,"./gatherer.js":53}],"../gather/gatherers/mixed-content":[function(require,module,exports){
+},{"../../lib/page-functions.js":76,"./gatherer.js":53}],"../gather/gatherers/mixed-content":[function(require,module,exports){
 (function(Buffer){
 
 
@@ -14310,7 +14677,7 @@
 module.exports=MixedContent;
 
 }).call(this,require("buffer").Buffer);
-},{"../../lib/url-shim.js":"url","../driver.js":51,"./gatherer.js":53,"buffer":102}],"../gather/gatherers/offline":[function(require,module,exports){
+},{"../../lib/url-shim.js":"url","../driver.js":51,"./gatherer.js":53,"buffer":101}],"../gather/gatherers/offline":[function(require,module,exports){
 
 
 
@@ -14326,6 +14693,9 @@
 
 
 beforePass(passContext){
+
+
+
 return passContext.driver.goOffline();
 }
 
@@ -14334,14 +14704,13 @@
 
 
 
-afterPass(passContext,loadData){
+async afterPass(passContext,loadData){
 const navigationRecord=loadData.networkRecords.filter(record=>{
 return URL.equalWithExcludedFragments(record.url,passContext.url)&&
 record.fetchedViaServiceWorker;
 }).pop();
 
-return passContext.driver.goOnline(passContext).
-then(_=>navigationRecord?navigationRecord.statusCode:-1);
+return navigationRecord?navigationRecord.statusCode:-1;
 }}
 
 
@@ -14497,7 +14866,7 @@
 
 module.exports=ScriptElements;
 
-},{"../../lib/dependency-graph/simulator/network-analyzer.js":62,"../../lib/network-request.js":76,"../../lib/page-functions.js":77,"./gatherer.js":53}],"../gather/gatherers/seo/embedded-content":[function(require,module,exports){
+},{"../../lib/dependency-graph/simulator/network-analyzer.js":62,"../../lib/network-request.js":75,"../../lib/page-functions.js":76,"./gatherer.js":53}],"../gather/gatherers/seo/embedded-content":[function(require,module,exports){
 
 
 
@@ -14540,7 +14909,7 @@
 
 module.exports=EmbeddedContent;
 
-},{"../../../lib/page-functions.js":77,"../gatherer.js":53}],"../gather/gatherers/seo/font-size":[function(require,module,exports){
+},{"../../../lib/page-functions.js":76,"../gatherer.js":53}],"../gather/gatherers/seo/font-size":[function(require,module,exports){
 
 
 
@@ -14705,7 +15074,7 @@
 
 
 
-function getEffectiveFontRule({inlineStyle,matchedCSSRules,inherited}){
+function getEffectiveFontRule({attributesStyle,inlineStyle,matchedCSSRules,inherited}){
 
 if(hasFontSizeDeclaration(inlineStyle))return{type:'Inline',...inlineStyle};
 
@@ -14714,6 +15083,9 @@
 if(matchedRule)return matchedRule;
 
 
+if(hasFontSizeDeclaration(attributesStyle))return{type:'Attributes',...attributesStyle};
+
+
 const inheritedRule=findInheritedCSSRule(inherited);
 if(inheritedRule)return inheritedRule;
 
@@ -14903,7 +15275,7 @@
 module.exports.computeSelectorSpecificity=computeSelectorSpecificity;
 module.exports.getEffectiveFontRule=getEffectiveFontRule;
 
-},{"../../../lib/sentry.js":79,"../gatherer.js":53}],"../gather/gatherers/seo/robots-txt":[function(require,module,exports){
+},{"../../../lib/sentry.js":78,"../gatherer.js":53}],"../gather/gatherers/seo/robots-txt":[function(require,module,exports){
 
 
 
@@ -15281,7 +15653,7 @@
 
 module.exports=TapTargets;
 
-},{"../../../lib/page-functions.js":77,"../../../lib/rect-helpers.js":78,"../gatherer.js":53}],"../gather/gatherers/service-worker":[function(require,module,exports){
+},{"../../../lib/page-functions.js":76,"../../../lib/rect-helpers.js":77,"../gatherer.js":53}],"../gather/gatherers/service-worker":[function(require,module,exports){
 
 
 
@@ -15309,7 +15681,170 @@
 
 module.exports=ServiceWorker;
 
-},{"./gatherer.js":53}],"../gather/gatherers/start-url":[function(require,module,exports){
+},{"./gatherer.js":53}],"../gather/gatherers/source-maps":[function(require,module,exports){
+(function(Buffer){
+
+
+
+
+
+'use strict';
+
+
+
+const Gatherer=require('./gatherer.js');
+const URL=require('../../lib/url-shim.js');
+
+
+
+
+
+
+
+
+
+
+async function fetchSourceMap(url){
+
+const response=await fetch(url);
+if(response.ok){
+return response.text();
+}else{
+throw new Error(`Received status code ${response.status} for ${url}`);
+}
+}
+
+
+
+
+class SourceMaps extends Gatherer{
+constructor(){
+super();
+
+this._scriptParsedEvents=[];
+this.onScriptParsed=this.onScriptParsed.bind(this);
+}
+
+
+
+
+
+
+async fetchSourceMapInPage(driver,sourceMapUrl){
+driver.setNextProtocolTimeout(1500);
+
+const sourceMapJson=
+await driver.evaluateAsync(`(${fetchSourceMap})(${JSON.stringify(sourceMapUrl)})`);
+return JSON.parse(sourceMapJson);
+}
+
+
+
+
+
+parseSourceMapFromDataUrl(sourceMapURL){
+const buffer=Buffer.from(sourceMapURL.split(',')[1],'base64');
+return JSON.parse(buffer.toString());
+}
+
+
+
+
+onScriptParsed(event){
+if(event.sourceMapURL){
+this._scriptParsedEvents.push(event);
+}
+}
+
+
+
+
+async beforePass(passContext){
+const driver=passContext.driver;
+driver.on('Debugger.scriptParsed',this.onScriptParsed);
+await driver.sendCommand('Debugger.enable');
+}
+
+
+
+
+
+
+_resolveUrl(url,base){
+try{
+return new URL(url,base).href;
+}catch(e){
+return;
+}
+}
+
+
+
+
+
+
+async _retrieveMapFromScriptParsedEvent(driver,event){
+if(!event.sourceMapURL){
+throw new Error('precondition failed: event.sourceMapURL should exist');
+}
+
+
+
+const isSourceMapADataUri=event.sourceMapURL.startsWith('data:');
+const scriptUrl=event.url;
+const rawSourceMapUrl=isSourceMapADataUri?
+event.sourceMapURL:
+this._resolveUrl(event.sourceMapURL,event.url);
+
+if(!rawSourceMapUrl){
+return{
+scriptUrl,
+errorMessage:`Could not resolve map url: ${event.sourceMapURL}`};
+
+}
+
+
+const sourceMapUrl=isSourceMapADataUri?undefined:rawSourceMapUrl;
+
+try{
+const map=isSourceMapADataUri?
+this.parseSourceMapFromDataUrl(rawSourceMapUrl):
+await this.fetchSourceMapInPage(driver,rawSourceMapUrl);
+return{
+scriptUrl,
+sourceMapUrl,
+map};
+
+}catch(err){
+return{
+scriptUrl,
+sourceMapUrl,
+errorMessage:err.toString()};
+
+}
+}
+
+
+
+
+
+async afterPass(passContext){
+const driver=passContext.driver;
+
+driver.off('Debugger.scriptParsed',this.onScriptParsed);
+await driver.sendCommand('Debugger.disable');
+
+const eventProcessPromises=this._scriptParsedEvents.
+map(event=>this._retrieveMapFromScriptParsedEvent(driver,event));
+
+return Promise.all(eventProcessPromises);
+}}
+
+
+module.exports=SourceMaps;
+
+}).call(this,require("buffer").Buffer);
+},{"../../lib/url-shim.js":"url","./gatherer.js":53,"buffer":101}],"../gather/gatherers/start-url":[function(require,module,exports){
 
 
 
@@ -15328,15 +15863,31 @@
 
 
 async afterPass(passContext){
+
+await passContext.driver.goOffline();
+const result=await this._determineStartUrlAvailability(passContext);
+await passContext.driver.goOnline(passContext);
+
+return result;
+}
+
+
+
+
+
+
+async _determineStartUrlAvailability(passContext){
 const manifest=passContext.baseArtifacts.WebAppManifest;
 const startUrlInfo=this._readManifestStartUrl(manifest);
 if(startUrlInfo.isReadFailure){
 return{statusCode:-1,explanation:startUrlInfo.reason};
 }
 
-return this._attemptStartURLFetch(passContext.driver,startUrlInfo.startUrl).catch(()=>{
-return{statusCode:-1,explanation:'Unable to fetch start URL via service worker.'};
-});
+try{
+return await this._attemptStartURLFetch(passContext.driver,startUrlInfo.startUrl);
+}catch(err){
+return{statusCode:-1,explanation:'Error while fetching start_url via service worker.'};
+}
 }
 
 
@@ -15371,7 +15922,7 @@
 
 const timeoutPromise=new Promise(resolve=>
 setTimeout(
-()=>resolve({statusCode:-1,explanation:'Timed out waiting for fetched start_url.'}),
+()=>resolve({statusCode:-1,explanation:'Timed out waiting for start_url to respond.'}),
 3000));
 
 
@@ -15389,7 +15940,7 @@
 if(!response.fromServiceWorker){
 return resolve({
 statusCode:-1,
-explanation:'Unable to fetch start URL via service worker.'});
+explanation:'The start_url did respond, but not via a service worker.'});
 
 }
 
@@ -15526,7 +16077,7 @@
 self.listenForStatus=listenForStatus;
 }
 
-},{"../lighthouse-core/gather/connections/raw.js":49,"../lighthouse-core/index.js":54,"lighthouse-logger":128}],2:[function(require,module,exports){
+},{"../lighthouse-core/gather/connections/raw.js":49,"../lighthouse-core/index.js":54,"lighthouse-logger":124}],2:[function(require,module,exports){
 (function(__filename){
 
 
@@ -15618,7 +16169,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/audits/accessibility/axe-audit.js");
-},{"../../lib/i18n/i18n.js":68,"../audit.js":3}],3:[function(require,module,exports){
+},{"../../lib/i18n/i18n.js":67,"../audit.js":3}],3:[function(require,module,exports){
 
 
 
@@ -15905,7 +16456,7 @@
 
 module.exports=Audit;
 
-},{"../lib/statistics.js":82,"../report/html/renderer/util.js":88}],4:[function(require,module,exports){
+},{"../lib/statistics.js":81,"../report/html/renderer/util.js":87}],4:[function(require,module,exports){
 (function(__filename){
 
 
@@ -16148,7 +16699,7 @@
 module.exports=UnusedBytes;
 
 }).call(this,"/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js");
-},{"../../computed/load-simulator.js":10,"../../computed/metrics/lantern-interactive.js":23,"../../computed/network-records.js":33,"../../computed/page-dependency-graph.js":34,"../../lib/i18n/i18n.js":68,"../../lib/statistics.js":82,"../audit.js":3}],5:[function(require,module,exports){
+},{"../../computed/load-simulator.js":10,"../../computed/metrics/lantern-interactive.js":23,"../../computed/network-records.js":33,"../../computed/page-dependency-graph.js":34,"../../lib/i18n/i18n.js":67,"../../lib/statistics.js":81,"../audit.js":3}],5:[function(require,module,exports){
 
 
 
@@ -16243,6 +16794,7 @@
 if(result.failures.length>0){
 return{
 score:0,
+
 explanation:`Failures: ${result.failures.join(',\n')}.`,
 details};
 
@@ -16362,7 +16914,7 @@
 
 module.exports=makeComputedArtifact;
 
-},{"../lib/arbitrary-equality-map.js":55,"lighthouse-logger":128}],9:[function(require,module,exports){
+},{"../lib/arbitrary-equality-map.js":55,"lighthouse-logger":124}],9:[function(require,module,exports){
 
 
 
@@ -16531,7 +17083,7 @@
 
 module.exports=makeComputedArtifact(CriticalRequestChains);
 
-},{"../lib/network-request.js":76,"./computed-artifact.js":8,"./main-resource.js":11,"./network-records.js":33,"assert":92}],10:[function(require,module,exports){
+},{"../lib/network-request.js":75,"./computed-artifact.js":8,"./main-resource.js":11,"./network-records.js":33,"assert":91}],10:[function(require,module,exports){
 
 
 
@@ -16686,7 +17238,7 @@
 
 module.exports=makeComputedArtifact(MainThreadTasks);
 
-},{"../lib/tracehouse/main-thread-tasks.js":84,"./computed-artifact.js":8,"./trace-of-tab.js":38}],13:[function(require,module,exports){
+},{"../lib/tracehouse/main-thread-tasks.js":83,"./computed-artifact.js":8,"./trace-of-tab.js":38}],13:[function(require,module,exports){
 
 
 
@@ -16805,7 +17357,7 @@
 
 module.exports=makeComputedArtifact(ManifestValues);
 
-},{"../lib/icons.js":70,"./computed-artifact.js":8}],14:[function(require,module,exports){
+},{"../lib/icons.js":69,"./computed-artifact.js":8}],14:[function(require,module,exports){
 
 
 
@@ -16881,7 +17433,7 @@
 
 module.exports=makeComputedArtifact(EstimatedInputLatency);
 
-},{"../../lib/lh-error.js":72,"../../lib/tracehouse/trace-processor.js":86,"../computed-artifact.js":8,"./lantern-estimated-input-latency.js":19,"./metric.js":29}],15:[function(require,module,exports){
+},{"../../lib/lh-error.js":71,"../../lib/tracehouse/trace-processor.js":85,"../computed-artifact.js":8,"./lantern-estimated-input-latency.js":19,"./metric.js":29}],15:[function(require,module,exports){
 
 
 
@@ -17135,7 +17687,7 @@
 
 module.exports=makeComputedArtifact(FirstCPUIdle);
 
-},{"../../lib/lh-error.js":72,"../../lib/tracehouse/trace-processor.js":86,"../computed-artifact.js":8,"./lantern-first-cpu-idle.js":21,"./metric.js":29}],17:[function(require,module,exports){
+},{"../../lib/lh-error.js":71,"../../lib/tracehouse/trace-processor.js":85,"../computed-artifact.js":8,"./lantern-first-cpu-idle.js":21,"./metric.js":29}],17:[function(require,module,exports){
 
 
 
@@ -17178,7 +17730,7 @@
 
 module.exports=makeComputedArtifact(FirstMeaningfulPaint);
 
-},{"../../lib/lh-error.js":72,"../computed-artifact.js":8,"./lantern-first-meaningful-paint.js":22,"./metric.js":29}],18:[function(require,module,exports){
+},{"../../lib/lh-error.js":71,"../computed-artifact.js":8,"./lantern-first-meaningful-paint.js":22,"./metric.js":29}],18:[function(require,module,exports){
 
 
 
@@ -17369,7 +17921,7 @@
 
 
 
-},{"../../lib/lh-error.js":72,"../../lib/network-recorder.js":75,"../../lib/tracehouse/trace-processor.js":86,"../computed-artifact.js":8,"./lantern-interactive.js":23,"./metric.js":29}],19:[function(require,module,exports){
+},{"../../lib/lh-error.js":71,"../../lib/network-recorder.js":74,"../../lib/tracehouse/trace-processor.js":85,"../computed-artifact.js":8,"./lantern-interactive.js":23,"./metric.js":29}],19:[function(require,module,exports){
 
 
 
@@ -17828,7 +18380,7 @@
 
 module.exports=makeComputedArtifact(LanternFirstMeaningfulPaint);
 
-},{"../../lib/lh-error.js":72,"../computed-artifact.js":8,"./lantern-first-contentful-paint.js":20,"./lantern-metric.js":25}],23:[function(require,module,exports){
+},{"../../lib/lh-error.js":71,"../computed-artifact.js":8,"./lantern-first-contentful-paint.js":20,"./lantern-metric.js":25}],23:[function(require,module,exports){
 
 
 
@@ -17938,7 +18490,7 @@
 
 module.exports=makeComputedArtifact(LanternInteractive);
 
-},{"../../lib/dependency-graph/base-node.js":57,"../../lib/network-request.js":76,"../computed-artifact.js":8,"./lantern-first-meaningful-paint.js":22,"./lantern-metric.js":25}],24:[function(require,module,exports){
+},{"../../lib/dependency-graph/base-node.js":57,"../../lib/network-request.js":75,"../computed-artifact.js":8,"./lantern-first-meaningful-paint.js":22,"./lantern-metric.js":25}],24:[function(require,module,exports){
 
 
 
@@ -18181,7 +18733,7 @@
 
 module.exports=LanternMetricArtifact;
 
-},{"../../lib/dependency-graph/base-node.js":57,"../../lib/network-request.js":76,"../load-simulator.js":10,"../page-dependency-graph.js":34,"../trace-of-tab.js":38}],26:[function(require,module,exports){
+},{"../../lib/dependency-graph/base-node.js":57,"../../lib/network-request.js":75,"../load-simulator.js":10,"../page-dependency-graph.js":34,"../trace-of-tab.js":38}],26:[function(require,module,exports){
 
 
 
@@ -18499,7 +19051,7 @@
 
 module.exports=makeComputedArtifact(MaxPotentialFID);
 
-},{"../../lib/lh-error.js":72,"../../lib/tracehouse/trace-processor.js":86,"../computed-artifact.js":8,"./lantern-max-potential-fid.js":24,"./metric.js":29}],29:[function(require,module,exports){
+},{"../../lib/lh-error.js":71,"../../lib/tracehouse/trace-processor.js":85,"../computed-artifact.js":8,"./lantern-max-potential-fid.js":24,"./metric.js":29}],29:[function(require,module,exports){
 
 
 
@@ -18573,7 +19125,7 @@
 
 module.exports=ComputedMetric;
 
-},{"../../lib/tracehouse/trace-processor.js":86,"../network-records.js":33,"../trace-of-tab.js":38}],30:[function(require,module,exports){
+},{"../../lib/tracehouse/trace-processor.js":85,"../network-records.js":33,"../trace-of-tab.js":38}],30:[function(require,module,exports){
 
 
 
@@ -18727,7 +19279,7 @@
 
 module.exports=makeComputedArtifact(TotalBlockingTime);
 
-},{"../../lib/lh-error.js":72,"../../lib/tracehouse/trace-processor.js":86,"../computed-artifact.js":8,"./interactive.js":18,"./lantern-total-blocking-time.js":27,"./metric.js":29}],32:[function(require,module,exports){
+},{"../../lib/lh-error.js":71,"../../lib/tracehouse/trace-processor.js":85,"../computed-artifact.js":8,"./interactive.js":18,"./lantern-total-blocking-time.js":27,"./metric.js":29}],32:[function(require,module,exports){
 
 
 
@@ -18816,7 +19368,7 @@
 
 module.exports=makeComputedArtifact(NetworkRecords);
 
-},{"../lib/network-recorder.js":75,"./computed-artifact.js":8}],34:[function(require,module,exports){
+},{"../lib/network-recorder.js":74,"./computed-artifact.js":8}],34:[function(require,module,exports){
 
 
 
@@ -19190,7 +19742,7 @@
 
 
 
-},{"../lib/dependency-graph/cpu-node.js":58,"../lib/dependency-graph/network-node.js":59,"../lib/dependency-graph/simulator/network-analyzer.js":62,"../lib/network-request.js":76,"../lib/tracehouse/trace-processor.js":86,"./computed-artifact.js":8,"./network-records.js":33,"./trace-of-tab.js":38}],35:[function(require,module,exports){
+},{"../lib/dependency-graph/cpu-node.js":58,"../lib/dependency-graph/network-node.js":59,"../lib/dependency-graph/simulator/network-analyzer.js":62,"../lib/network-request.js":75,"../lib/tracehouse/trace-processor.js":85,"./computed-artifact.js":8,"./network-records.js":33,"./trace-of-tab.js":38}],35:[function(require,module,exports){
 
 
 
@@ -19363,7 +19915,7 @@
 
 module.exports=makeComputedArtifact(Speedline);
 
-},{"../lib/lh-error.js":72,"./computed-artifact.js":8,"./trace-of-tab.js":38,"speedline-core":168}],38:[function(require,module,exports){
+},{"../lib/lh-error.js":71,"./computed-artifact.js":8,"./trace-of-tab.js":38,"speedline-core":164}],38:[function(require,module,exports){
 
 
 
@@ -19430,7 +19982,7 @@
 
 module.exports=makeComputedArtifact(TraceOfTab);
 
-},{"../lib/lh-error.js":72,"../lib/tracehouse/trace-processor.js":86,"./computed-artifact.js":8}],39:[function(require,module,exports){
+},{"../lib/lh-error.js":71,"../lib/tracehouse/trace-processor.js":85,"./computed-artifact.js":8}],39:[function(require,module,exports){
 
 
 
@@ -19573,7 +20125,7 @@
 
 
 
-},{"./computed-artifact.js":8,"metaviewport-parser":132}],41:[function(require,module,exports){
+},{"./computed-artifact.js":8,"metaviewport-parser":128}],41:[function(require,module,exports){
 
 
 
@@ -19676,6 +20228,98 @@
 
 
 
+static throwInvalidPathError(path,error){
+throw new Error(`Invalid path ${path}. ${error}\n`+
+`'Path' should be specified using the 'robots.txt' format.\n`+
+`Learn more about the 'robots.txt' format here:\n`+
+`https://developers.google.com/search/reference/robots_txt#url-matching-based-on-path-values`);
+}
+
+
+
+
+
+
+
+
+static validatePath(path){
+if(path===undefined){
+return undefined;
+}else if(typeof path!=='string'){
+this.throwInvalidPathError(path,`Path should be a string.`);
+return;
+}else if(!path.startsWith('/')){
+this.throwInvalidPathError(path,`Path should start with '/'.`);
+}else if((path.match(/\*/g)||[]).length>1){
+this.throwInvalidPathError(path,`Path should only contain one '*'.`);
+}else if((path.match(/\$/g)||[]).length>1){
+this.throwInvalidPathError(path,`Path should only contain one '$' character.`);
+}else if(path.includes('$')&&!path.endsWith('$')){
+this.throwInvalidPathError(path,`'$' character should only occur at end of path.`);
+}
+return path;
+}
+
+
+
+
+
+
+
+
+
+static urlMatchesPattern(url,pattern='/'){
+const urlObj=new URL(url);
+const urlPath=urlObj.pathname+urlObj.search;
+
+const hasWildcard=pattern.includes('*');
+const hasDollarSign=pattern.includes('$');
+
+
+
+
+
+
+
+
+
+if(!hasWildcard&&!hasDollarSign){
+return urlPath.startsWith(pattern);
+
+
+
+
+
+}else if(!hasWildcard&&hasDollarSign){
+return urlPath===pattern.slice(0,-1);
+
+
+
+
+
+
+}else if(hasWildcard&&!hasDollarSign){
+const[beforeWildcard,afterWildcard]=pattern.split('*');
+const remainingUrl=urlPath.slice(beforeWildcard.length);
+return urlPath.startsWith(beforeWildcard)&&remainingUrl.includes(afterWildcard);
+
+
+
+
+
+
+}else if(hasWildcard&&hasDollarSign){
+const[beforeWildcard,afterWildcard]=pattern.split('*');
+const urlEnd=urlPath.slice(beforeWildcard.length);
+return urlPath.startsWith(beforeWildcard)&&urlEnd.endsWith(afterWildcard.slice(0,-1));
+}
+return false;
+}
+
+
+
+
+
 static validateTimingBudget(timingBudget){
 const{metric,budget,tolerance,...invalidRest}=timingBudget;
 Budget.assertNoExcessProperties(invalidRest,'Timing Budget');
@@ -19723,9 +20367,11 @@
 
 const budget={};
 
-const{resourceSizes,resourceCounts,timings,...invalidRest}=b;
+const{path,resourceSizes,resourceCounts,timings,...invalidRest}=b;
 Budget.assertNoExcessProperties(invalidRest,'Budget');
 
+budget.path=Budget.validatePath(path);
+
 if(isArrayOfUnknownObjects(resourceSizes)){
 budget.resourceSizes=resourceSizes.map(Budget.validateResourceBudget);
 Budget.assertNoDuplicateStrings(budget.resourceSizes.map(r=>r.resourceType),
@@ -19973,7 +20619,7 @@
 
 
 }).call(this,require('_process'),"/lighthouse-core/config");
-},{"../audits/audit.js":3,"../runner.js":90,"_process":145,"path":143}],43:[function(require,module,exports){
+},{"../audits/audit.js":3,"../runner.js":89,"_process":141,"path":139}],43:[function(require,module,exports){
 
 
 
@@ -21026,7 +21672,7 @@
 module.exports=Config;
 
 }).call(this,"/lighthouse-core/config");
-},{"../runner.js":90,"./../lib/i18n/i18n.js":68,"./budget.js":41,"./config-helpers.js":42,"./config-plugin.js":43,"./constants.js":45,"./default-config.js":46,"./full-config.js":47,"lighthouse-logger":128,"lodash.isequal":129,"path":143}],45:[function(require,module,exports){
+},{"../runner.js":89,"./../lib/i18n/i18n.js":67,"./budget.js":41,"./config-helpers.js":42,"./config-plugin.js":43,"./constants.js":45,"./default-config.js":46,"./full-config.js":47,"lighthouse-logger":124,"lodash.isequal":125,"path":139}],45:[function(require,module,exports){
 
 
 
@@ -21072,7 +21718,7 @@
 
 const defaultSettings={
 output:'json',
-maxWaitForFcp:15*1000,
+maxWaitForFcp:30*1000,
 maxWaitForLoad:45*1000,
 throttlingMethod:'simulate',
 throttling:throttling.mobileSlow4G,
@@ -21221,6 +21867,15 @@
 
 seoCrawlingGroupDescription:'To appear in search results, crawlers need access to your app.',
 
+pwaCategoryTitle:'Progressive Web App',
+
+pwaCategoryDescription:'These checks validate the aspects of a Progressive Web App. '+
+'[Learn more](https://developers.google.com/web/progressive-web-apps/checklist).',
+
+pwaCategoryManualDescription:'These checks are required by the baseline '+
+'[PWA Checklist](https://developers.google.com/web/progressive-web-apps/checklist) but are '+
+'not automatically checked by Lighthouse. They do not affect your score but it\'s important that you verify them manually.',
+
 bestPracticesCategoryTitle:'Best Practices',
 
 pwaFastReliableGroupTitle:'Fast and reliable',
@@ -21252,6 +21907,7 @@
 'link-elements',
 'meta-elements',
 'script-elements',
+'iframe-elements',
 'dobetterweb/appcache',
 'dobetterweb/doctype',
 'dobetterweb/domstats',
@@ -21633,11 +22289,9 @@
 
 
 'pwa':{
-title:'Progressive Web App',
-description:'These checks validate the aspects of a Progressive Web App. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist).',
-manualDescription:'These checks are required by the baseline '+
-'[PWA Checklist](https://developers.google.com/web/progressive-web-apps/checklist) but are '+
-'not automatically checked by Lighthouse. They do not affect your score but it\'s important that you verify them manually.',
+title:str_(UIStrings.pwaCategoryTitle),
+description:str_(UIStrings.pwaCategoryDescription),
+manualDescription:str_(UIStrings.pwaCategoryManualDescription),
 auditRefs:[
 
 {id:'load-fast-enough-for-pwa',weight:7,group:'pwa-fast-reliable'},
@@ -21673,7 +22327,7 @@
 
 
 }).call(this,"/lighthouse-core/config/default-config.js");
-},{"../lib/i18n/i18n.js":68,"./constants.js":45}],47:[function(require,module,exports){
+},{"../lib/i18n/i18n.js":67,"./constants.js":45}],47:[function(require,module,exports){
 
 
 
@@ -21871,7 +22525,7 @@
 
 module.exports=Connection;
 
-},{"../../lib/lh-error.js":72,"events":108,"lighthouse-logger":128}],49:[function(require,module,exports){
+},{"../../lib/lh-error.js":71,"events":107,"lighthouse-logger":124}],49:[function(require,module,exports){
 
 
 
@@ -23573,7 +24227,7 @@
 module.exports=Driver;
 
 }).call(this,require("buffer").Buffer);
-},{"../config/constants.js":45,"../lib/element.js":65,"../lib/emulation.js":66,"../lib/lh-error.js":72,"../lib/network-recorder.js":75,"../lib/network-request.js":76,"../lib/page-functions.js":77,"../lib/url-shim.js":"url","./connections/connection.js":48,"./devtools-log.js":50,"buffer":102,"events":108,"lighthouse-logger":128}],52:[function(require,module,exports){
+},{"../config/constants.js":45,"../lib/element.js":65,"../lib/emulation.js":66,"../lib/lh-error.js":71,"../lib/network-recorder.js":74,"../lib/network-request.js":75,"../lib/page-functions.js":76,"../lib/url-shim.js":"url","./connections/connection.js":48,"./devtools-log.js":50,"buffer":101,"events":107,"lighthouse-logger":124}],52:[function(require,module,exports){
 
 
 
@@ -23585,7 +24239,7 @@
 const manifestParser=require('../lib/manifest-parser.js');
 const stacksGatherer=require('../lib/stack-collector.js');
 const LHError=require('../lib/lh-error.js');
-const URL=require('../lib/url-shim.js');
+const NetworkAnalyzer=require('../lib/dependency-graph/simulator/network-analyzer.js');
 const NetworkRecorder=require('../lib/network-recorder.js');
 const constants=require('../config/constants.js');
 const i18n=require('../lib/i18n/i18n.js');
@@ -23711,13 +24365,7 @@
 
 
 
-
-static getNetworkError(url,networkRecords){
-const mainRecord=networkRecords.find(record=>{
-
-return URL.equalWithExcludedFragments(record.url,url);
-});
-
+static getNetworkError(mainRecord){
 if(!mainRecord){
 return new LHError(LHError.errors.NO_DOCUMENT_REQUEST);
 }else if(mainRecord.failed){
@@ -23749,29 +24397,28 @@
 
 
 
-static getInterstitialError(networkRecords){
+
+static getInterstitialError(mainRecord,networkRecords){
+
+if(!mainRecord)return undefined;
+
 const interstitialRequest=networkRecords.
 find(record=>record.documentURL.startsWith('chrome-error://'));
 
 if(!interstitialRequest)return undefined;
 
-const pageNetworkRecords=networkRecords.
-filter(record=>!URL.NON_NETWORK_PROTOCOLS.includes(record.protocol)&&
-!record.documentURL.startsWith('chrome-error://'));
 
 
 
-
-if(!pageNetworkRecords.some(record=>record.failed))return undefined;
+if(!mainRecord.failed)return undefined;
 
 
-const insecureRequest=pageNetworkRecords.find(record=>
-record.failed&&record.localizedFailDescription.startsWith('net::ERR_CERT'));
-if(insecureRequest){
+if(mainRecord.localizedFailDescription.startsWith('net::ERR_CERT')){
 return new LHError(LHError.errors.INSECURE_DOCUMENT_REQUEST,{securityMessages:
-insecureRequest.localizedFailDescription});
+mainRecord.localizedFailDescription});
 }
 
+
 return new LHError(LHError.errors.CHROME_INTERSTITIAL_ERROR);
 }
 
@@ -23784,8 +24431,15 @@
 
 
 static getPageLoadError(passContext,loadData,navigationError){
-const networkError=GatherRunner.getNetworkError(passContext.url,loadData.networkRecords);
-const interstitialError=GatherRunner.getInterstitialError(loadData.networkRecords);
+const{networkRecords}=loadData;
+
+let mainRecord;
+try{
+mainRecord=NetworkAnalyzer.findMainDocument(networkRecords,passContext.url);
+}catch(_){}
+
+const networkError=GatherRunner.getNetworkError(mainRecord);
+const interstitialError=GatherRunner.getInterstitialError(mainRecord,networkRecords);
 
 
 if(!passContext.driver.online)return;
@@ -24245,7 +24899,7 @@
 
 module.exports=GatherRunner;
 
-},{"../config/constants.js":45,"../lib/i18n/i18n.js":68,"../lib/lh-error.js":72,"../lib/manifest-parser.js":73,"../lib/network-recorder.js":75,"../lib/stack-collector.js":80,"../lib/url-shim.js":"url","lighthouse-logger":128}],53:[function(require,module,exports){
+},{"../config/constants.js":45,"../lib/dependency-graph/simulator/network-analyzer.js":62,"../lib/i18n/i18n.js":67,"../lib/lh-error.js":71,"../lib/manifest-parser.js":72,"../lib/network-recorder.js":74,"../lib/stack-collector.js":79,"lighthouse-logger":124}],53:[function(require,module,exports){
 
 
 
@@ -24375,10 +25029,11 @@
 lighthouse.Audit=require('./audits/audit.js');
 lighthouse.Gatherer=require('./gather/gatherers/gatherer.js');
 lighthouse.NetworkRecords=require('./computed/network-records.js');
+lighthouse.registerLocaleData=require('./lib/i18n/i18n.js').registerLocaleData;
 
 module.exports=lighthouse;
 
-},{"./audits/audit.js":3,"./computed/network-records.js":33,"./config/config.js":44,"./gather/connections/cri.js":101,"./gather/driver.js":51,"./gather/gatherers/gatherer.js":53,"./runner.js":90,"lighthouse-logger":128}],55:[function(require,module,exports){
+},{"./audits/audit.js":3,"./computed/network-records.js":33,"./config/config.js":44,"./gather/connections/cri.js":100,"./gather/driver.js":51,"./gather/gatherers/gatherer.js":53,"./lib/i18n/i18n.js":67,"./runner.js":89,"lighthouse-logger":124}],55:[function(require,module,exports){
 
 
 
@@ -24461,7 +25116,7 @@
 
 module.exports=ArbitraryEqualityMap;
 
-},{"lodash.isequal":129}],56:[function(require,module,exports){
+},{"lodash.isequal":125}],56:[function(require,module,exports){
 (function(process){
 
 
@@ -24757,7 +25412,7 @@
 
 
 }).call(this,require('_process'));
-},{"../computed/load-simulator.js":10,"../computed/network-analysis.js":32,"../lib/lh-error.js":72,"./dependency-graph/simulator/simulator.js":63,"./lantern-trace-saver.js":71,"./traces/pwmetrics-events.js":87,"_process":145,"fs":101,"lighthouse-logger":128,"mkdirp":101,"path":143,"rimraf":101,"stream":170}],57:[function(require,module,exports){
+},{"../computed/load-simulator.js":10,"../computed/network-analysis.js":32,"../lib/lh-error.js":71,"./dependency-graph/simulator/simulator.js":63,"./lantern-trace-saver.js":70,"./traces/pwmetrics-events.js":86,"_process":141,"fs":100,"lighthouse-logger":124,"mkdirp":100,"path":139,"rimraf":100,"stream":166}],57:[function(require,module,exports){
 
 
 
@@ -25244,7 +25899,7 @@
 
 module.exports=NetworkNode;
 
-},{"../network-request.js":76,"./base-node.js":57}],60:[function(require,module,exports){
+},{"../network-request.js":75,"./base-node.js":57}],60:[function(require,module,exports){
 
 
 
@@ -25967,7 +26622,7 @@
 
 
 
-},{"../../network-request.js":76,"../../url-shim.js":"url"}],63:[function(require,module,exports){
+},{"../../network-request.js":75,"../../url-shim.js":"url"}],63:[function(require,module,exports){
 
 
 
@@ -26928,1870 +27583,6 @@
 
 
 },{}],67:[function(require,module,exports){
-module.exports={
-"lighthouse-core/audits/accessibility/accesskeys.js | description":{
-"message":"Access keys let users quickly focus a part of the page. For proper navigation, each access key must be unique. [Learn more](https://dequeuniversity.com/rules/axe/3.1/accesskeys?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/accesskeys.js | failureTitle":{
-"message":"`[accesskey]` values are not unique",
-"description":"Title of an accesibility audit that evaluates if the ARIA HTML attributes are misaligned with the aria-role HTML attribute specificed on the element, such mismatches are invalid. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/accesskeys.js | title":{
-"message":"`[accesskey]` values are unique",
-"description":"Title of an accesibility audit that evaluates if the accesskey HTML attribute values are unique across all elements. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/aria-allowed-attr.js | description":{
-"message":"Each ARIA `role` supports a specific subset of `aria-*` attributes. Mismatching these invalidates the `aria-*` attributes. [Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-allowed-attr?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/aria-allowed-attr.js | failureTitle":{
-"message":"`[aria-*]` attributes do not match their roles",
-"description":"Title of an accesibility audit that evaluates if the ARIA HTML attributes are misaligned with the aria-role HTML attribute specificed on the element, such mismatches are invalid. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/aria-allowed-attr.js | title":{
-"message":"`[aria-*]` attributes match their roles",
-"description":"Title of an accesibility audit that evaluates if the ARIA HTML attributes are misaligned with the aria-role HTML attribute specificed on the element, such mismatches are invalid. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/aria-required-attr.js | description":{
-"message":"Some ARIA roles have required attributes that describe the state of the element to screen readers. [Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-required-attr?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/aria-required-attr.js | failureTitle":{
-"message":"`[role]`s do not have all required `[aria-*]` attributes",
-"description":"Title of an accesibility audit that evaluates if all elements with the aria-role attribute have the other corresponding ARIA attributes set as well. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/aria-required-attr.js | title":{
-"message":"`[role]`s have all required `[aria-*]` attributes",
-"description":"Title of an accesibility audit that evaluates if all elements with the aria-role attribute have the other corresponding ARIA attributes set as well. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/aria-required-children.js | description":{
-"message":"Some ARIA parent roles must contain specific child roles to perform their intended accessibility functions. [Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-required-children?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/aria-required-children.js | failureTitle":{
-"message":"Elements with `[role]` that require specific children `[role]`s, are missing.",
-"description":"Title of an accesibility audit that evaluates if the elements with an aria-role that require child elements have the required children. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/aria-required-children.js | title":{
-"message":"Elements with `[role]` that require specific children `[role]`s, are present",
-"description":"Title of an accesibility audit that evaluates if the elements with an aria-role that require child elements have the required children. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/aria-required-parent.js | description":{
-"message":"Some ARIA child roles must be contained by specific parent roles to properly perform their intended accessibility functions. [Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-required-parent?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/aria-required-parent.js | failureTitle":{
-"message":"`[role]`s are not contained by their required parent element",
-"description":"Title of an accesibility audit that evaluates valid aria-role usage. Some ARIA roles require that elements must be a child of specific parent element. This audit checks that when those roles are used, the element with the role is in fact a child of the required parent. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/aria-required-parent.js | title":{
-"message":"`[role]`s are contained by their required parent element",
-"description":"Title of an accesibility audit that evaluates valid aria-role usage. Some ARIA roles require that elements must be a child of specific parent element. This audit checks that when those roles are used, the element with the role is in fact a child of the required parent. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/aria-roles.js | description":{
-"message":"ARIA roles must have valid values in order to perform their intended accessibility functions. [Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-roles?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/aria-roles.js | failureTitle":{
-"message":"`[role]` values are not valid",
-"description":"Title of an accesibility audit that evaluates if all elements have valid aria-role HTML attributes. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/aria-roles.js | title":{
-"message":"`[role]` values are valid",
-"description":"Title of an accesibility audit that evaluates if all elements have valid aria-role HTML attributes. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/aria-valid-attr-value.js | description":{
-"message":"Assistive technologies, like screen readers, can't interpret ARIA attributes with invalid values. [Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-valid-attr-value?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/aria-valid-attr-value.js | failureTitle":{
-"message":"`[aria-*]` attributes do not have valid values",
-"description":"Title of an accesibility audit that evaluates if all elements that have an ARIA HTML attribute have a valid value for that attribute. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/aria-valid-attr-value.js | title":{
-"message":"`[aria-*]` attributes have valid values",
-"description":"Title of an accesibility audit that evaluates if all elements that have an ARIA HTML attribute have a valid value for that attribute. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/aria-valid-attr.js | description":{
-"message":"Assistive technologies, like screen readers, can't interpret ARIA attributes with invalid names. [Learn more](https://dequeuniversity.com/rules/axe/3.1/aria-valid-attr?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/aria-valid-attr.js | failureTitle":{
-"message":"`[aria-*]` attributes are not valid or misspelled",
-"description":"Title of an accesibility audit that evaluates if all elements with ARIA HTML attributes have spelled the name of attribute correctly. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/aria-valid-attr.js | title":{
-"message":"`[aria-*]` attributes are valid and not misspelled",
-"description":"Title of an accesibility audit that evaluates if all elements with ARIA HTML attributes have spelled the name of attribute correctly. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/audio-caption.js | description":{
-"message":"Captions make audio elements usable for deaf or hearing-impaired users, providing critical information such as who is talking, what they're saying, and other non-speech information. [Learn more](https://dequeuniversity.com/rules/axe/3.1/audio-caption?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/audio-caption.js | failureTitle":{
-"message":"`<audio>` elements are missing a `<track>` element with `[kind=\"captions\"]`.",
-"description":"Title of an accesibility audit that evaluates if all audio elements have a track element that has captions for screen readers. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/audio-caption.js | title":{
-"message":"`<audio>` elements contain a `<track>` element with `[kind=\"captions\"]`",
-"description":"Title of an accesibility audit that evaluates if all audio elements have a track element that has captions for screen readers. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/axe-audit.js | failingElementsHeader":{
-"message":"Failing Elements",
-"description":"Label of a table column that identifies HTML elements that have failed an audit."},
-
-"lighthouse-core/audits/accessibility/button-name.js | description":{
-"message":"When a button doesn't have an accessible name, screen readers announce it as \"button\", making it unusable for users who rely on screen readers. [Learn more](https://dequeuniversity.com/rules/axe/3.1/button-name?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/button-name.js | failureTitle":{
-"message":"Buttons do not have an accessible name",
-"description":"Title of an accesibility audit that evaluates if all button elements have names accessible to screen readers. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/button-name.js | title":{
-"message":"Buttons have an accessible name",
-"description":"Title of an accesibility audit that evaluates if all button elements have names accessible to screen readers. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/bypass.js | description":{
-"message":"Adding ways to bypass repetitive content lets keyboard users navigate the page more efficiently. [Learn more](https://dequeuniversity.com/rules/axe/3.1/bypass?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/bypass.js | failureTitle":{
-"message":"The page does not contain a heading, skip link, or landmark region",
-"description":"Title of an accesibility audit that evaluates if the page has elements that let screen reader users skip over repetitive content. `heading`, `skip link`, and `landmark region` are technical terms for the elements that enable quick page navigation. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/bypass.js | title":{
-"message":"The page contains a heading, skip link, or landmark region",
-"description":"Title of an accesibility audit that evaluates if the page has elements that let screen reader users skip over repetitive content. `heading`, `skip link`, and `landmark region` are technical terms for the elements that enable quick page navigation. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/color-contrast.js | description":{
-"message":"Low-contrast text is difficult or impossible for many users to read. [Learn more](https://dequeuniversity.com/rules/axe/3.1/color-contrast?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/color-contrast.js | failureTitle":{
-"message":"Background and foreground colors do not have a sufficient contrast ratio.",
-"description":"Title of an accesibility audit that evaluates if all foreground colors are distinct enough from their background colors to be legible for users. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/color-contrast.js | title":{
-"message":"Background and foreground colors have a sufficient contrast ratio",
-"description":"Title of an accesibility audit that evaluates if all foreground colors are distinct enough from their background colors to be legible for users. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/definition-list.js | description":{
-"message":"When definition lists are not properly marked up, screen readers may produce confusing or inaccurate output. [Learn more](https://dequeuniversity.com/rules/axe/3.1/definition-list?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/definition-list.js | failureTitle":{
-"message":"`<dl>`'s do not contain only properly-ordered `<dt>` and `<dd>` groups, `<script>` or `<template>` elements.",
-"description":"Title of an accesibility audit that evaluates if all the definition list elements have valid markup for screen readers. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/definition-list.js | title":{
-"message":"`<dl>`'s contain only properly-ordered `<dt>` and `<dd>` groups, `<script>` or `<template>` elements.",
-"description":"Title of an accesibility audit that evaluates if all the definition list elements have valid markup for screen readers. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/dlitem.js | description":{
-"message":"Definition list items (`<dt>` and `<dd>`) must be wrapped in a parent `<dl>` element to ensure that screen readers can properly announce them. [Learn more](https://dequeuniversity.com/rules/axe/3.1/dlitem?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/dlitem.js | failureTitle":{
-"message":"Definition list items are not wrapped in `<dl>` elements",
-"description":"Title of an accesibility audit that evaluates if all definition list item elements (`<dt>`/`<dd>`) have a definition list parent element (`<dl>`). This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/dlitem.js | title":{
-"message":"Definition list items are wrapped in `<dl>` elements",
-"description":"Title of an accesibility audit that evaluates if all definition list item elements (`<dt>`/`<dd>`) have a definition list parent element (`<dl>`). This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/document-title.js | description":{
-"message":"The title gives screen reader users an overview of the page, and search engine users rely on it heavily to determine if a page is relevant to their search. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/title).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/document-title.js | failureTitle":{
-"message":"Document doesn't have a `<title>` element",
-"description":"Title of an accesibility audit that evaluates if the page has a <title> element that describes the page. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/document-title.js | title":{
-"message":"Document has a `<title>` element",
-"description":"Title of an accesibility audit that evaluates if the page has a <title> element that describes the page. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/duplicate-id.js | description":{
-"message":"The value of an id attribute must be unique to prevent other instances from being overlooked by assistive technologies. [Learn more](https://dequeuniversity.com/rules/axe/3.1/duplicate-id?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/duplicate-id.js | failureTitle":{
-"message":"`[id]` attributes on the page are not unique",
-"description":"Title of an accesibility audit that evaluates if there are any duplicate id HTML attributes on the page. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/duplicate-id.js | title":{
-"message":"`[id]` attributes on the page are unique",
-"description":"Title of an accesibility audit that evaluates if there are any duplicate id HTML attributes on the page. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/frame-title.js | description":{
-"message":"Screen reader users rely on frame titles to describe the contents of frames. [Learn more](https://dequeuniversity.com/rules/axe/3.1/frame-title?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/frame-title.js | failureTitle":{
-"message":"`<frame>` or `<iframe>` elements do not have a title",
-"description":"Title of an accesibility audit that evaluates if all `<frame>` and `<iframe>` elements on the page have a title HTML attribute to describe their contents. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/frame-title.js | title":{
-"message":"`<frame>` or `<iframe>` elements have a title",
-"description":"Title of an accesibility audit that evaluates if all `<frame>` and `<iframe>` elements on the page have a title HTML attribute to describe their contents. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/html-has-lang.js | description":{
-"message":"If a page doesn't specify a lang attribute, a screen reader assumes that the page is in the default language that the user chose when setting up the screen reader. If the page isn't actually in the default language, then the screen reader might not announce the page's text correctly. [Learn more](https://dequeuniversity.com/rules/axe/3.1/html-has-lang?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/html-has-lang.js | failureTitle":{
-"message":"`<html>` element does not have a `[lang]` attribute",
-"description":"Title of an accesibility audit that evaluates if the root HTML tag has a lang attribute identifying the page's language. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/html-has-lang.js | title":{
-"message":"`<html>` element has a `[lang]` attribute",
-"description":"Title of an accesibility audit that evaluates if the root HTML tag has a lang attribute identifying the page's language. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/html-lang-valid.js | description":{
-"message":"Specifying a valid [BCP 47 language](https://www.w3.org/International/questions/qa-choosing-language-tags#question) helps screen readers announce text properly. [Learn more](https://dequeuniversity.com/rules/axe/3.1/valid-lang?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/html-lang-valid.js | failureTitle":{
-"message":"`<html>` element does not have a valid value for its `[lang]` attribute.",
-"description":"Title of an accesibility audit that evaluates if the value for root HTML tag's lang attribute is a valid BCP 47 language. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/html-lang-valid.js | title":{
-"message":"`<html>` element has a valid value for its `[lang]` attribute",
-"description":"Title of an accesibility audit that evaluates if the value for root HTML tag's lang attribute is a valid BCP 47 language. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/image-alt.js | description":{
-"message":"Informative elements should aim for short, descriptive alternate text. Decorative elements can be ignored with an empty alt attribute. [Learn more](https://dequeuniversity.com/rules/axe/3.1/image-alt?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/image-alt.js | failureTitle":{
-"message":"Image elements do not have `[alt]` attributes",
-"description":"Title of an accesibility audit that evaluates if all image elements have the alt HTML attribute to describe their contents. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/image-alt.js | title":{
-"message":"Image elements have `[alt]` attributes",
-"description":"Title of an accesibility audit that evaluates if all image elements have the alt HTML attribute to describe their contents. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/input-image-alt.js | description":{
-"message":"When an image is being used as an `<input>` button, providing alternative text can help screen reader users understand the purpose of the button. [Learn more](https://dequeuniversity.com/rules/axe/3.1/input-image-alt?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/input-image-alt.js | failureTitle":{
-"message":"`<input type=\"image\">` elements do not have `[alt]` text",
-"description":"Title of an accesibility audit that evaluates if all input elements of type image have an alt HTML attribute to describe their contents. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/input-image-alt.js | title":{
-"message":"`<input type=\"image\">` elements have `[alt]` text",
-"description":"Title of an accesibility audit that evaluates if all input elements of type image have an alt HTML attribute to describe their contents. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/label.js | description":{
-"message":"Labels ensure that form controls are announced properly by assistive technologies, like screen readers. [Learn more](https://dequeuniversity.com/rules/axe/3.1/label?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/label.js | failureTitle":{
-"message":"Form elements do not have associated labels",
-"description":"Title of an accesibility audit that evaluates if all form elements have corresponding label elements. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/label.js | title":{
-"message":"Form elements have associated labels",
-"description":"Title of an accesibility audit that evaluates if all form elements have corresponding label elements. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/layout-table.js | description":{
-"message":"A table being used for layout purposes should not include data elements, such as the th or caption elements or the summary attribute, because this can create a confusing experience for screen reader users. [Learn more](https://dequeuniversity.com/rules/axe/3.1/layout-table?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/layout-table.js | failureTitle":{
-"message":"Presentational `<table>` elements do not avoid using `<th>`, `<caption>` or the `[summary]` attribute.",
-"description":"Title of an accesibility audit that evaluates if a table intended for layout contains data annotations as it can be confusing for screen readers. This is evaluated by checking if tables with the ARIA role of `presentation` or `none` contain any data elements such as table headers (`<th>`). This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/layout-table.js | title":{
-"message":"Presentational `<table>` elements avoid using `<th>`, `<caption>` or the `[summary]` attribute.",
-"description":"Title of an accesibility audit that evaluates if a table intended for layout contains data annotations as it can be confusing for screen readers. This is evaluated by checking if tables with the ARIA role of `presentation` or `none` contain any data elements such as table headers (`<th>`). This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/link-name.js | description":{
-"message":"Link text (and alternate text for images, when used as links) that is discernible, unique, and focusable improves the navigation experience for screen reader users. [Learn more](https://dequeuniversity.com/rules/axe/3.1/link-name?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/link-name.js | failureTitle":{
-"message":"Links do not have a discernible name",
-"description":"Title of an accesibility audit that evaluates if all link elements have a non-generic name to screen readers. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/link-name.js | title":{
-"message":"Links have a discernible name",
-"description":"Title of an accesibility audit that evaluates if all link elements have a non-generic name to screen readers. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/list.js | description":{
-"message":"Screen readers have a specific way of announcing lists. Ensuring proper list structure aids screen reader output. [Learn more](https://dequeuniversity.com/rules/axe/3.1/list?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/list.js | failureTitle":{
-"message":"Lists do not contain only `<li>` elements and script supporting elements (`<script>` and `<template>`).",
-"description":"Title of an accesibility audit that evaluates if all list elements have a valid structure containing only list items. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/list.js | title":{
-"message":"Lists contain only `<li>` elements and script supporting elements (`<script>` and `<template>`).",
-"description":"Title of an accesibility audit that evaluates if all list elements have a valid structure containing only list items. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/listitem.js | description":{
-"message":"Screen readers require list items (`<li>`) to be contained within a parent `<ul>` or `<ol>` to be announced properly. [Learn more](https://dequeuniversity.com/rules/axe/3.1/listitem?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/listitem.js | failureTitle":{
-"message":"List items (`<li>`) are not contained within `<ul>` or `<ol>` parent elements.",
-"description":"Title of an accesibility audit that evaluates if any list item elements do not have list parent elements. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/listitem.js | title":{
-"message":"List items (`<li>`) are contained within `<ul>` or `<ol>` parent elements",
-"description":"Title of an accesibility audit that evaluates if any list item elements do not have list parent elements. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/meta-refresh.js | description":{
-"message":"Users do not expect a page to refresh automatically, and doing so will move focus back to the top of the page. This may create a frustrating or confusing experience. [Learn more](https://dequeuniversity.com/rules/axe/3.1/meta-refresh?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/meta-refresh.js | failureTitle":{
-"message":"The document uses `<meta http-equiv=\"refresh\">`",
-"description":"Title of an accesibility audit that evaluates if the page uses a meta tag that refreshes the page automatically. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/meta-refresh.js | title":{
-"message":"The document does not use `<meta http-equiv=\"refresh\">`",
-"description":"Title of an accesibility audit that evaluates if the page uses a meta tag that refreshes the page automatically. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/meta-viewport.js | description":{
-"message":"Disabling zooming is problematic for users with low vision who rely on screen magnification to properly see the contents of a web page. [Learn more](https://dequeuniversity.com/rules/axe/3.1/meta-viewport?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/meta-viewport.js | failureTitle":{
-"message":"`[user-scalable=\"no\"]` is used in the `<meta name=\"viewport\">` element or the `[maximum-scale]` attribute is less than 5.",
-"description":"Title of an accesibility audit that evaluates if the page has limited the scaling properties of the page in a way that harms users with low vision. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/meta-viewport.js | title":{
-"message":"`[user-scalable=\"no\"]` is not used in the `<meta name=\"viewport\">` element and the `[maximum-scale]` attribute is not less than 5.",
-"description":"Title of an accesibility audit that evaluates if the page has limited the scaling properties of the page in a way that harms users with low vision. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/object-alt.js | description":{
-"message":"Screen readers cannot translate non-text content. Adding alt text to `<object>` elements helps screen readers convey meaning to users. [Learn more](https://dequeuniversity.com/rules/axe/3.1/object-alt?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/object-alt.js | failureTitle":{
-"message":"`<object>` elements do not have `[alt]` text",
-"description":"Title of an accesibility audit that evaluates if all object elements have an alt HTML attribute that describes their contents. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/object-alt.js | title":{
-"message":"`<object>` elements have `[alt]` text",
-"description":"Title of an accesibility audit that evaluates if all object elements have an alt HTML attribute that describes their contents. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/tabindex.js | description":{
-"message":"A value greater than 0 implies an explicit navigation ordering. Although technically valid, this often creates frustrating experiences for users who rely on assistive technologies. [Learn more](https://dequeuniversity.com/rules/axe/3.1/tabindex?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/tabindex.js | failureTitle":{
-"message":"Some elements have a `[tabindex]` value greater than 0",
-"description":"Title of an accesibility audit that evaluates if any elements have custom tabindex HTML attributes that might frustrate users of assitive technology. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/tabindex.js | title":{
-"message":"No element has a `[tabindex]` value greater than 0",
-"description":"Title of an accesibility audit that evaluates if any elements have custom tabindex HTML attributes that might frustrate users of assitive technology. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/td-headers-attr.js | description":{
-"message":"Screen readers have features to make navigating tables easier. Ensuring `<td>` cells using the `[headers]` attribute only refer to other cells in the same table may improve the experience for screen reader users. [Learn more](https://dequeuniversity.com/rules/axe/3.1/td-headers-attr?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/td-headers-attr.js | failureTitle":{
-"message":"Cells in a `<table>` element that use the `[headers]` attribute refers to other cells of that same table.",
-"description":"Title of an accesibility audit that evaluates if all table cell elements in a table that use the headers HTML attribute use it correctly to refer to cells within the same table. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/td-headers-attr.js | title":{
-"message":"Cells in a `<table>` element that use the `[headers]` attribute only refer to other cells of that same table.",
-"description":"Title of an accesibility audit that evaluates if all table cell elements in a table that use the headers HTML attribute use it correctly to refer to cells within the same table. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/th-has-data-cells.js | description":{
-"message":"Screen readers have features to make navigating tables easier. Ensuring table headers always refer to some set of cells may improve the experience for screen reader users. [Learn more](https://dequeuniversity.com/rules/axe/3.1/th-has-data-cells?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/th-has-data-cells.js | failureTitle":{
-"message":"`<th>` elements and elements with `[role=\"columnheader\"/\"rowheader\"]` do not have data cells they describe.",
-"description":"Title of an accesibility audit that evaluates if all table header elements have children. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/th-has-data-cells.js | title":{
-"message":"`<th>` elements and elements with `[role=\"columnheader\"/\"rowheader\"]` have data cells they describe.",
-"description":"Title of an accesibility audit that evaluates if all table header elements have children. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/valid-lang.js | description":{
-"message":"Specifying a valid [BCP 47 language](https://www.w3.org/International/questions/qa-choosing-language-tags#question) on elements helps ensure that text is pronounced correctly by a screen reader. [Learn more](https://dequeuniversity.com/rules/axe/3.1/valid-lang?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/valid-lang.js | failureTitle":{
-"message":"`[lang]` attributes do not have a valid value",
-"description":"Title of an accesibility audit that evaluates if all lang HTML attributes are valid BCP 47 languages. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/valid-lang.js | title":{
-"message":"`[lang]` attributes have a valid value",
-"description":"Title of an accesibility audit that evaluates if all lang HTML attributes are valid BCP 47 languages. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/video-caption.js | description":{
-"message":"When a video provides a caption it is easier for deaf and hearing impaired users to access its information. [Learn more](https://dequeuniversity.com/rules/axe/3.1/video-caption?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/video-caption.js | failureTitle":{
-"message":"`<video>` elements do not contain a `<track>` element with `[kind=\"captions\"]`.",
-"description":"Title of an accesibility audit that evaluates if all video elements contain a child track element that has captions describing their audio. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/video-caption.js | title":{
-"message":"`<video>` elements contain a `<track>` element with `[kind=\"captions\"]`",
-"description":"Title of an accesibility audit that evaluates if all video elements contain a child track element that has captions describing their audio. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/accessibility/video-description.js | description":{
-"message":"Audio descriptions provide relevant information for videos that dialogue cannot, such as facial expressions and scenes. [Learn more](https://dequeuniversity.com/rules/axe/3.1/video-description?application=lighthouse).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/accessibility/video-description.js | failureTitle":{
-"message":"`<video>` elements do not contain a `<track>` element with `[kind=\"description\"]`.",
-"description":"Title of an accesibility audit that evaluates if all video elements have child track elements that contain a description of the video content. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed."},
-
-"lighthouse-core/audits/accessibility/video-description.js | title":{
-"message":"`<video>` elements contain a `<track>` element with `[kind=\"description\"]`",
-"description":"Title of an accesibility audit that evaluates if all video elements have child track elements that contain a description of the video content. This title is descriptive of the successful state and is shown to users when no user action is required."},
-
-"lighthouse-core/audits/apple-touch-icon.js | description":{
-"message":"For ideal appearance on iOS when users add to the home screen, define an apple-touch-icon. It must point to a non-transparent 192px (or 180px) square PNG. [Learn More](https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/).",
-"description":"Description of a Lighthouse audit that tells the user what having a valid apple-touch-icon does. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. \"apple-touch-icon\" is an HTML attribute value and should not be translated."},
-
-"lighthouse-core/audits/apple-touch-icon.js | failureTitle":{
-"message":"Does not provide a valid `apple-touch-icon`",
-"description":"Title of a Lighthouse audit that tells the user that their site contains a vaild touch icon. This descriptive title is shown when the page does not contain a valid iOS touch icon. \"apple-touch-icon\" is an HTML attribute value and should not be translated."},
-
-"lighthouse-core/audits/apple-touch-icon.js | precomposedWarning":{
-"message":"`apple-touch-icon-precomposed` is out of date; `apple-touch-icon` is preferred.",
-"description":"Warning that HTML attribute `apple-touch-icon-precomposed` should not be used in favor of `apple-touch-icon`.  \"apple-touch-icon-precomposed\" and \"apple-touch-icon\" are HTML attribute values and should not be translated."},
-
-"lighthouse-core/audits/apple-touch-icon.js | title":{
-"message":"Provides a valid `apple-touch-icon`",
-"description":"Title of a Lighthouse audit that tells the user that their site contains a vaild touch icon. This descriptive title is shown when the page contains a valid iOS touch icon. \"apple-touch-icon\" is an HTML attribute value and should not be translated."},
-
-"lighthouse-core/audits/bootup-time.js | chromeExtensionsWarning":{
-"message":"Chrome extensions negatively affected this page's load performance. Try auditing the page in incognito mode or from a Chrome profile without extensions.",
-"description":"A message displayed in a Lighthouse audit result warning that Chrome extensions on the user's system substantially affected Lighthouse's measurements and instructs the user on how to run again without those extensions."},
-
-"lighthouse-core/audits/bootup-time.js | columnScriptEval":{
-"message":"Script Evaluation",
-"description":"Label for a time column in a data table; entries will be the number of milliseconds spent evaluating script for every script loaded by the page."},
-
-"lighthouse-core/audits/bootup-time.js | columnScriptParse":{
-"message":"Script Parse",
-"description":"Label for a time column in a data table; entries will be the number of milliseconds spent parsing script files for every script loaded by the page."},
-
-"lighthouse-core/audits/bootup-time.js | columnTotal":{
-"message":"Total CPU Time",
-"description":"Label for the total time column in a data table; entries will be the number of milliseconds spent executing per resource loaded by the page."},
-
-"lighthouse-core/audits/bootup-time.js | description":{
-"message":"Consider reducing the time spent parsing, compiling, and executing JS. You may find delivering smaller JS payloads helps with this. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/bootup).",
-"description":"Description of a Lighthouse audit that tells the user that they should reduce the amount of time spent executing javascript and one method of doing so. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/bootup-time.js | failureTitle":{
-"message":"Reduce JavaScript execution time",
-"description":"Title of a diagnostic audit that provides detail on the time spent executing javascript files during the load. This imperative title is shown to users when there is a significant amount of execution time that could be reduced."},
-
-"lighthouse-core/audits/bootup-time.js | title":{
-"message":"JavaScript execution time",
-"description":"Title of a diagnostic audit that provides detail on the time spent executing javascript files during the load. This descriptive title is shown to users when the amount is acceptable and no user action is required."},
-
-"lighthouse-core/audits/byte-efficiency/efficient-animated-content.js | description":{
-"message":"Large GIFs are inefficient for delivering animated content. Consider using MPEG4/WebM videos for animations and PNG/WebP for static images instead of GIF to save network bytes. [Learn more](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/replace-animated-gifs-with-video/)",
-"description":"Description of a Lighthouse audit that tells the user *why* they should use video instead of GIF format for delivering animated content. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/byte-efficiency/efficient-animated-content.js | title":{
-"message":"Use video formats for animated content",
-"description":"Imperative title of a Lighthouse audit that tells the user to use video formats rather than animated GIFs, which are wasteful. This is displayed in a list of audit titles that Lighthouse generates."},
-
-"lighthouse-core/audits/byte-efficiency/offscreen-images.js | description":{
-"message":"Consider lazy-loading offscreen and hidden images after all critical resources have finished loading to lower time to interactive. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/offscreen-images).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should defer loading offscreen images. Offscreen images are images located outside of the visible browser viewport. As they are unseen by the user and slow down page load, they should be loaded later, closer to when the user is going to see them. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/byte-efficiency/offscreen-images.js | title":{
-"message":"Defer offscreen images",
-"description":"Imperative title of a Lighthouse audit that tells the user to defer loading offscreen images. Offscreen images are images located outside of the visible browser viewport. As they are unseen by the user and slow down page load, they should be loaded later, closer to when the user is going to see them. This is displayed in a list of audit titles that Lighthouse generates."},
-
-"lighthouse-core/audits/byte-efficiency/render-blocking-resources.js | description":{
-"message":"Resources are blocking the first paint of your page. Consider delivering critical JS/CSS inline and deferring all non-critical JS/styles. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/blocking-resources).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should reduce or remove network resources that block the initial render of the page. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/byte-efficiency/render-blocking-resources.js | title":{
-"message":"Eliminate render-blocking resources",
-"description":"Imperative title of a Lighthouse audit that tells the user to reduce or remove network resources that block the initial render of the page. This is displayed in a list of audit titles that Lighthouse generates."},
-
-"lighthouse-core/audits/byte-efficiency/total-byte-weight.js | description":{
-"message":"Large network payloads cost users real money and are highly correlated with long load times. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/network-payloads).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should reduce the size of the network resources required by the page. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/byte-efficiency/total-byte-weight.js | displayValue":{
-"message":"Total size was {totalBytes, number, bytes} KB",
-"description":"Used to summarize the total byte size of the page and all its network requests. The `{totalBytes}` placeholder will be replaced with the total byte sizes, shown in kilobytes (e.g. 142 KB)"},
-
-"lighthouse-core/audits/byte-efficiency/total-byte-weight.js | failureTitle":{
-"message":"Avoid enormous network payloads",
-"description":"Title of a diagnostic audit that provides detail on large network resources required during page load. 'Payloads' is roughly equivalent to 'resources'. This imperative title is shown to users when there is a significant amount of execution time that could be reduced."},
-
-"lighthouse-core/audits/byte-efficiency/total-byte-weight.js | title":{
-"message":"Avoids enormous network payloads",
-"description":"Title of a diagnostic audit that provides detail on large network resources required during page load. 'Payloads' is roughly equivalent to 'resources'. This descriptive title is shown to users when the amount is acceptable and no user action is required."},
-
-"lighthouse-core/audits/byte-efficiency/unminified-css.js | description":{
-"message":"Minifying CSS files can reduce network payload sizes. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/minify-css).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should minify (remove whitespace) the page's CSS code. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/byte-efficiency/unminified-css.js | title":{
-"message":"Minify CSS",
-"description":"Imperative title of a Lighthouse audit that tells the user to minify (remove whitespace) the page's CSS code. This is displayed in a list of audit titles that Lighthouse generates."},
-
-"lighthouse-core/audits/byte-efficiency/unminified-javascript.js | description":{
-"message":"Minifying JavaScript files can reduce payload sizes and script parse time. [Learn more](https://developers.google.com/speed/docs/insights/MinifyResources).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should minify the page’s JS code to reduce file size. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/byte-efficiency/unminified-javascript.js | title":{
-"message":"Minify JavaScript",
-"description":"Imperative title of a Lighthouse audit that tells the user to minify the page’s JS code to reduce file size. This is displayed in a list of audit titles that Lighthouse generates."},
-
-"lighthouse-core/audits/byte-efficiency/unused-css-rules.js | description":{
-"message":"Remove dead rules from stylesheets and defer the loading of CSS not used for above-the-fold content to reduce unnecessary bytes consumed by network activity. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/unused-css).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should defer loading any content in CSS that isn’t needed at page load. This is displayed after a user expands the section to see more. No word length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/byte-efficiency/unused-css-rules.js | title":{
-"message":"Remove unused CSS",
-"description":"Imperative title of a Lighthouse audit that tells the user to remove content from their CSS that isn’t needed immediately and instead load that content at a later time. This is displayed in a list of audit titles that Lighthouse generates."},
-
-"lighthouse-core/audits/byte-efficiency/unused-javascript.js | description":{
-"message":"Remove unused JavaScript to reduce bytes consumed by network activity.",
-"description":"Description of a Lighthouse audit that tells the user *why* they should remove JavaScript that is never needed/evaluated by the browser. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/byte-efficiency/unused-javascript.js | title":{
-"message":"Remove unused JavaScript",
-"description":"Imperative title of a Lighthouse audit that tells the user to remove JavaScript that is never evaluated during page load. This is displayed in a list of audit titles that Lighthouse generates."},
-
-"lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | description":{
-"message":"A long cache lifetime can speed up repeat visits to your page. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/cache-policy).",
-"description":"Description of a Lighthouse audit that tells the user *why* they need to adopt a long cache lifetime policy. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | displayValue":{
-"message":"{itemCount, plural,\n    =1 {1 resource found}\n    other {# resources found}\n    }",
-"description":"[ICU Syntax] Label for the audit identifying network resources with inefficient cache values. Clicking this will expand the audit to show the resources."},
-
-"lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | failureTitle":{
-"message":"Serve static assets with an efficient cache policy",
-"description":"Title of a diagnostic audit that provides details on the any page resources that could have been served with more efficient cache policies. Cache refers to browser disk cache, which keeps old versions of network resources around for future use. This imperative title is shown to users when there is a significant amount of assets served with poor cache policies."},
-
-"lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js | title":{
-"message":"Uses efficient cache policy on static assets",
-"description":"Title of a diagnostic audit that provides detail on the cache policy applies to the page's static assets. Cache refers to browser disk cache, which keeps old versions of network resources around for future use. This is displayed in a list of audit titles that Lighthouse generates."},
-
-"lighthouse-core/audits/byte-efficiency/uses-optimized-images.js | description":{
-"message":"Optimized images load faster and consume less cellular data. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/optimize-images).",
-"description":"Description of a Lighthouse audit that tells the user *why* they need to efficiently encode images. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/byte-efficiency/uses-optimized-images.js | title":{
-"message":"Efficiently encode images",
-"description":"Imperative title of a Lighthouse audit that tells the user to encode images with optimization (better compression). This is displayed in a list of audit titles that Lighthouse generates."},
-
-"lighthouse-core/audits/byte-efficiency/uses-responsive-images.js | description":{
-"message":"Serve images that are appropriately-sized to save cellular data and improve load time. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/oversized-images).",
-"description":"Description of a Lighthouse audit that tells the user *why* they need to serve appropriately sized images. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/byte-efficiency/uses-responsive-images.js | title":{
-"message":"Properly size images",
-"description":"Imperative title of a Lighthouse audit that tells the user to resize images to match the display dimensions. This is displayed in a list of audit titles that Lighthouse generates."},
-
-"lighthouse-core/audits/byte-efficiency/uses-text-compression.js | description":{
-"message":"Text-based resources should be served with compression (gzip, deflate or brotli) to minimize total network bytes. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/text-compression).",
-"description":"Description of a Lighthouse audit that tells the user *why* their text-based resources should be served with compression (like gzip). This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/byte-efficiency/uses-text-compression.js | title":{
-"message":"Enable text compression",
-"description":"Imperative title of a Lighthouse audit that tells the user to enable text compression (like gzip) in order to enhance the performance of a page. This is displayed in a list of audit titles that Lighthouse generates."},
-
-"lighthouse-core/audits/byte-efficiency/uses-webp-images.js | description":{
-"message":"Image formats like JPEG 2000, JPEG XR, and WebP often provide better compression than PNG or JPEG, which means faster downloads and less data consumption. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/webp).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should use newer and more efficient image formats. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/byte-efficiency/uses-webp-images.js | title":{
-"message":"Serve images in next-gen formats",
-"description":"Imperative title of a Lighthouse audit that tells the user to serve images in newer and more efficient image formats in order to enhance the performance of a page. A non-modern image format was designed 20+ years ago. This is displayed in a list of audit titles that Lighthouse generates."},
-
-"lighthouse-core/audits/critical-request-chains.js | description":{
-"message":"The Critical Request Chains below show you what resources are loaded with a high priority. Consider reducing the length of chains, reducing the download size of resources, or deferring the download of unnecessary resources to improve page load. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/critical-request-chains).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should reduce the depth of critical network requests to enhance initial load of a page . This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/critical-request-chains.js | displayValue":{
-"message":"{itemCount, plural,\n    =1 {1 chain found}\n    other {# chains found}\n    }",
-"description":"[ICU Syntax] Label for an audit identifying the number of sequences of dependent network requests used to load the page."},
-
-"lighthouse-core/audits/critical-request-chains.js | title":{
-"message":"Minimize Critical Requests Depth",
-"description":"Imperative title of a Lighthouse audit that tells the user to reduce the depth of critical network requests to enhance initial load of a page. Critical request chains are series of dependent network requests that are important for page rendering. For example, here's a 4-request-deep chain: The biglogo.jpg image is required, but is requested via the styles.css style code, which is requested by the initialize.js javascript, which is requested by the page's HTML. This is displayed in a list of audit titles that Lighthouse generates."},
-
-"lighthouse-core/audits/deprecations.js | columnDeprecate":{
-"message":"Deprecation / Warning",
-"description":"Header of the table column which displays the warning message describing use of a deprecated API by code running in the web page."},
-
-"lighthouse-core/audits/deprecations.js | columnLine":{
-"message":"Line",
-"description":"Table column header for line of code (eg. 432) that is using a deprecated API."},
-
-"lighthouse-core/audits/deprecations.js | description":{
-"message":"Deprecated APIs will eventually be removed from the browser. [Learn more](https://www.chromestatus.com/features#deprecated).",
-"description":"Description of a Lighthouse audit that tells the user why they should not use deprecated APIs on their page. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/deprecations.js | displayValue":{
-"message":"{itemCount, plural,\n    =1 {1 warning found}\n    other {# warnings found}\n    }",
-"description":"[ICU Syntax] Label for the audit identifying the number of warnings generated by using deprecated APIs."},
-
-"lighthouse-core/audits/deprecations.js | failureTitle":{
-"message":"Uses deprecated APIs",
-"description":"Title of a Lighthouse audit that provides detail on the use of deprecated APIs. This descriptive title is shown to users when the page uses deprecated APIs."},
-
-"lighthouse-core/audits/deprecations.js | title":{
-"message":"Avoids deprecated APIs",
-"description":"Title of a Lighthouse audit that provides detail on the use of deprecated APIs. This descriptive title is shown to users when the page does not use deprecated APIs."},
-
-"lighthouse-core/audits/dobetterweb/appcache-manifest.js | description":{
-"message":"Application Cache is deprecated. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/appcache).",
-"description":"Description of a Lighthouse audit that tells the user why they should not use the Application Cache API. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/dobetterweb/appcache-manifest.js | displayValue":{
-"message":"Found \"{AppCacheManifest}\"",
-"description":"Label for the audit identifying uses of the Application Cache."},
-
-"lighthouse-core/audits/dobetterweb/appcache-manifest.js | failureTitle":{
-"message":"Uses Application Cache",
-"description":"Title of a Lighthouse audit that provides detail on the use of the Application Cache API. This descriptive title is shown to users when they do use the Application Cache API, which is considered bad practice."},
-
-"lighthouse-core/audits/dobetterweb/appcache-manifest.js | title":{
-"message":"Avoids Application Cache",
-"description":"Title of a Lighthouse audit that provides detail on the use of the Application Cache API. This descriptive title is shown to users when they do not use the Application Cache API."},
-
-"lighthouse-core/audits/dobetterweb/doctype.js | description":{
-"message":"Specifying a doctype prevents the browser from switching to quirks-mode. Read more on the [MDN Web Docs page](https://developer.mozilla.org/en-US/docs/Glossary/Doctype)",
-"description":"Description of a Lighthouse audit that tells the user why they should define an HTML doctype. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/dobetterweb/doctype.js | explanationBadDoctype":{
-"message":"Doctype name must be the lowercase string `html`",
-"description":"Explanatory message stating that the doctype is set, but is not \"html\" and is therefore invalid."},
-
-"lighthouse-core/audits/dobetterweb/doctype.js | explanationNoDoctype":{
-"message":"Document must contain a doctype",
-"description":"Explanatory message stating that the document has no doctype."},
-
-"lighthouse-core/audits/dobetterweb/doctype.js | explanationPublicId":{
-"message":"Expected publicId to be an empty string",
-"description":"Explanatory message stating that the publicId field is not empty."},
-
-"lighthouse-core/audits/dobetterweb/doctype.js | explanationSystemId":{
-"message":"Expected systemId to be an empty string",
-"description":"Explanatory message stating that the systemId field is not empty."},
-
-"lighthouse-core/audits/dobetterweb/doctype.js | failureTitle":{
-"message":"Page lacks the HTML doctype, thus triggering quirks-mode",
-"description":"Title of a Lighthouse audit that provides detail on the doctype of a page. This descriptive title is shown to users when the page's doctype is not set to HTML."},
-
-"lighthouse-core/audits/dobetterweb/doctype.js | title":{
-"message":"Page has the HTML doctype",
-"description":"Title of a Lighthouse audit that provides detail on the doctype of a page. This descriptive title is shown to users when the pages's doctype is set to HTML."},
-
-"lighthouse-core/audits/dobetterweb/dom-size.js | columnElement":{
-"message":"Element",
-"description":"Table column header for the DOM element. Each DOM element is described with its HTML representation."},
-
-"lighthouse-core/audits/dobetterweb/dom-size.js | columnStatistic":{
-"message":"Statistic",
-"description":"Table column header for the type of statistic. These statistics describe how big the DOM is (count of DOM elements, children, depth)."},
-
-"lighthouse-core/audits/dobetterweb/dom-size.js | columnValue":{
-"message":"Value",
-"description":"Table column header for the observed value of the DOM statistic."},
-
-"lighthouse-core/audits/dobetterweb/dom-size.js | description":{
-"message":"Browser engineers recommend pages contain fewer than ~1,500 DOM elements. The sweet spot is a tree depth < 32 elements and fewer than 60 children/parent element. A large DOM can increase memory usage, cause longer [style calculations](https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations), and produce costly [layout reflows](https://developers.google.com/speed/articles/reflow). [Learn more](https://developers.google.com/web/tools/lighthouse/audits/dom-size).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should reduce the size of the web page's DOM. The size of a DOM is characterized by the total number of DOM elements and greatest DOM depth. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/dobetterweb/dom-size.js | displayValue":{
-"message":"{itemCount, plural,\n    =1 {1 element}\n    other {# elements}\n    }",
-"description":"[ICU Syntax] Label for an audit identifying the number of DOM elements found in the page."},
-
-"lighthouse-core/audits/dobetterweb/dom-size.js | failureTitle":{
-"message":"Avoid an excessive DOM size",
-"description":"Title of a diagnostic audit that provides detail on the size of the web page's DOM. The size of a DOM is characterized by the total number of DOM elements and greatest DOM depth. This imperative title is shown to users when there is a significant amount of execution time that could be reduced."},
-
-"lighthouse-core/audits/dobetterweb/dom-size.js | statisticDOMDepth":{
-"message":"Maximum DOM Depth",
-"description":"Label for the numeric value of the maximum depth in the page's DOM tree."},
-
-"lighthouse-core/audits/dobetterweb/dom-size.js | statisticDOMElements":{
-"message":"Total DOM Elements",
-"description":"Label for the total number of DOM elements found in the page."},
-
-"lighthouse-core/audits/dobetterweb/dom-size.js | statisticDOMWidth":{
-"message":"Maximum Child Elements",
-"description":"Label for the numeric value of the maximum number of children any DOM element in the page has. The element described will have the most children in the page."},
-
-"lighthouse-core/audits/dobetterweb/dom-size.js | title":{
-"message":"Avoids an excessive DOM size",
-"description":"Title of a diagnostic audit that provides detail on the size of the web page's DOM. The size of a DOM is characterized by the total number of DOM elements and greatest DOM depth. This descriptive title is shown to users when the amount is acceptable and no user action is required."},
-
-"lighthouse-core/audits/dobetterweb/external-anchors-use-rel-noopener.js | columnRel":{
-"message":"Rel",
-"description":"Label for a column in a data table; entries will be the values of the html \"rel\" attribute from link in a page."},
-
-"lighthouse-core/audits/dobetterweb/external-anchors-use-rel-noopener.js | columnTarget":{
-"message":"Target",
-"description":"Label for a column in a data table; entries will be the target attribute of a link. Each entry is either an empty string or a string like `_blank`."},
-
-"lighthouse-core/audits/dobetterweb/external-anchors-use-rel-noopener.js | description":{
-"message":"Add `rel=\"noopener\"` or `rel=\"noreferrer\"` to any external links to improve performance and prevent security vulnerabilities. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/noopener).",
-"description":"Description of a Lighthouse audit that tells the user why and how they should secure cross-origin links. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/dobetterweb/external-anchors-use-rel-noopener.js | failureTitle":{
-"message":"Links to cross-origin destinations are unsafe",
-"description":"Title of a Lighthouse audit that provides detail on the cross-origin links that the web page contains, and whether the links can be considered safe. This descriptive title is shown to users when not all links can be considered safe."},
-
-"lighthouse-core/audits/dobetterweb/external-anchors-use-rel-noopener.js | title":{
-"message":"Links to cross-origin destinations are safe",
-"description":"Title of a Lighthouse audit that provides detail on the cross-origin links that the web page contains, and whether the links can be considered safe. This descriptive title is shown to users when all links are safe."},
-
-"lighthouse-core/audits/dobetterweb/external-anchors-use-rel-noopener.js | warning":{
-"message":"Unable to determine the destination for anchor ({anchorHTML}). If not used as a hyperlink, consider removing target=_blank.",
-"description":"Warning that some links' destinations cannot be determined and therefore the audit cannot evaluate the link's safety."},
-
-"lighthouse-core/audits/dobetterweb/geolocation-on-start.js | description":{
-"message":"Users are mistrustful of or confused by sites that request their location without context. Consider tying the request to a user action instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/geolocation-on-load).",
-"description":"Description of a Lighthouse audit that tells the user why they should not ask for geolocation permissions on load. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/dobetterweb/geolocation-on-start.js | failureTitle":{
-"message":"Requests the geolocation permission on page load",
-"description":"Title of a Lighthouse audit that provides detail on geolocation permissions requests. This descriptive title is shown to users when the page does ask for geolocation permissions on load."},
-
-"lighthouse-core/audits/dobetterweb/geolocation-on-start.js | title":{
-"message":"Avoids requesting the geolocation permission on page load",
-"description":"Title of a Lighthouse audit that provides detail on geolocation permission requests while the page is loading. This descriptive title is shown to users when the page does not ask for geolocation permissions on load."},
-
-"lighthouse-core/audits/dobetterweb/js-libraries.js | columnName":{
-"message":"Name",
-"description":"Label for a column in a data table; entries will be the names of the detected Javascript libraries."},
-
-"lighthouse-core/audits/dobetterweb/js-libraries.js | columnVersion":{
-"message":"Version",
-"description":"Label for a column in a data table; entries will be the version numbers of the detected Javascript libraries."},
-
-"lighthouse-core/audits/dobetterweb/js-libraries.js | description":{
-"message":"All front-end JavaScript libraries detected on the page.",
-"description":"Description of a Lighthouse audit that tells the user what this audit is detecting. This is displayed after a user expands the section to see more. No character length limits."},
-
-"lighthouse-core/audits/dobetterweb/js-libraries.js | title":{
-"message":"Detected JavaScript libraries",
-"description":"Title of a Lighthouse audit that provides detail on the Javascript libraries that are used on the page."},
-
-"lighthouse-core/audits/dobetterweb/no-document-write.js | description":{
-"message":"For users on slow connections, external scripts dynamically injected via `document.write()` can delay page load by tens of seconds. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/document-write).",
-"description":"Description of a Lighthouse audit that tells the user why they should avoid `document.write`. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/dobetterweb/no-document-write.js | failureTitle":{
-"message":"Uses `document.write()`",
-"description":"Title of a Lighthouse audit that provides detail on the page's use of the `document.write` API. This descriptive title is shown to users when the page does use `document.write`."},
-
-"lighthouse-core/audits/dobetterweb/no-document-write.js | title":{
-"message":"Avoids `document.write()`",
-"description":"Title of a Lighthouse audit that provides detail on the page's use of the `document.write` API. This descriptive title is shown to users when the page does not use `document.write`."},
-
-"lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js | columnSeverity":{
-"message":"Highest Severity",
-"description":"Label for a column in a data table; entries will be the severity of the vulnerabilities found within a Javascript library."},
-
-"lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js | columnVersion":{
-"message":"Library Version",
-"description":"Label for a column in a data table; entries will be the version numbers of the Javascript libraries found."},
-
-"lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js | columnVuln":{
-"message":"Vulnerability Count",
-"description":"Label for a column in a data table; entries will be the counts of JavaScript-library vulnerabilities found."},
-
-"lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js | description":{
-"message":"Some third-party scripts may contain known security vulnerabilities that are easily identified and exploited by attackers. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/vulnerabilities).",
-"description":"Description of a Lighthouse audit that tells the user why they should be concerned about the third party Javascript libraries that they use. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js | displayValue":{
-"message":"{itemCount, plural,\n    =1 {1 vulnerability detected}\n    other {# vulnerabilities detected}\n    }",
-"description":"[ICU Syntax] Label for the audit identifying the number of vulnerable Javascript libraries found."},
-
-"lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js | failureTitle":{
-"message":"Includes front-end JavaScript libraries with known security vulnerabilities",
-"description":"Title of a Lighthouse audit that provides detail on Javascript libraries the page uses. This descriptive title is shown to users when some detected Javascript libraries have known security vulnerabilities."},
-
-"lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js | rowSeverityHigh":{
-"message":"High",
-"description":"Table row value for the severity of a high impact, or dangerous Javascript vulnerability.  Part of a ranking scale in the form: low, medium, high."},
-
-"lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js | rowSeverityLow":{
-"message":"Low",
-"description":"Table row value for the severity of a small, or low impact Javascript vulnerability.  Part of a ranking scale in the form: low, medium, high."},
-
-"lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js | rowSeverityMedium":{
-"message":"Medium",
-"description":"Table row value for the severity of a Javascript vulnerability.  Part of a ranking scale in the form: low, medium, high."},
-
-"lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js | title":{
-"message":"Avoids front-end JavaScript libraries with known security vulnerabilities",
-"description":"Title of a Lighthouse audit that provides detail on Javascript libraries the page uses. This descriptive title is shown to users when all Javascript libraries are free of known security vulnerabilities."},
-
-"lighthouse-core/audits/dobetterweb/notification-on-start.js | description":{
-"message":"Users are mistrustful of or confused by sites that request to send notifications without context. Consider tying the request to user gestures instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/notifications-on-load).",
-"description":"Description of a Lighthouse audit that tells the user why they should not ask for notification permission on load. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/dobetterweb/notification-on-start.js | failureTitle":{
-"message":"Requests the notification permission on page load",
-"description":"Title of a Lighthouse audit that provides detail on the page's notification permission requests. This descriptive title is shown to users when the page does ask for notification permission on load."},
-
-"lighthouse-core/audits/dobetterweb/notification-on-start.js | title":{
-"message":"Avoids requesting the notification permission on page load",
-"description":"Title of a Lighthouse audit that provides detail on the page's notification permission requests. This descriptive title is shown to users when the page does not ask for notification permission on load."},
-
-"lighthouse-core/audits/dobetterweb/password-inputs-can-be-pasted-into.js | columnFailingElem":{
-"message":"Failing Elements",
-"description":"Table column header for the HTML elements that do not allow pasting of content."},
-
-"lighthouse-core/audits/dobetterweb/password-inputs-can-be-pasted-into.js | description":{
-"message":"Preventing password pasting undermines good security policy. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/password-pasting).",
-"description":"Description of a Lighthouse audit that tells the user why they should allow pasting of content into password fields. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/dobetterweb/password-inputs-can-be-pasted-into.js | failureTitle":{
-"message":"Prevents users to paste into password fields",
-"description":"Title of a Lighthouse audit that provides detail on the ability to paste into password fields. This descriptive title is shown to users when the page does not allow pasting of content into password fields."},
-
-"lighthouse-core/audits/dobetterweb/password-inputs-can-be-pasted-into.js | title":{
-"message":"Allows users to paste into password fields",
-"description":"Title of a Lighthouse audit that provides detail on the ability to paste into password fields. This descriptive title is shown to users when the page allows pasting of content into password fields."},
-
-"lighthouse-core/audits/dobetterweb/uses-http2.js | columnProtocol":{
-"message":"Protocol",
-"description":"Label for a column in a data table; entries in the column will be the HTTP Protocol used to make a network request."},
-
-"lighthouse-core/audits/dobetterweb/uses-http2.js | description":{
-"message":"HTTP/2 offers many benefits over HTTP/1.1, including binary headers, multiplexing, and server push. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http2).",
-"description":"Description of a Lighthouse audit that tells the user why they should use HTTP/2. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/dobetterweb/uses-http2.js | displayValue":{
-"message":"{itemCount, plural,\n    =1 {1 request not served via HTTP/2}\n    other {# requests not served via HTTP/2}\n    }",
-"description":"[ICU Syntax] Label identifying the number of network requests that were not served with HTTP/2."},
-
-"lighthouse-core/audits/dobetterweb/uses-http2.js | failureTitle":{
-"message":"Does not use HTTP/2 for all of its resources",
-"description":"Title of a Lighthouse audit that provides detail on whether the webpage uses HTTP/2 for resources it requests over the network. This descriptive title is shown to users when the page does not use HTTP/2 for its requests."},
-
-"lighthouse-core/audits/dobetterweb/uses-http2.js | title":{
-"message":"Uses HTTP/2 for its own resources",
-"description":"Title of a Lighthouse audit that provides detail on whether the webpage uses HTTP/2 for resources it requests over the network. This descriptive title is shown to users when the page uses HTTP/2 for its requests."},
-
-"lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js | description":{
-"message":"Consider marking your touch and wheel event listeners as `passive` to improve your page's scroll performance. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/passive-event-listeners).",
-"description":"Description of a Lighthouse audit that tells the user why they should use passive event listeners on the page. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js | failureTitle":{
-"message":"Does not use passive listeners to improve scrolling performance",
-"description":"Title of a Lighthouse audit that provides detail on the page's use of passive event listeners used to improve the scrolling performance of the page. This descriptive title is shown to users when the page does not use passive listeners."},
-
-"lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js | title":{
-"message":"Uses passive listeners to improve scrolling performance",
-"description":"Title of a Lighthouse audit that provides detail on the page's use of passive event listeners used to improve the scrolling performance of the page. This descriptive title is shown to users when the page does use passive listeners."},
-
-"lighthouse-core/audits/errors-in-console.js | columnDesc":{
-"message":"Description",
-"description":"Label for a column in a data table; entries in the column will be the descriptions of logged browser errors."},
-
-"lighthouse-core/audits/errors-in-console.js | description":{
-"message":"Errors logged to the console indicate unresolved problems. They can come from network request failures and other browser concerns.",
-"description":"Description of a Lighthouse audit that tells the user why errors being logged to the devtools console are a cause for concern and so should be fixed. This is displayed after a user expands the section to see more. No character length limits."},
-
-"lighthouse-core/audits/errors-in-console.js | failureTitle":{
-"message":"Browser errors were logged to the console",
-"description":"Title of a Lighthouse audit that provides detail on browser errors. This descriptive title is shown to users when browser errors occurred and were logged into the devtools console."},
-
-"lighthouse-core/audits/errors-in-console.js | title":{
-"message":"No browser errors logged to the console",
-"description":"Title of a Lighthouse audit that provides detail on browser errors. This descriptive title is shown to users when no browser errors were logged into the devtools console."},
-
-"lighthouse-core/audits/font-display.js | description":{
-"message":"Leverage the font-display CSS feature to ensure text is user-visible while webfonts are loading. [Learn more](https://developers.google.com/web/updates/2016/02/font-display).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should use the font-display CSS feature. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/font-display.js | failureTitle":{
-"message":"Ensure text remains visible during webfont load",
-"description":"Title of a diagnostic audit that provides detail on the load of the page's webfonts. Often the text is invisible for seconds before the webfont resource is loaded. This imperative title is shown to users when there is a significant amount of execution time that could be reduced."},
-
-"lighthouse-core/audits/font-display.js | title":{
-"message":"All text remains visible during webfont loads",
-"description":"Title of a diagnostic audit that provides detail on if all the text on a webpage was visible while the page was loading its webfonts. This descriptive title is shown to users when the amount is acceptable and no user action is required."},
-
-"lighthouse-core/audits/font-display.js | undeclaredFontURLWarning":{
-"message":"Lighthouse was unable to automatically check the font-display value for the following URL: {fontURL}.",
-"description":"A warning message that is shown when Lighthouse couldn't automatically check some of the page's fonts and that the user will need to manually check it."},
-
-"lighthouse-core/audits/image-aspect-ratio.js | columnActual":{
-"message":"Aspect Ratio (Actual)",
-"description":"Label for a column in a data table; entries in the column will be the numeric aspect ratio of the raw (actual) image."},
-
-"lighthouse-core/audits/image-aspect-ratio.js | columnDisplayed":{
-"message":"Aspect Ratio (Displayed)",
-"description":"Label for a column in a data table; entries in the column will be the numeric aspect ratio of an image as displayed in a web page."},
-
-"lighthouse-core/audits/image-aspect-ratio.js | description":{
-"message":"Image display dimensions should match natural aspect ratio. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/aspect-ratio).",
-"description":"Description of a Lighthouse audit that tells the user why they should maintain the correct aspect ratios for all images. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/image-aspect-ratio.js | failureTitle":{
-"message":"Displays images with incorrect aspect ratio",
-"description":"Title of a Lighthouse audit that provides detail on the aspect ratios of all images on the page. This descriptive title is shown to users when not all images use correct aspect ratios."},
-
-"lighthouse-core/audits/image-aspect-ratio.js | title":{
-"message":"Displays images with correct aspect ratio",
-"description":"Title of a Lighthouse audit that provides detail on the aspect ratios of all images on the page. This descriptive title is shown to users when all images use correct aspect ratios."},
-
-"lighthouse-core/audits/image-aspect-ratio.js | warningCompute":{
-"message":"Invalid image sizing information {url}",
-"description":"Warning that the size information for an image was nonsensical. `url` will be replaced with the url of that image."},
-
-"lighthouse-core/audits/is-on-https.js | columnInsecureURL":{
-"message":"Insecure URL",
-"description":"Label for a column in a data table; entries in the column will be the URLs of insecure (non-HTTPS) network requests."},
-
-"lighthouse-core/audits/is-on-https.js | description":{
-"message":"All sites should be protected with HTTPS, even ones that don't handle sensitive data. HTTPS prevents intruders from tampering with or passively listening in on the communications between your app and your users, and is a prerequisite for HTTP/2 and many new web platform APIs. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/https).",
-"description":"Description of a Lighthouse audit that tells the user *why* HTTPS use is important. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/is-on-https.js | displayValue":{
-"message":"{itemCount, plural,\n    =1 {1 insecure request found}\n    other {# insecure requests found}\n    }",
-"description":"[ICU Syntax] Label identifying the number of insecure network requests found by an audit of a web page."},
-
-"lighthouse-core/audits/is-on-https.js | failureTitle":{
-"message":"Does not use HTTPS",
-"description":"Title of a Lighthouse audit that provides detail on the useage of HTTPS on a page. This descriptive title is shown to users when some, or all, requests on the page use HTTP instead of HTTPS."},
-
-"lighthouse-core/audits/is-on-https.js | title":{
-"message":"Uses HTTPS",
-"description":"Title of a Lighthouse audit that provides detail on the useage of HTTPS on a page. This descriptive title is shown to users when all requests on a page are fufilled using HTTPS."},
-
-"lighthouse-core/audits/load-fast-enough-for-pwa.js | description":{
-"message":"A fast page load over a cellular network ensures a good mobile user experience. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/fast-3g).",
-"description":"Description of a Lighthouse audit that tells the user *why* they need to load fast enough on mobile networks. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/load-fast-enough-for-pwa.js | displayValueText":{
-"message":"Interactive at {timeInMs, number, seconds} s",
-"description":"Label for the audit identifying the time it took for the page to become interactive."},
-
-"lighthouse-core/audits/load-fast-enough-for-pwa.js | displayValueTextWithOverride":{
-"message":"Interactive on simulated mobile network at {timeInMs, number, seconds} s",
-"description":"Label for the audit identifying the time it took for the page to become interactive on a mobile network."},
-
-"lighthouse-core/audits/load-fast-enough-for-pwa.js | failureTitle":{
-"message":"Page load is not fast enough on mobile networks",
-"description":"Imperative title of a Lighthouse audit that tells the user that their page has loaded fast enough to be considered a Progressive Web App. This imperative title is shown to users when the web page has loaded too slowly to be considered a Progressive Web App."},
-
-"lighthouse-core/audits/load-fast-enough-for-pwa.js | title":{
-"message":"Page load is fast enough on mobile networks",
-"description":"Imperative title of a Lighthouse audit that tells the user that their page has loaded fast enough to be considered a Progressive Web App. This is displayed in a list of audit titles that Lighthouse generates."},
-
-"lighthouse-core/audits/mainthread-work-breakdown.js | columnCategory":{
-"message":"Category",
-"description":"Label for the Main Thread Category column in data tables, rows will have a main thread Category and main thread Task Name."},
-
-"lighthouse-core/audits/mainthread-work-breakdown.js | description":{
-"message":"Consider reducing the time spent parsing, compiling and executing JS. You may find delivering smaller JS payloads helps with this.",
-"description":"Description of a Lighthouse audit that tells the user *why* they should reduce JS execution times. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/mainthread-work-breakdown.js | failureTitle":{
-"message":"Minimize main-thread work",
-"description":"Title of a diagnostic audit that provides detail on the main thread work the browser did to load the page. This imperative title is shown to users when there is a significant amount of execution time that could be reduced."},
-
-"lighthouse-core/audits/mainthread-work-breakdown.js | title":{
-"message":"Minimizes main-thread work",
-"description":"Title of a diagnostic audit that provides detail on the main thread work the browser did to load the page. This descriptive title is shown to users when the amount is acceptable and no user action is required."},
-
-"lighthouse-core/audits/metrics/estimated-input-latency.js | description":{
-"message":"Estimated Input Latency is an estimate of how long your app takes to respond to user input, in milliseconds, during the busiest 5s window of page load. If your latency is higher than 50 ms, users may perceive your app as laggy. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/estimated-input-latency).",
-"description":"Description of the Estimated Input Latency metric that estimates the amount of time, in milliseconds, that the app takes to respond to user input. This description is displayed within a tooltip when the user hovers on the metric name to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/metrics/estimated-input-latency.js | title":{
-"message":"Estimated Input Latency",
-"description":"The name of the metric that marks the estimated time between the page receiving input (a user clicking, tapping, or typing) and the page responding. Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit."},
-
-"lighthouse-core/audits/metrics/first-contentful-paint.js | description":{
-"message":"First Contentful Paint marks the time at which the first text or image is painted. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/first-contentful-paint).",
-"description":"Description of the First Contentful Paint (FCP) metric, which marks the time at which the first text or image is painted by the browser. This is displayed within a tooltip when the user hovers on the metric name to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/metrics/first-contentful-paint.js | title":{
-"message":"First Contentful Paint",
-"description":"The name of the metric that marks the time at which the first text or image is painted by the browser. Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit."},
-
-"lighthouse-core/audits/metrics/first-cpu-idle.js | description":{
-"message":"First CPU Idle marks the first time at which the page's main thread is quiet enough to handle input. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/first-interactive).",
-"description":"Description of the First CPU Idle metric, which marks the time at which the page has displayed content and the CPU is not busy executing the page's scripts. This is displayed within a tooltip when the user hovers on the metric name to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/metrics/first-cpu-idle.js | title":{
-"message":"First CPU Idle",
-"description":"The name of the metric that marks when the page has displayed content and the CPU is not busy executing the page's scripts. Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit."},
-
-"lighthouse-core/audits/metrics/first-meaningful-paint.js | description":{
-"message":"First Meaningful Paint measures when the primary content of a page is visible. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/first-meaningful-paint).",
-"description":"Description of the First Meaningful Paint (FMP) metric, which marks the time at which a majority of the content has been painted by the browser. This is displayed within a tooltip when the user hovers on the metric name to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/metrics/first-meaningful-paint.js | title":{
-"message":"First Meaningful Paint",
-"description":"The name of the metric that marks the time at which a majority of the content has been painted by the browser. Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit."},
-
-"lighthouse-core/audits/metrics/interactive.js | description":{
-"message":"Time to interactive is the amount of time it takes for the page to become fully interactive. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/consistently-interactive).",
-"description":"Description of the Time to Interactive (TTI) metric, which evaluates when a page has completed its primary network activity and main thread work. This is displayed within a tooltip when the user hovers on the metric name to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/metrics/interactive.js | title":{
-"message":"Time to Interactive",
-"description":"The name of the metric that marks the time at which the page is fully loaded and is able to quickly respond to user input (clicks, taps, and keypresses feel responsive). Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit."},
-
-"lighthouse-core/audits/metrics/max-potential-fid.js | description":{
-"message":"The maximum potential First Input Delay that your users could experience is the duration, in milliseconds, of the longest task. [Learn more](https://developers.google.com/web/updates/2018/05/first-input-delay).",
-"description":"Description of the Maximum Potential First Input Delay metric that marks the maximum estimated time between the page receiving input (a user clicking, tapping, or typing) and the page responding. This description is displayed within a tooltip when the user hovers on the metric name to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/metrics/max-potential-fid.js | title":{
-"message":"Max Potential First Input Delay",
-"description":"The name of the metric \"Maximum Potential First Input Delay\" that marks the maximum estimated time between the page receiving input (a user clicking, tapping, or typing) and the page responding. Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit."},
-
-"lighthouse-core/audits/metrics/speed-index.js | description":{
-"message":"Speed Index shows how quickly the contents of a page are visibly populated. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/speed-index).",
-"description":"Description of the Speed Index metric, which summarizes how quickly the page looked visually complete. This is displayed within a tooltip when the user hovers on the metric name to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/metrics/speed-index.js | title":{
-"message":"Speed Index",
-"description":"The name of the metric that summarizes how quickly the page looked visually complete. The name of this metric is largely abstract and can be loosely translated. Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit."},
-
-"lighthouse-core/audits/metrics/total-blocking-time.js | description":{
-"message":"Sum of all time periods between FCP and Time to Interactive, when task length exceeded 50ms, expressed in milliseconds.",
-"description":"Description of the Total Blocking Time (TBT) metric, which calculates the total duration of blocking time for a web page. Blocking times are time periods when the page would be blocked (prevented) from responding to user input (clicks, taps, and keypresses will feel slow to respond). This is displayed within a tooltip when the user hovers on the metric name to see more. No character length limits."},
-
-"lighthouse-core/audits/metrics/total-blocking-time.js | title":{
-"message":"Total Blocking Time",
-"description":"The name of a metric that calculates the total duration of blocking time for a web page. Blocking times are time periods when the page would be blocked (prevented) from responding to user input (clicks, taps, and keypresses will feel slow to respond). Shown to users as the label for the numeric metric value. Ideally fits within a ~40 character limit."},
-
-"lighthouse-core/audits/network-rtt.js | description":{
-"message":"Network round trip times (RTT) have a large impact on performance. If the RTT to an origin is high, it's an indication that servers closer to the user could improve performance. [Learn more](https://hpbn.co/primer-on-latency-and-bandwidth/).",
-"description":"Description of a Lighthouse audit that tells the user that a high network round trip time (RTT) can effect their website's performance because the server is physically far away from them thus making the RTT high. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/network-rtt.js | title":{
-"message":"Network Round Trip Times",
-"description":"Descriptive title of a Lighthouse audit that tells the user the round trip times to each origin the page connected to. This is displayed in a list of audit titles that Lighthouse generates."},
-
-"lighthouse-core/audits/network-server-latency.js | description":{
-"message":"Server latencies can impact web performance. If the server latency of an origin is high, it's an indication the server is overloaded or has poor backend performance. [Learn more](https://hpbn.co/primer-on-web-performance/#analyzing-the-resource-waterfall).",
-"description":"Description of a Lighthouse audit that tells the user that server latency can effect their website's performance negatively. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/network-server-latency.js | title":{
-"message":"Server Backend Latencies",
-"description":"Descriptive title of a Lighthouse audit that tells the user the server latencies observed from each origin the page connected to. This is displayed in a list of audit titles that Lighthouse generates."},
-
-"lighthouse-core/audits/performance-budget.js | columnOverBudget":{
-"message":"Over Budget",
-"description":"Label for a column in a data table; entries will be how much the quantity or size of network requests exceeded a predetermined budget."},
-
-"lighthouse-core/audits/performance-budget.js | description":{
-"message":"Keep the quantity and size of network requests under the targets set by the provided performance budget. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).",
-"description":"Description of a Lighthouse audit where a user sets budgets for the quantity and size of page resources. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/performance-budget.js | requestCountOverBudget":{
-"message":"{count, plural,\n    =1 {1 request}\n    other {# requests}\n   }",
-"description":"[ICU Syntax] Entry in a data table identifying the number of network requests of a particular type. Count will be a whole number. String should be as short as possible to be able to fit well into the table."},
-
-"lighthouse-core/audits/performance-budget.js | title":{
-"message":"Performance budget",
-"description":"Title of a Lighthouse audit that compares the size and quantity of page resources against targets set by the user. These targets are thought of as \"performance budgets\" because these metrics impact page performance (i.e. how quickly a page loads)."},
-
-"lighthouse-core/audits/redirects.js | description":{
-"message":"Redirects introduce additional delays before the page can be loaded. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/redirects).",
-"description":"Description of a Lighthouse audit that tells users why they should reduce the number of server-side redirects on their page. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/redirects.js | title":{
-"message":"Avoid multiple page redirects",
-"description":"Imperative title of a Lighthouse audit that tells the user to eliminate the redirects taken through multiple URLs to load the page. This is shown in a list of audits that Lighthouse generates."},
-
-"lighthouse-core/audits/resource-summary.js | description":{
-"message":"To set budgets for the quantity and size of page resources, add a budget.json file. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/budgets).",
-"description":"Description of a Lighthouse audit that tells the user that they can setup a budgets for the quantity and size of page resources. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/resource-summary.js | displayValue":{
-"message":"{requestCount, plural, =1 {1 request} other {# requests}} • { byteCount, number, bytes } KB",
-"description":"[ICU Syntax] Label for an audit identifying the number of requests and kilobytes used to load the page."},
-
-"lighthouse-core/audits/resource-summary.js | title":{
-"message":"Keep request counts low and transfer sizes small",
-"description":"Imperative title of a Lighthouse audit that tells the user to minimize the size and quantity of resources used to load the page."},
-
-"lighthouse-core/audits/seo/canonical.js | description":{
-"message":"Canonical links suggest which URL to show in search results. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/canonical).",
-"description":"Description of a Lighthouse audit that tells the user *why* they need to have a valid rel=canonical link. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/seo/canonical.js | explanationConflict":{
-"message":"Multiple conflicting URLs ({urlList})",
-"description":"Explanatory message stating that there was a failure in an audit caused by multiple URLs conflicting with each other. \"urlList\" will be replaced by a list of URLs (e.g. https://example.com, https://example2.com, etc )."},
-
-"lighthouse-core/audits/seo/canonical.js | explanationDifferentDomain":{
-"message":"Points to a different domain ({url})",
-"description":"Explanatory message stating that there was a failure in an audit caused by a URL pointing to a different domain. \"url\" will be replaced by the invalid URL (e.g. https://example.com)."},
-
-"lighthouse-core/audits/seo/canonical.js | explanationInvalid":{
-"message":"Invalid URL ({url})",
-"description":"Explanatory message stating that there was a failure in an audit caused by a URL being invalid. \"url\" will be replaced by the invalid URL (e.g. https://example.com)."},
-
-"lighthouse-core/audits/seo/canonical.js | explanationPointsElsewhere":{
-"message":"Points to another `hreflang` location ({url})",
-"description":"Explanatory message stating that there was a failure in an audit caused by a URL pointing to a different hreflang than the current context. \"url\" will be replaced by the invalid URL (e.g. https://example.com). 'hreflang' is an HTML attribute and should not be translated."},
-
-"lighthouse-core/audits/seo/canonical.js | explanationRelative":{
-"message":"Relative URL ({url})",
-"description":"Explanatory message stating that there was a failure in an audit caused by a URL being relative instead of absolute. \"url\" will be replaced by the invalid URL (e.g. https://example.com)."},
-
-"lighthouse-core/audits/seo/canonical.js | explanationRoot":{
-"message":"Points to the domain's root URL (the homepage), instead of an equivalent page of content",
-"description":"Explanatory message stating that the page's canonical URL was pointing to the domain's root URL, which is a common mistake. \"points\" refers to the action of the 'rel=canonical' referencing another link. \"root\" refers to the starting/home page of the website. \"domain\" refers to the registered domain name of the website."},
-
-"lighthouse-core/audits/seo/canonical.js | failureTitle":{
-"message":"Document does not have a valid `rel=canonical`",
-"description":"Title of a Lighthouse audit that provides detail on a page's rel=canonical link. This descriptive title is shown to users when the rel=canonical link is invalid and should be fixed. \"rel=canonical\" is an HTML attribute and value and so should not be translated."},
-
-"lighthouse-core/audits/seo/canonical.js | title":{
-"message":"Document has a valid `rel=canonical`",
-"description":"Title of a Lighthouse audit that provides detail on a page's rel=canonical link. This descriptive title is shown to users when the rel=canonical link is valid. \"rel=canonical\" is an HTML attribute and value and so should not be translated."},
-
-"lighthouse-core/audits/seo/font-size.js | description":{
-"message":"Font sizes less than 12px are too small to be legible and require mobile visitors to “pinch to zoom” in order to read. Strive to have >60% of page text ≥12px. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/font-sizes).",
-"description":"Description of a Lighthouse audit that tells the user *why* they need to use a larger font size. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/seo/font-size.js | displayValue":{
-"message":"{decimalProportion, number, extendedPercent} legible text",
-"description":"Label for the audit identifying font sizes that are too small."},
-
-"lighthouse-core/audits/seo/font-size.js | explanationViewport":{
-"message":"Text is illegible because there's no viewport meta tag optimized for mobile screens.",
-"description":"Explanatory message stating that there was a failure in an audit caused by a missing page viewport meta tag configuration. \"viewport\" and \"meta\" are HTML terms and should not be translated."},
-
-"lighthouse-core/audits/seo/font-size.js | explanationWithDisclaimer":{
-"message":"{decimalProportion, number, extendedPercent} of text is too small (based on {decimalProportionVisited, number, extendedPercent} sample).",
-"description":"Explanatory message stating that there was a failure in an audit caused by a certain percentage of the text on the page being too small, based on a sample size of text that was less than 100% of the text on the page. \"decimalProportion\" will be replaced by a percentage between 0 and 100%."},
-
-"lighthouse-core/audits/seo/font-size.js | failureTitle":{
-"message":"Document doesn't use legible font sizes",
-"description":"Title of a Lighthouse audit that provides detail on the font sizes used on the page. This descriptive title is shown to users when there is a font that may be too small to be read by users."},
-
-"lighthouse-core/audits/seo/font-size.js | title":{
-"message":"Document uses legible font sizes",
-"description":"Title of a Lighthouse audit that provides detail on the font sizes used on the page. This descriptive title is shown to users when the fonts used on the page are large enough to be considered legible."},
-
-"lighthouse-core/audits/seo/hreflang.js | description":{
-"message":"hreflang links tell search engines what version of a page they should list in search results for a given language or region. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/hreflang).",
-"description":"Description of a Lighthouse audit that tells the user *why* they need to have an hreflang link on their page. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. \"hreflang\" is an HTML attribute and should not be translated."},
-
-"lighthouse-core/audits/seo/hreflang.js | failureTitle":{
-"message":"Document doesn't have a valid `hreflang`",
-"description":"Title of a Lighthouse audit that provides detail on the `hreflang` attribute on a page. This descriptive title is shown when the page's `hreflang` attribute is not valid and needs to be fixed. \"hreflang\" is an HTML attribute and should not be translated."},
-
-"lighthouse-core/audits/seo/hreflang.js | title":{
-"message":"Document has a valid `hreflang`",
-"description":"Title of a Lighthouse audit that provides detail on the `hreflang` attribute on a page. This descriptive title is shown when the page's `hreflang` attribute is configured correctly. \"hreflang\" is an HTML attribute and should not be translated."},
-
-"lighthouse-core/audits/seo/http-status-code.js | description":{
-"message":"Pages with unsuccessful HTTP status codes may not be indexed properly. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/successful-http-code).",
-"description":"Description of a Lighthouse audit that tells the user *why* they need to serve pages with a valid HTTP status code. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/seo/http-status-code.js | failureTitle":{
-"message":"Page has unsuccessful HTTP status code",
-"description":"Descriptive title of a Lighthouse audit that provides detail on the HTTP status code a page responds with. This descriptive title is shown when the page responds to requests with an HTTP status code that indicates the request was unsuccessful."},
-
-"lighthouse-core/audits/seo/http-status-code.js | title":{
-"message":"Page has successful HTTP status code",
-"description":"Title of a Lighthouse audit that provides detail on the HTTP status code a page responds with. This descriptive title is shown when the page has responded with a valid HTTP status code."},
-
-"lighthouse-core/audits/seo/is-crawlable.js | description":{
-"message":"Search engines are unable to include your pages in search results if they don't have permission to crawl them. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/indexing).",
-"description":"Description of a Lighthouse audit that tells the user *why* allowing search-engine crawling of their page is beneficial. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/seo/is-crawlable.js | failureTitle":{
-"message":"Page is blocked from indexing",
-"description":"Title of a Lighthouse audit that provides detail on if search-engine crawlers are blocked from indexing the page. This title is shown when the page has been configured to block indexing and therefore cannot be indexed by search engines."},
-
-"lighthouse-core/audits/seo/is-crawlable.js | title":{
-"message":"Page isn’t blocked from indexing",
-"description":"Title of a Lighthouse audit that provides detail on if search-engine crawlers are blocked from indexing the page. This title is shown when the page is not blocked from indexing and can be crawled."},
-
-"lighthouse-core/audits/seo/link-text.js | description":{
-"message":"Descriptive link text helps search engines understand your content. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/descriptive-link-text).",
-"description":"Description of a Lighthouse audit that tells the user *why* they need to have descriptive text on the links in their page. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/seo/link-text.js | displayValue":{
-"message":"{itemCount, plural,\n    =1 {1 link found}\n    other {# links found}\n    }",
-"description":"[ICU Syntax] Label for the audit identifying the number of links found. \"link\" here refers to the links in a web page to other web pages."},
-
-"lighthouse-core/audits/seo/link-text.js | failureTitle":{
-"message":"Links do not have descriptive text",
-"description":"Title of a Lighthouse audit that tests if each link on a page contains a sufficient description of what a user will find when they click it. Generic, non-descriptive text like \"click here\" doesn't give an indication of what the link leads to. This descriptive title is shown when one or more links on the page contain generic, non-descriptive text."},
-
-"lighthouse-core/audits/seo/link-text.js | title":{
-"message":"Links have descriptive text",
-"description":"Title of a Lighthouse audit that tests if each link on a page contains a sufficient description of what a user will find when they click it. Generic, non-descriptive text like \"click here\" doesn't give an indication of what the link leads to. This descriptive title is shown when all links on the page have sufficient textual descriptions."},
-
-"lighthouse-core/audits/seo/manual/structured-data.js | description":{
-"message":"Run the [Structured Data Testing Tool](https://search.google.com/structured-data/testing-tool/) and the [Structured Data Linter](http://linter.structured-data.org/) to validate structured data. [Learn more](https://developers.google.com/search/docs/guides/mark-up-content).",
-"description":"Description of a Lighthouse audit that provides detail on the structured data in a page. \"Structured data\" is a standardized data format on a page that helps a search engine categorize and understand its contents. This description is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/seo/manual/structured-data.js | title":{
-"message":"Structured data is valid",
-"description":"Title of a Lighthouse audit that prompts users to manually check their page for valid structured data. \"Structured data\" is a standardized data format on a page that helps a search engine categorize and understand its contents."},
-
-"lighthouse-core/audits/seo/meta-description.js | description":{
-"message":"Meta descriptions may be included in search results to concisely summarize page content. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/description).",
-"description":"Description of a Lighthouse audit that tells the user *why* they need to have meta descriptions on their page. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/seo/meta-description.js | explanation":{
-"message":"Description text is empty.",
-"description":"Explanatory message stating that there was a failure in an audit caused by the page's meta description text being empty."},
-
-"lighthouse-core/audits/seo/meta-description.js | failureTitle":{
-"message":"Document does not have a meta description",
-"description":"Title of a Lighthouse audit that provides detail on the web page's document meta description. This descriptive title is shown when the document does not have a meta description. \"meta\" should be left untranslated because it refers to an HTML element."},
-
-"lighthouse-core/audits/seo/meta-description.js | title":{
-"message":"Document has a meta description",
-"description":"Title of a Lighthouse audit that provides detail on the web page's document meta description. This descriptive title is shown when the document has a meta description. \"meta\" should be left untranslated because it refers to an HTML element."},
-
-"lighthouse-core/audits/seo/plugins.js | description":{
-"message":"Search engines can't index plugin content, and many devices restrict plugins or don't support them. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/plugins).",
-"description":"Description of a Lighthouse audit that tells the user *why* they need to avoid using browser plugins in their content. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/seo/plugins.js | failureTitle":{
-"message":"Document uses plugins",
-"description":"Descriptive title of a Lighthouse audit that provides detail on the browser plugins used by the page. This title is shown when there is plugin content on the page."},
-
-"lighthouse-core/audits/seo/plugins.js | title":{
-"message":"Document avoids plugins",
-"description":"Title of a Lighthouse audit that provides detail on the browser plugins used by the page. This descriptive title is shown when there is no plugin content on the page that would restrict search indexing."},
-
-"lighthouse-core/audits/seo/robots-txt.js | description":{
-"message":"If your robots.txt file is malformed, crawlers may not be able to understand how you want your website to be crawled or indexed.",
-"description":"Description of a Lighthouse audit that tells the user *why* they need to have a valid robots.txt file. Note: \"robots.txt\" is a canonical filename and should not be translated. This is displayed after a user expands the section to see more. No character length limits."},
-
-"lighthouse-core/audits/seo/robots-txt.js | displayValueHttpBadCode":{
-"message":"request for robots.txt returned HTTP status: {statusCode}",
-"description":"Label for the audit identifying that the robots.txt request has returned a specific HTTP status code. Note: \"robots.txt\" is a canonical filename and should not be translated. \"statusCode\" will be replaced with a 3 digit integer which represents the status of the HTTP connectiong for this page."},
-
-"lighthouse-core/audits/seo/robots-txt.js | displayValueValidationError":{
-"message":"{itemCount, plural,\n    =1 {1 error found}\n    other {# errors found}\n    }",
-"description":"[ICU Syntax] Label for the audit identifying the number of errors that occured while validating the robots.txt file. \"itemCount\" will be replaced by the integer count of errors encountered."},
-
-"lighthouse-core/audits/seo/robots-txt.js | explanation":{
-"message":"Lighthouse was unable to download a robots.txt file",
-"description":"Explanatory message stating that there was a failure in an audit caused by Lighthouse not being able to download the robots.txt file for the site.  Note: \"robots.txt\" is a canonical filename and should not be translated."},
-
-"lighthouse-core/audits/seo/robots-txt.js | failureTitle":{
-"message":"robots.txt is not valid",
-"description":"Title of a Lighthouse audit that provides detail on the site's robots.txt file. Note: \"robots.txt\" is a canonical filename and should not be translated. This descriptive title is shown when the robots.txt file is misconfigured, which makes the page hard or impossible to scan via web crawler."},
-
-"lighthouse-core/audits/seo/robots-txt.js | title":{
-"message":"robots.txt is valid",
-"description":"Title of a Lighthouse audit that provides detail on the site's robots.txt file. Note: \"robots.txt\" is a canonical filename and should not be translated. This descriptive title is shown when the robots.txt file is present and configured correctly."},
-
-"lighthouse-core/audits/seo/tap-targets.js | description":{
-"message":"Interactive elements like buttons and links should be large enough (48x48px), and have enough space around them, to be easy enough to tap without overlapping onto other elements. [Learn more](https://developers.google.com/web/fundamentals/accessibility/accessible-styles#multi-device_responsive_design).",
-"description":"Description of a Lighthouse audit that tells the user why buttons and links need to be big enough and what 'big enough' means. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/seo/tap-targets.js | displayValue":{
-"message":"{decimalProportion, number, percent} appropriately sized tap targets",
-"description":"Explanatory message stating that a certain percentage of the tap targets (like buttons and links) on the page are of an appropriately large size."},
-
-"lighthouse-core/audits/seo/tap-targets.js | explanationViewportMetaNotOptimized":{
-"message":"Tap targets are too small because there's no viewport meta tag optimized for mobile screens",
-"description":"Explanatory message stating that there was a failure in an audit caused by the viewport meta tag not being optimized for mobile screens, which caused tap targets like buttons and links to be too small to tap on."},
-
-"lighthouse-core/audits/seo/tap-targets.js | failureTitle":{
-"message":"Tap targets are not sized appropriately",
-"description":"Descriptive title of a Lighthouse audit that provides detail on whether tap targets (like buttons and links) on a page are big enough so they can easily be tapped on a mobile device. This descriptive title is shown when tap targets are not easy to tap on."},
-
-"lighthouse-core/audits/seo/tap-targets.js | overlappingTargetHeader":{
-"message":"Overlapping Target",
-"description":"Label of a table column that identifies a tap target (like a link or button) that overlaps with another tap target."},
-
-"lighthouse-core/audits/seo/tap-targets.js | sizeHeader":{
-"message":"Size",
-"description":"Label of a table column that specifies the size of tap targets like buttons and links."},
-
-"lighthouse-core/audits/seo/tap-targets.js | tapTargetHeader":{
-"message":"Tap Target",
-"description":"Label of a table column that identifies tap targets (like buttons and links) that have failed the audit and aren't easy to tap on."},
-
-"lighthouse-core/audits/seo/tap-targets.js | title":{
-"message":"Tap targets are sized appropriately",
-"description":"Title of a Lighthouse audit that provides detail on whether tap targets (like buttons and links) on a page are big enough so they can easily be tapped on a mobile device. This descriptive title is shown when tap targets are easy to tap on."},
-
-"lighthouse-core/audits/third-party-summary.js | columnMainThreadTime":{
-"message":"Main Thread Time",
-"description":"Label for a table column that displays how much time each row spent executing on the main thread, entries will be the number of milliseconds spent."},
-
-"lighthouse-core/audits/third-party-summary.js | columnThirdParty":{
-"message":"Third-Party",
-"description":"Label for a table column that displays the name of a third-party provider that potentially links to their website."},
-
-"lighthouse-core/audits/third-party-summary.js | description":{
-"message":"Third-party code can significantly impact load performance. Limit the number of redundant third-party providers and try to load third-party code after your page has primarily finished loading. [Learn more](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/loading-third-party-javascript/).",
-"description":"Description of a Lighthouse audit that identifies the code on the page that the user doesn't control. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/third-party-summary.js | displayValue":{
-"message":"{itemCount, plural,\n    =1 {1 Third-Party Found}\n    other {# Third-Parties Found}\n  }",
-"description":"Summary text for the result of a Lighthouse audit that identifies the code on the page that the user doesn't control. This text summarizes the number of distinct entities that were found on the page."},
-
-"lighthouse-core/audits/third-party-summary.js | title":{
-"message":"Third-Party Usage",
-"description":"Title of a Lighthouse audit that identifies the code on the page that the user doesn't control. This is shown in a list of audits that Lighthouse generates."},
-
-"lighthouse-core/audits/time-to-first-byte.js | description":{
-"message":"Time To First Byte identifies the time at which your server sends a response. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/ttfb).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should reduce the amount of time it takes their server to start responding to requests. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/time-to-first-byte.js | displayValue":{
-"message":"Root document took {timeInMs, number, milliseconds} ms",
-"description":"Used to summarize the total Time to First Byte duration for the primary HTML response. The `{timeInMs}` placeholder will be replaced with the time duration, shown in milliseconds (e.g. 210 ms)"},
-
-"lighthouse-core/audits/time-to-first-byte.js | failureTitle":{
-"message":"Reduce server response times (TTFB)",
-"description":"Title of a diagnostic audit that provides detail on how long it took from starting a request to when the server started responding. This imperative title is shown to users when there is a significant amount of execution time that could be reduced."},
-
-"lighthouse-core/audits/time-to-first-byte.js | title":{
-"message":"Server response times are low (TTFB)",
-"description":"Title of a diagnostic audit that provides detail on how long it took from starting a request to when the server started responding. This descriptive title is shown to users when the amount is acceptable and no user action is required."},
-
-"lighthouse-core/audits/user-timings.js | columnDuration":{
-"message":"Duration",
-"description":"Label for the Duration column in the User Timing event data table. User Timing API entries are added by the developer of the web page. Durations are only provided for 'Measure' entries. Durations are the number of total number milliseconds from Start Time to their ending point. e.g. '2,020.64 ms'"},
-
-"lighthouse-core/audits/user-timings.js | columnName":{
-"message":"Name",
-"description":"Label for the Name column in the User Timing event data table. User Timing API entries are added by the developer of the web page. An example user timing event name: 'pageload_logoimage_done'"},
-
-"lighthouse-core/audits/user-timings.js | columnStartTime":{
-"message":"Start Time",
-"description":"Label for the Start Time column in the User Timing event data table. User Timing API entries are added by the developer of the web page. Start Times are the number of milliseconds since the page started loading, e.g. '380.26 ms'"},
-
-"lighthouse-core/audits/user-timings.js | columnType":{
-"message":"Type",
-"description":"Label for the Type column in the User Timing event data table. User Timing API entries are added by the developer of the web page. The only possible types are 'Mark' and Measure'."},
-
-"lighthouse-core/audits/user-timings.js | description":{
-"message":"Consider instrumenting your app with the User Timing API to measure your app's real-world performance during key user experiences. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/user-timing).",
-"description":"Description of a Lighthouse audit that tells the user they may want to use the User Timing API to help measure the performance of aspects of their page load and interaction. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/user-timings.js | displayValue":{
-"message":"{itemCount, plural,\n    =1 {1 user timing}\n    other {# user timings}\n    }",
-"description":"[ICU Syntax] Label for an audit identifying the number of User Timing timestamps present in the page."},
-
-"lighthouse-core/audits/user-timings.js | title":{
-"message":"User Timing marks and measures",
-"description":"Descriptive title of a diagnostic audit that provides details on any timestamps generated by the page. User Timing refers to the 'User Timing API', which enables a website to record specific times as 'marks', or spans of time as 'measures'."},
-
-"lighthouse-core/audits/uses-rel-preconnect.js | crossoriginWarning":{
-"message":"A preconnect <link> was found for \"{securityOrigin}\" but was not used by the browser. Check that you are using the `crossorigin` attribute properly.",
-"description":"A warning message that is shown when the user tried to follow the advice of the audit, but it's not working as expected. Forgetting to set the `crossorigin` HTML attribute, or setting it to an incorrect value, on the link is a common mistake when adding preconnect links."},
-
-"lighthouse-core/audits/uses-rel-preconnect.js | description":{
-"message":"Consider adding preconnect or dns-prefetch resource hints to establish early connections to important third-party origins. [Learn more](https://developers.google.com/web/fundamentals/performance/resource-prioritization#preconnect).",
-"description":"Description of a Lighthouse audit that tells the user how to connect early to third-party domains that will be used to load page resources. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/uses-rel-preconnect.js | title":{
-"message":"Preconnect to required origins",
-"description":"Imperative title of a Lighthouse audit that tells the user to connect early to internet domains that will be used to load page resources. Origin is the correct term, however 'domain name' could be used if neccsesary. This is displayed in a list of audit titles that Lighthouse generates."},
-
-"lighthouse-core/audits/uses-rel-preload.js | crossoriginWarning":{
-"message":"A preload <link> was found for \"{preloadURL}\" but was not used by the browser. Check that you are using the `crossorigin` attribute properly.",
-"description":"A warning message that is shown when the user tried to follow the advice of the audit, but it's not working as expected. Forgetting to set the `crossorigin` HTML attribute, or setting it to an incorrect value, on the link is a common mistake when adding preload links."},
-
-"lighthouse-core/audits/uses-rel-preload.js | description":{
-"message":"Consider using <link rel=preload> to prioritize fetching resources that are currently requested later in page load. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/preload).",
-"description":"Description of a Lighthouse audit that tells the user *why* they should preload important network requests. The associated network requests are started halfway through pageload (or later) but should be started at the beginning. This is displayed after a user expands the section to see more. No character length limits. '<link rel=preload>' is the html code the user would include in their page and shouldn't be translated. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/audits/uses-rel-preload.js | title":{
-"message":"Preload key requests",
-"description":"Imperative title of a Lighthouse audit that tells the user to use <link rel=preload> to initiate important network requests earlier during page load. This is displayed in a list of audit titles that Lighthouse generates."},
-
-"lighthouse-core/config/default-config.js | a11yAriaGroupDescription":{
-"message":"These are opportunities to improve the usage of ARIA in your application which may enhance the experience for users of assistive technology, like a screen reader.",
-"description":"Description of the ARIA validity section within the Accessibility category. Within this section are audits with descriptive titles that highlight if whether all the aria- HTML attributes have been used properly."},
-
-"lighthouse-core/config/default-config.js | a11yAriaGroupTitle":{
-"message":"ARIA",
-"description":"Title of the ARIA validity section within the Accessibility category. Within this section are audits with descriptive titles that highlight if whether all the aria- HTML attributes have been used properly."},
-
-"lighthouse-core/config/default-config.js | a11yAudioVideoGroupDescription":{
-"message":"These are opportunities to provide alternative content for audio and video. This may improve the experience for users with hearing or vision impairments.",
-"description":"Description of the navigation section within the Accessibility category. Within this section are audits with descriptive titles that highlight opportunities to provide alternative content for audio and video."},
-
-"lighthouse-core/config/default-config.js | a11yAudioVideoGroupTitle":{
-"message":"Audio and video",
-"description":"Title of the navigation section within the Accessibility category. Within this section are audits with descriptive titles that highlight opportunities to provide alternative content for audio and video."},
-
-"lighthouse-core/config/default-config.js | a11yBestPracticesGroupDescription":{
-"message":"These items highlight common accessibility best practices.",
-"description":"Description of the best practices section within the Accessibility category. Within this section are audits with descriptive titles that highlight common accessibility best practices."},
-
-"lighthouse-core/config/default-config.js | a11yBestPracticesGroupTitle":{
-"message":"Best practices",
-"description":"Title of the best practices section of the Accessibility category. Within this section are audits with descriptive titles that highlight common accessibility best practices."},
-
-"lighthouse-core/config/default-config.js | a11yCategoryDescription":{
-"message":"These checks highlight opportunities to [improve the accessibility of your web app](https://developers.google.com/web/fundamentals/accessibility). Only a subset of accessibility issues can be automatically detected so manual testing is also encouraged.",
-"description":"Description of the Accessibility category. This is displayed at the top of a list of audits focused on making web content accessible to all users. No character length limits. 'improve the accessibility of your web app' becomes link text to additional documentation."},
-
-"lighthouse-core/config/default-config.js | a11yCategoryManualDescription":{
-"message":"These items address areas which an automated testing tool cannot cover. Learn more in our guide on [conducting an accessibility review](https://developers.google.com/web/fundamentals/accessibility/how-to-review).",
-"description":"Description of the Accessibility manual checks category. This description is displayed above a list of accessibility audits that currently have no automated test and so must be verified manually by the user. No character length limits. 'conducting an accessibility review' becomes link text to additional documentation."},
-
-"lighthouse-core/config/default-config.js | a11yCategoryTitle":{
-"message":"Accessibility",
-"description":"Title of the Accessibility category of audits. This section contains audits focused on making web content accessible to all users. Also used as a label of a score gauge; try to limit to 20 characters."},
-
-"lighthouse-core/config/default-config.js | a11yColorContrastGroupDescription":{
-"message":"These are opportunities to improve the legibility of your content.",
-"description":"Description of the color contrast section within the Accessibility category. Within this section are audits with descriptive titles that highlight the color and vision aspects of the page's accessibility that are passing or failing."},
-
-"lighthouse-core/config/default-config.js | a11yColorContrastGroupTitle":{
-"message":"Contrast",
-"description":"Title of the color contrast section within the Accessibility category. Within this section are audits with descriptive titles that highlight the color and vision aspects of the page's accessibility that are passing or failing."},
-
-"lighthouse-core/config/default-config.js | a11yLanguageGroupDescription":{
-"message":"These are opportunities to improve the interpretation of your content by users in different locales.",
-"description":"Description of the language section within the Accessibility category. Within this section are audits with descriptive titles that highlight if the language has been annotated in the correct HTML attributes on the page."},
-
-"lighthouse-core/config/default-config.js | a11yLanguageGroupTitle":{
-"message":"Internationalization and localization",
-"description":"Title of the language section within the Accessibility category. Within this section are audits with descriptive titles that highlight if the language has been annotated in the correct HTML attributes on the page."},
-
-"lighthouse-core/config/default-config.js | a11yNamesLabelsGroupDescription":{
-"message":"These are opportunities to improve the semantics of the controls in your application. This may enhance the experience for users of assistive technology, like a screen reader.",
-"description":"Description of the HTML element naming section within the Accessibility category. Within this section are audits with descriptive titles that highlight if the non-textual HTML elements on the page have names discernible by a screen reader."},
-
-"lighthouse-core/config/default-config.js | a11yNamesLabelsGroupTitle":{
-"message":"Names and labels",
-"description":"Title of the HTML element naming section within the Accessibility category. Within this section are audits with descriptive titles that highlight if the non-textual HTML elements on the page have names discernible by a screen reader."},
-
-"lighthouse-core/config/default-config.js | a11yNavigationGroupDescription":{
-"message":"These are opportunities to improve keyboard navigation in your application.",
-"description":"Description of the navigation section within the Accessibility category. Within this section are audits with descriptive titles that highlight opportunities to improve keyboard navigation."},
-
-"lighthouse-core/config/default-config.js | a11yNavigationGroupTitle":{
-"message":"Navigation",
-"description":"Title of the navigation section within the Accessibility category. Within this section are audits with descriptive titles that highlight opportunities to improve keyboard navigation."},
-
-"lighthouse-core/config/default-config.js | a11yTablesListsVideoGroupDescription":{
-"message":"These are opportunities to to improve the experience of reading tabular or list data using assistive technology, like a screen reader.",
-"description":"Description of the navigation section within the Accessibility category. Within this section are audits with descriptive titles that highlight opportunities to improve the experience of reading tabular or list data using assistive technology."},
-
-"lighthouse-core/config/default-config.js | a11yTablesListsVideoGroupTitle":{
-"message":"Tables and lists",
-"description":"Title of the navigation section within the Accessibility category. Within this section are audits with descriptive titles that highlight opportunities to improve the experience of reading tabular or list data using assistive technology."},
-
-"lighthouse-core/config/default-config.js | bestPracticesCategoryTitle":{
-"message":"Best Practices",
-"description":"Title of the Best Practices category of audits. This is displayed at the top of a list of audits focused on topics related to following web development best practices and accepted guidelines. Also used as a label of a score gauge; try to limit to 20 characters."},
-
-"lighthouse-core/config/default-config.js | budgetsGroupDescription":{
-"message":"Performance budgets set standards for the performance of your site.",
-"description":"Description of the Budgets section of the Performance category. Within this section the budget results are displayed."},
-
-"lighthouse-core/config/default-config.js | budgetsGroupTitle":{
-"message":"Budgets",
-"description":"Title of the Budgets section of the Performance Category. 'Budgets' refers to a budget (like a financial budget), but applied to the amount of resources on a page, rather than money."},
-
-"lighthouse-core/config/default-config.js | diagnosticsGroupDescription":{
-"message":"More information about the performance of your application. These numbers don't [directly affect](https://github.com/GoogleChrome/lighthouse/blob/d2ec9ffbb21de9ad1a0f86ed24575eda32c796f0/docs/scoring.md#how-are-the-scores-weighted) the Performance score.",
-"description":"Description of the diagnostics section of the Performance category. Within this section are audits with non-imperative titles that provide more detail on a web page's load performance characteristics. Within this section, the user may read the details and deduce additional actions they could take to improve performance."},
-
-"lighthouse-core/config/default-config.js | diagnosticsGroupTitle":{
-"message":"Diagnostics",
-"description":"Title of the diagnostics section of the Performance category. Within this section are audits with non-imperative titles that provide more detail on the page's page load performance characteristics. Whereas the 'Opportunities' suggest an action along with expected time savings, diagnostics do not. Within this section, the user may read the details and deduce additional actions they could take."},
-
-"lighthouse-core/config/default-config.js | firstPaintImprovementsGroupDescription":{
-"message":"The most critical aspect of performance is how quickly pixels are rendered onscreen. Key metrics: First Contentful Paint, First Meaningful Paint",
-"description":"Description of an opportunity sub-section of the Performance category. Within this section are audits with imperative titles that suggest actions the user can take to improve the time of the first initial render of the webpage."},
-
-"lighthouse-core/config/default-config.js | firstPaintImprovementsGroupTitle":{
-"message":"First Paint Improvements",
-"description":"Title of an opportunity sub-section of the Performance category. Within this section are audits with imperative titles that suggest actions the user can take to improve the time of the first initial render of the webpage."},
-
-"lighthouse-core/config/default-config.js | loadOpportunitiesGroupDescription":{
-"message":"These suggestions can help your page load faster. They don't [directly affect](https://github.com/GoogleChrome/lighthouse/blob/d2ec9ffbb21de9ad1a0f86ed24575eda32c796f0/docs/scoring.md#how-are-the-scores-weighted) the Performance score.",
-"description":"Description of the opportunity section of the Performance category. 'Suggestions' could also be 'recommendations'. Within this section are audits with imperative titles that suggest actions the user can take to improve the loading performance of their web page."},
-
-"lighthouse-core/config/default-config.js | loadOpportunitiesGroupTitle":{
-"message":"Opportunities",
-"description":"Title of the opportunity section of the Performance category. Within this section are audits with imperative titles that suggest actions the user can take to improve the loading performance of their web page. 'Suggestion'/'Optimization'/'Recommendation' are reasonable synonyms for 'opportunity' in this case."},
-
-"lighthouse-core/config/default-config.js | metricGroupTitle":{
-"message":"Metrics",
-"description":"Title of the speed metrics section of the Performance category. Within this section are various speed metrics which quantify the pageload performance into values presented in seconds and milliseconds."},
-
-"lighthouse-core/config/default-config.js | overallImprovementsGroupDescription":{
-"message":"Enhance the overall loading experience, so the page is responsive and ready to use as soon as possible. Key metrics: Time to Interactive, Speed Index",
-"description":"Description of an opportunity sub-section of the Performance category. Within this section are audits with imperative titles that suggest actions the user can take to improve the overall loading performance of their web page."},
-
-"lighthouse-core/config/default-config.js | overallImprovementsGroupTitle":{
-"message":"Overall Improvements",
-"description":"Title of an opportunity sub-section of the Performance category. Within this section are audits with imperative titles that suggest actions the user can take to improve the overall loading performance of their web page."},
-
-"lighthouse-core/config/default-config.js | performanceCategoryTitle":{
-"message":"Performance",
-"description":"Title of the Performance category of audits. Equivalent to 'Web performance', this term is inclusive of all web page speed and loading optimization topics. Also used as a label of a score gauge; try to limit to 20 characters."},
-
-"lighthouse-core/config/default-config.js | pwaFastReliableGroupTitle":{
-"message":"Fast and reliable",
-"description":"Title of the Fast and Reliable section of the web app category. Within this section are audits that check if the web site loaded quickly and can reliably load even if the internet connection is very slow or goes offline."},
-
-"lighthouse-core/config/default-config.js | pwaInstallableGroupTitle":{
-"message":"Installable",
-"description":"Title of the Installable section of the web app category. Within this section are audits that check if Chrome supports installing the web site as an app on their device."},
-
-"lighthouse-core/config/default-config.js | pwaOptimizedGroupTitle":{
-"message":"PWA Optimized",
-"description":"Title of the \"PWA Optimized\" section of the web app category. Within this section are audits that check if the developer has taken advantage of features to make their web page more enjoyable and engaging for the user."},
-
-"lighthouse-core/config/default-config.js | seoCategoryDescription":{
-"message":"These checks ensure that your page is optimized for search engine results ranking. There are additional factors Lighthouse does not check that may affect your search ranking. [Learn more](https://support.google.com/webmasters/answer/35769).",
-"description":"Description of the Search Engine Optimization (SEO) category. This is displayed at the top of a list of audits focused on optimizing a website for indexing by search engines. No character length limits. 'Learn More' becomes link text to additional documentation."},
-
-"lighthouse-core/config/default-config.js | seoCategoryManualDescription":{
-"message":"Run these additional validators on your site to check additional SEO best practices.",
-"description":"Description of the Search Engine Optimization (SEO) manual checks category, the additional validators must be run by hand in order to check all SEO best practices. This is displayed at the top of a list of manually run audits focused on optimizing a website for indexing by search engines. No character length limits."},
-
-"lighthouse-core/config/default-config.js | seoCategoryTitle":{
-"message":"SEO",
-"description":"Title of the Search Engine Optimization (SEO) category of audits. This is displayed at the top of a list of audits focused on topics related to optimizing a website for indexing by search engines. Also used as a label of a score gauge; try to limit to 20 characters."},
-
-"lighthouse-core/config/default-config.js | seoContentGroupDescription":{
-"message":"Format your HTML in a way that enables crawlers to better understand your app’s content.",
-"description":"Description of the navigation section within the Search Engine Optimization (SEO) category. Within this section are audits with descriptive titles that highlight ways to make a website content more easily understood by search engine crawler bots."},
-
-"lighthouse-core/config/default-config.js | seoContentGroupTitle":{
-"message":"Content Best Practices",
-"description":"Title of the navigation section within the Search Engine Optimization (SEO) category. Within this section are audits with descriptive titles that highlight ways to make a website content more easily understood by search engine crawler bots."},
-
-"lighthouse-core/config/default-config.js | seoCrawlingGroupDescription":{
-"message":"To appear in search results, crawlers need access to your app.",
-"description":"Description of the navigation section within the Search Engine Optimization (SEO) category. Within this section are audits with descriptive titles that highlight ways to make a website accessible to search engine crawlers."},
-
-"lighthouse-core/config/default-config.js | seoCrawlingGroupTitle":{
-"message":"Crawling and Indexing",
-"description":"Title of the navigation section within the Search Engine Optimization (SEO) category. Within this section are audits with descriptive titles that highlight ways to make a website accessible to search engine crawlers."},
-
-"lighthouse-core/config/default-config.js | seoMobileGroupDescription":{
-"message":"Make sure your pages are mobile friendly so users don’t have to pinch or zoom in order to read the content pages. [Learn more](https://developers.google.com/search/mobile-sites/).",
-"description":"Description of the navigation section within the Search Engine Optimization (SEO) category. Within this section are audits with descriptive titles that highlight opportunities to make a page more usable on mobile devices."},
-
-"lighthouse-core/config/default-config.js | seoMobileGroupTitle":{
-"message":"Mobile Friendly",
-"description":"Title of the navigation section within the Search Engine Optimization (SEO) category. Within this section are audits with descriptive titles that highlight opportunities to make a page more usable on mobile devices."},
-
-"lighthouse-core/lib/i18n/i18n.js | columnCacheTTL":{
-"message":"Cache TTL",
-"description":"Label for a column in a data table; entries will be the time to live value of the cache header on a web resource."},
-
-"lighthouse-core/lib/i18n/i18n.js | columnLocation":{
-"message":"Location",
-"description":"Label for a column in a data table; entries will be the location of a specific line of code in a file, in the format \"line: 102\"."},
-
-"lighthouse-core/lib/i18n/i18n.js | columnRequests":{
-"message":"Requests",
-"description":"Label for a column in a data table; entries will be the number of network requests done by a webpage."},
-
-"lighthouse-core/lib/i18n/i18n.js | columnResourceType":{
-"message":"Resource Type",
-"description":"Label for a column in a data table; entries will be types of resources loaded over the network, e.g. \"Scripts\", \"Third-Party\", \"Stylesheet\"."},
-
-"lighthouse-core/lib/i18n/i18n.js | columnSize":{
-"message":"Size",
-"description":"Label for a column in a data table; entries will be the size of a web resource in kilobytes."},
-
-"lighthouse-core/lib/i18n/i18n.js | columnTimeSpent":{
-"message":"Time Spent",
-"description":"Label for a column in a data table; entries will be the number of milliseconds spent during a particular activity."},
-
-"lighthouse-core/lib/i18n/i18n.js | columnTransferSize":{
-"message":"Transfer Size",
-"description":"Label for a column in a data table; entries will be the number of kilobytes transferred to load a set of files."},
-
-"lighthouse-core/lib/i18n/i18n.js | columnURL":{
-"message":"URL",
-"description":"Label for a column in a data table; entries will be the URL of a web resource"},
-
-"lighthouse-core/lib/i18n/i18n.js | columnWastedBytes":{
-"message":"Potential Savings",
-"description":"Label for a column in a data table; entries will be the number of kilobytes the user could reduce their page by if they implemented the suggestions."},
-
-"lighthouse-core/lib/i18n/i18n.js | columnWastedMs":{
-"message":"Potential Savings",
-"description":"Label for a column in a data table; entries will be the number of milliseconds the user could reduce page load by if they implemented the suggestions."},
-
-"lighthouse-core/lib/i18n/i18n.js | displayValueByteSavings":{
-"message":"Potential savings of {wastedBytes, number, bytes} KB",
-"description":"Label shown per-audit to show how many bytes smaller the page could be if the user implemented the suggestions. The `{wastedBytes}` placeholder will be replaced with the number of bytes, shown in kilobytes (e.g. 148 KB)"},
-
-"lighthouse-core/lib/i18n/i18n.js | displayValueMsSavings":{
-"message":"Potential savings of {wastedMs, number, milliseconds} ms",
-"description":"Label shown per-audit to show how many milliseconds faster the page load could be if the user implemented the suggestions. The `{wastedMs}` placeholder will be replaced with the time duration, shown in milliseconds (e.g. 140 ms)"},
-
-"lighthouse-core/lib/i18n/i18n.js | documentResourceType":{
-"message":"Document",
-"description":"Label for a row in a data table; entries will be the total number and byte size of all 'Document' resources loaded by a web page."},
-
-"lighthouse-core/lib/i18n/i18n.js | fontResourceType":{
-"message":"Font",
-"description":"Label for a row in a data table; entries will be the total number and byte size of all 'Font' resources loaded by a web page."},
-
-"lighthouse-core/lib/i18n/i18n.js | imageResourceType":{
-"message":"Image",
-"description":"Label for a row in a data table; entries will be the total number and byte size of all 'Image' resources loaded by a web page."},
-
-"lighthouse-core/lib/i18n/i18n.js | mediaResourceType":{
-"message":"Media",
-"description":"Label for a row in a data table; entries will be the total number and byte size of all 'Media' resources loaded by a web page. 'Media' refers to audio and video files."},
-
-"lighthouse-core/lib/i18n/i18n.js | ms":{
-"message":"{timeInMs, number, milliseconds} ms",
-"description":"Used to show the duration in milliseconds that something lasted. The `{timeInMs}` placeholder will be replaced with the time duration, shown in milliseconds (e.g. 63 ms)"},
-
-"lighthouse-core/lib/i18n/i18n.js | otherResourceType":{
-"message":"Other",
-"description":"Label for a row in a data table; entries will be the total number and byte size of all resources loaded by a web page that don't fit into the categories of Document, Script, Stylesheet, Image, Media, & Font."},
-
-"lighthouse-core/lib/i18n/i18n.js | scriptResourceType":{
-"message":"Script",
-"description":"Label for a row in a data table; entries will be the total number and byte size of all 'Script' resources loaded by a web page. 'Script' refers to JavaScript or other files that are executable by a browser."},
-
-"lighthouse-core/lib/i18n/i18n.js | seconds":{
-"message":"{timeInMs, number, seconds} s",
-"description":"Used to show the duration in seconds that something lasted. The {timeInMs} placeholder will be replaced with the time duration, shown in seconds (e.g. 5.2 s)"},
-
-"lighthouse-core/lib/i18n/i18n.js | stylesheetResourceType":{
-"message":"Stylesheet",
-"description":"Label for a row in a data table; entries will be the total number and byte size of all 'Stylesheet' resources loaded by a web page. 'Stylesheet' refers to CSS stylesheets."},
-
-"lighthouse-core/lib/i18n/i18n.js | thirdPartyResourceType":{
-"message":"Third-party",
-"description":"Label for a row in a data table; entries will be the total number and byte size of all third-party resources loaded by a web page. 'Third-party resources are items loaded from URLs that aren't controlled by the owner of the web page."},
-
-"lighthouse-core/lib/i18n/i18n.js | totalResourceType":{
-"message":"Total",
-"description":"Label for a row in a data table; entries will be the total number and byte size of all resources loaded by a web page."},
-
-"lighthouse-core/lib/lh-error.js | badTraceRecording":{
-"message":"Something went wrong with recording the trace over your page load. Please run Lighthouse again. ({errorCode})",
-"description":"Error message explaining that the network trace was not able to be recorded for the Lighthouse run."},
-
-"lighthouse-core/lib/lh-error.js | criTimeout":{
-"message":"Timeout waiting for initial Debugger Protocol connection.",
-"description":"Error message explaining that Lighthouse timed out while waiting for the initial connection to the Chrome Devtools protocol."},
-
-"lighthouse-core/lib/lh-error.js | didntCollectScreenshots":{
-"message":"Chrome didn't collect any screenshots during the page load. Please make sure there is content visible on the page, and then try re-running Lighthouse. ({errorCode})",
-"description":"Error message explaining that the Lighthouse run was not able to collect screenshots through Chrome."},
-
-"lighthouse-core/lib/lh-error.js | dnsFailure":{
-"message":"DNS servers could not resolve the provided domain.",
-"description":"Error message explaining that the requested page could not be resolved by the DNS server."},
-
-"lighthouse-core/lib/lh-error.js | erroredRequiredArtifact":{
-"message":"Required {artifactName} gatherer encountered an error: {errorMessage}",
-"description":"Error message explaning that there was an error while trying to collect a resource that was required for testing. \"artifactName\" will be replaced with the name of the resource that wasn't collected; \"errorMessage\" will be replaced with a string description of the error that occurred."},
-
-"lighthouse-core/lib/lh-error.js | internalChromeError":{
-"message":"An internal Chrome error occurred. Please restart Chrome and try re-running Lighthouse.",
-"description":"Error message explaining that Chrome has encountered an error during the Lighthouse run, and that Chrome should be restarted."},
-
-"lighthouse-core/lib/lh-error.js | missingRequiredArtifact":{
-"message":"Required {artifactName} gatherer did not run.",
-"description":"Error message explaning that a resource that was required for testing was never collected. \"artifactName\" will be replaced with the name of the resource that wasn't collected."},
-
-"lighthouse-core/lib/lh-error.js | pageLoadFailed":{
-"message":"Lighthouse was unable to reliably load the page you requested. Make sure you are testing the correct URL and that the server is properly responding to all requests.",
-"description":"Error message explaining that Lighthouse could not load the requested URL and the steps that might be taken to fix the unreliability."},
-
-"lighthouse-core/lib/lh-error.js | pageLoadFailedHung":{
-"message":"Lighthouse was unable to reliably load the URL you requested because the page stopped responding.",
-"description":"Error message explaining that Lighthouse couldn't complete because the page has stopped responding to its instructions."},
-
-"lighthouse-core/lib/lh-error.js | pageLoadFailedInsecure":{
-"message":"The URL you have provided does not have a valid security certificate. {securityMessages}",
-"description":"Error message explaining that the security certificate of the page Lighthouse observed was invalid, so the URL cannot be accessed. securityMessages will be replaced with one or more strings from the browser explaining what was insecure about the page load."},
-
-"lighthouse-core/lib/lh-error.js | pageLoadFailedInterstitial":{
-"message":"Chrome prevented page load with an interstitial. Make sure you are testing the correct URL and that the server is properly responding to all requests.",
-"description":"Error message explaining that Chrome prevented the page from loading and displayed an interstitial screen instead, so the URL cannot be accessed."},
-
-"lighthouse-core/lib/lh-error.js | pageLoadFailedWithDetails":{
-"message":"Lighthouse was unable to reliably load the page you requested. Make sure you are testing the correct URL and that the server is properly responding to all requests. (Details: {errorDetails})",
-"description":"Error message explaining that Lighthouse could not load the requested URL and the steps that might be taken to fix the unreliability."},
-
-"lighthouse-core/lib/lh-error.js | pageLoadFailedWithStatusCode":{
-"message":"Lighthouse was unable to reliably load the page you requested. Make sure you are testing the correct URL and that the server is properly responding to all requests. (Status code: {statusCode})",
-"description":"Error message explaining that Lighthouse could not load the requested URL and the steps that might be taken to fix the unreliability."},
-
-"lighthouse-core/lib/lh-error.js | pageLoadTookTooLong":{
-"message":"Your page took too long to load. Please follow the opportunities in the report to reduce your page load time, and then try re-running Lighthouse. ({errorCode})",
-"description":"Error message explaining that the page loaded too slowly to perform a Lighthouse run."},
-
-"lighthouse-core/lib/lh-error.js | protocolTimeout":{
-"message":"Waiting for DevTools protocol response has exceeded the allotted time. (Method: {protocolMethod})",
-"description":"Error message explaining that the Chrome Devtools protocol has exceeded the maximum timeout allowed."},
-
-"lighthouse-core/lib/lh-error.js | requestContentTimeout":{
-"message":"Fetching resource content has exceeded the allotted time",
-"description":"Error message explaining that fetching the resources of the webpage has taken longer than the maximum time."},
-
-"lighthouse-core/lib/lh-error.js | urlInvalid":{
-"message":"The URL you have provided appears to be invalid.",
-"description":"Error message explaining that the provided URL Lighthouse points to is not valid, and cannot be loaded."},
-
-"lighthouse-core/report/html/renderer/util.js | auditGroupExpandTooltip":{
-"message":"Show audits",
-"description":"The tooltip text on an expandable chevron icon. Clicking the icon expands a section to reveal a list of audit results that was hidden by default."},
-
-"lighthouse-core/report/html/renderer/util.js | crcInitialNavigation":{
-"message":"Initial Navigation",
-"description":"String of text shown in a graphical representation of the flow of network requests for the web page. This label represents the initial network request that fetches an HTML page. This navigation may be redirected (eg. Initial navigation to http://example.com redirects to https://www.example.com)."},
-
-"lighthouse-core/report/html/renderer/util.js | crcLongestDurationLabel":{
-"message":"Maximum critical path latency:",
-"description":"Label of value shown in the summary of critical request chains. Refers to the total amount of time (milliseconds) of the longest critical path chain/sequence of network requests. Example value: 2310 ms"},
-
-"lighthouse-core/report/html/renderer/util.js | errorLabel":{
-"message":"Error!",
-"description":"A label, shown next to an audit title or metric title, indicating that there was an error computing it. The user can hover on the label to reveal a tooltip with the extended error message. Translation should be short (< 20 characters)."},
-
-"lighthouse-core/report/html/renderer/util.js | errorMissingAuditInfo":{
-"message":"Report error: no audit information",
-"description":"An error string displayed next to a particular audit when it has errored, but not provided any specific error message."},
-
-"lighthouse-core/report/html/renderer/util.js | labDataTitle":{
-"message":"Lab Data",
-"description":"Title of the lab data section of the Performance category. Within this section are various speed metrics which quantify the pageload performance into values presented in seconds and milliseconds. \"Lab\" is an abbreviated form of \"laboratory\", and refers to the fact that the data is from a controlled test of a website, not measurements from real users visiting that site."},
-
-"lighthouse-core/report/html/renderer/util.js | lsPerformanceCategoryDescription":{
-"message":"[Lighthouse](https://developers.google.com/web/tools/lighthouse/) analysis of the current page on an emulated mobile network. Values are estimated and may vary.",
-"description":"Explanation shown to users below performance results to inform them that the test was done with a 4G network connection and to warn them that the numbers they see will likely change slightly the next time they run Lighthouse. 'Lighthouse' becomes link text to additional documentation."},
-
-"lighthouse-core/report/html/renderer/util.js | manualAuditsGroupTitle":{
-"message":"Additional items to manually check",
-"description":"Section heading shown above a list of audits that were not computed by Lighthouse. They serve as a list of suggestions for the user to go and manually check. For example, Lighthouse can't automate testing cross-browser compatibility, so that is listed within this section, so the user is reminded to test it themselves. This section is collapsed by default, as the user should be focusing on the failed audits instead. Users can click this heading to reveal the list."},
-
-"lighthouse-core/report/html/renderer/util.js | notApplicableAuditsGroupTitle":{
-"message":"Not applicable",
-"description":"Section heading shown above a list of audits that do not apply to the page. For example, if an audit is 'Are images optimized?', but the page has no images on it, the audit will be marked as not applicable. This is neither passing or failing. This section is collapsed by default, as the user should be focusing on the failed audits instead. Users can click this heading to reveal the list."},
-
-"lighthouse-core/report/html/renderer/util.js | opportunityResourceColumnLabel":{
-"message":"Opportunity",
-"description":"Column heading label for the listing of opportunity audits. Each audit title represents an opportunity. There are only 2 columns, so no strict character limit."},
-
-"lighthouse-core/report/html/renderer/util.js | opportunitySavingsColumnLabel":{
-"message":"Estimated Savings",
-"description":"Column heading label for the estimated page load savings of opportunity audits. Estimated Savings is the total amount of time (in seconds) that Lighthouse computed could be reduced from the total page load time, if the suggested action is taken. There are only 2 columns, so no strict character limit."},
-
-"lighthouse-core/report/html/renderer/util.js | passedAuditsGroupTitle":{
-"message":"Passed audits",
-"description":"Section heading shown above a list of audits that are passing. 'Passed' here refers to a passing grade. This section is collapsed by default, as the user should be focusing on the failed audits instead. Users can click this heading to reveal the list."},
-
-"lighthouse-core/report/html/renderer/util.js | snippetCollapseButtonLabel":{
-"message":"Collapse snippet",
-"description":"Label for button that only shows a few lines of the snippet when clicked"},
-
-"lighthouse-core/report/html/renderer/util.js | snippetExpandButtonLabel":{
-"message":"Expand snippet",
-"description":"Label for button that shows all lines of the snippet when clicked"},
-
-"lighthouse-core/report/html/renderer/util.js | thirdPartyResourcesLabel":{
-"message":"Show 3rd-party resources",
-"description":"This label is for a checkbox above a table of items loaded by a web page. The checkbox is used to show or hide third-party (or \"3rd-party\") resources in the table, where \"third-party resources\" refers to items loaded by a web page from URLs that aren't controlled by the owner of the web page."},
-
-"lighthouse-core/report/html/renderer/util.js | toplevelWarningsMessage":{
-"message":"There were issues affecting this run of Lighthouse:",
-"description":"Label shown preceding any important warnings that may have invalidated the entire report. For example, if the user has Chrome extensions installed, they may add enough performance overhead that Lighthouse's performance metrics are unreliable. If shown, this will be displayed at the top of the report UI."},
-
-"lighthouse-core/report/html/renderer/util.js | varianceDisclaimer":{
-"message":"Values are estimated and may vary. The performance score is [based only on these metrics](https://github.com/GoogleChrome/lighthouse/blob/d2ec9ffbb21de9ad1a0f86ed24575eda32c796f0/docs/scoring.md#how-are-the-scores-weighted).",
-"description":"Disclaimer shown to users below the metric values (First Contentful Paint, Time to Interactive, etc) to warn them that the numbers they see will likely change slightly the next time they run Lighthouse."},
-
-"lighthouse-core/report/html/renderer/util.js | warningAuditsGroupTitle":{
-"message":"Passed audits but with warnings",
-"description":"Section heading shown above a list of passed audits that contain warnings. Audits under this section do not negatively impact the score, but Lighthouse has generated some potentially actionable suggestions that should be reviewed. This section is expanded by default and displays after the failing audits."},
-
-"lighthouse-core/report/html/renderer/util.js | warningHeader":{
-"message":"Warnings: ",
-"description":"This label is shown above a bulleted list of warnings. It is shown directly below an audit that produced warnings. Warnings describe situations the user should be aware of, as Lighthouse was unable to complete all the work required on this audit. For example, The 'Unable to decode image (biglogo.jpg)' warning may show up below an image encoding audit."},
-
-"stack-packs/packs/wordpress.js | efficient_animated_content":{
-"message":"Consider uploading your GIF to a service which will make it available to embed as an HTML5 video.",
-"description":"Additional description of a Lighthouse audit that tells the user how they can improve performance by encoding animated images as video, in the context of the Wordpress CMS platform. This is displayed after a user expands the section to see more. No character length limits. Links in (parenthesis) become link texts to additional documentation."},
-
-"stack-packs/packs/wordpress.js | offscreen_images":{
-"message":"Install a [lazy-load WordPress plugin](https://wordpress.org/plugins/search/lazy+load/) that provides the ability to defer any offscreen images, or switch to a theme that provides that functionality. Also consider using [the AMP plugin](https://wordpress.org/plugins/amp/).",
-"description":"Additional description of a Lighthouse audit that tells the user how they can improve performance by lazy loading images that are initially offscreen in the context of the Wordpress CMS platform. This is displayed after a user expands the section to see more. No character length limits. Links in (parenthesis) become link texts to additional documentation."},
-
-"stack-packs/packs/wordpress.js | render_blocking_resources":{
-"message":"There are a number of WordPress plugins that can help you [inline critical assets](https://wordpress.org/plugins/search/critical+css/) or [defer less important resources](https://wordpress.org/plugins/search/defer+css+javascript/). Beware that optimizations provided by these plugins may break features of your theme or plugins, so you will likely need to make code changes.",
-"description":"Additional description of a Lighthouse audit that tells the user how they can improve performance by reducing the amount of render blocking resources present on their page, in the context of the Wordpress CMS platform. This is displayed after a user expands the section to see more. No character length limits. Links in (parenthesis) become link texts to additional documentation."},
-
-"stack-packs/packs/wordpress.js | time_to_first_byte":{
-"message":"Themes, plugins, and server specifications all contribute to server response time. Consider finding a more optimized theme, carefully selecting an optimization plugin, and/or upgrading your server.",
-"description":"Additional description of a Lighthouse audit that tells the user how they can improve the time to first byte speed metric, in the context of the Wordpress CMS platform. This is displayed after a user expands the section to see more. No character length limits. Links in (parenthesis) become link texts to additional documentation."},
-
-"stack-packs/packs/wordpress.js | total_byte_weight":{
-"message":"Consider showing excerpts in your post lists (e.g. via the more tag), reducing the number of posts shown on a given page, breaking your long posts into multiple pages, or using a plugin to lazy-load comments.",
-"description":"Additional description of a Lighthouse audit that tells the user how they can improve site loading performance by reducing the total bytes delivered by their page in the context of the Wordpress CMS platform. This is displayed after a user expands the section to see more. No character length limits. Links in (parenthesis) become link texts to additional documentation."},
-
-"stack-packs/packs/wordpress.js | unminified_css":{
-"message":"A number of [WordPress plugins](https://wordpress.org/plugins/search/minify+css/) can speed up your site by concatenating, minifying, and compressing your styles. You may also want to use a build process to do this minification up-front if possible.",
-"description":"Additional description of a Lighthouse audit that tells the user how they can improve performance by minifying their CSS files in the context of the Wordpress CMS platform. This is displayed after a user expands the section to see more. No character length limits. Links in (parenthesis) become link texts to additional documentation."},
-
-"stack-packs/packs/wordpress.js | unminified_javascript":{
-"message":"A number of [WordPress plugins](https://wordpress.org/plugins/search/minify+javascript/) can speed up your site by concatenating, minifying, and compressing your scripts. You may also want to use a build process to do this minification up front if possible.",
-"description":"Additional description of a Lighthouse audit that tells the user how they can improve performance by minifying their Javascript files in the context of the Wordpress CMS platform. This is displayed after a user expands the section to see more. No character length limits. Links in (parenthesis) become link texts to additional documentation."},
-
-"stack-packs/packs/wordpress.js | unused_css_rules":{
-"message":"Consider reducing, or switching, the number of [WordPress plugins](https://wordpress.org/plugins/) loading unused CSS in your page. To identify plugins that are adding extraneous CSS, try running [code coverage](https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage) in Chrome DevTools. You can identify the theme/plugin responsible from the URL of the stylesheet. Look out for plugins that have many stylesheets in the list which have a lot of red in code coverage. A plugin should only enqueue a stylesheet if it is actually used on the page.",
-"description":"Additional description of a Lighthouse audit that tells the user how they can improve performance by removing unused CSS, in the context of the Wordpress CMS platform. This is displayed after a user expands the section to see more. No character length limits. Links in (parenthesis) become link texts to additional documentation."},
-
-"stack-packs/packs/wordpress.js | unused_javascript":{
-"message":"Consider reducing, or switching, the number of [WordPress plugins](https://wordpress.org/plugins/) loading unused JavaScript in your page. To identify plugins that are adding extraneous JS, try running [code coverage](https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage) in Chrome DevTools. You can identify the theme/plugin responsible from the URL of the script. Look out for plugins that have many scripts in the list which have a lot of red in code coverage. A plugin should only enqueue a script if it is actually used on the page.",
-"description":"Additional description of a Lighthouse audit that tells the user how they can improve performance by removing unused Javascript files in the context of the Wordpress CMS platform. This is displayed after a user expands the section to see more. No character length limits. Links in (parenthesis) become link texts to additional documentation."},
-
-"stack-packs/packs/wordpress.js | uses_long_cache_ttl":{
-"message":"Read about [Browser Caching in WordPress](https://codex.wordpress.org/WordPress_Optimization#Browser_Caching).",
-"description":"Additional description of a Lighthouse audit that tells the user how they can improve their site by enabling long caching in the context of the Wordpress CMS platform. This is displayed after a user expands the section to see more. No character length limits. Links in (parenthesis) become link texts to additional documentation."},
-
-"stack-packs/packs/wordpress.js | uses_optimized_images":{
-"message":"Consider using an [image optimization WordPress plugin](https://wordpress.org/plugins/search/optimize+images/) that compresses your images while retaining quality.",
-"description":"Additional description of a Lighthouse audit that tells the user how they can improve site performance by optimizing images, in the context of the Wordpress CMS platform. This is displayed after a user expands the section to see more. No character length limits. Links in (parenthesis) become link texts to additional documentation."},
-
-"stack-packs/packs/wordpress.js | uses_responsive_images":{
-"message":"Upload images directly through the [media library](https://codex.wordpress.org/Media_Library_Screen) to ensure that the required image sizes are available, and then insert them from the media library or use the image widget to ensure the optimal image sizes are used (including those for the responsive breakpoints). Avoid using `Full Size` images unless the dimensions are adequate for their usage. [Learn More](https://codex.wordpress.org/Inserting_Images_into_Posts_and_Pages#Image_Size).",
-"description":"Additional description of a Lighthouse audit that tells the user how they can improve performance by using responsive images in the context of the Wordpress CMS platform. This is displayed after a user expands the section to see more. No character length limits. Links in (parenthesis) become link texts to additional documentation."},
-
-"stack-packs/packs/wordpress.js | uses_text_compression":{
-"message":"You can enable text compression in your web server configuration.",
-"description":"Additional description of a Lighthouse audit that tells the user how they can improve performance via enabling text compression in the context of the Wordpress CMS platform. This is displayed after a user expands the section to see more. No character length limits. Links in (parenthesis) become link texts to additional documentation."},
-
-"stack-packs/packs/wordpress.js | uses_webp_images":{
-"message":"Consider using a [plugin](https://wordpress.org/plugins/search/convert+webp/) or service that will automatically convert your uploaded images to the optimal formats.",
-"description":"Additional description of a Lighthouse audit that tells the user how they can improve image loading by using webp in the context of the Wordpress CMS platform. This is displayed after a user expands the section to see more. No character length limits. Links in (parenthesis) become link texts to additional documentation."}};
-
-
-
-},{}],68:[function(require,module,exports){
 (function(__filename,__dirname){
 
 
@@ -28804,10 +27595,12 @@
 const isDeepEqual=require('lodash.isequal');
 const log=require('lighthouse-logger');
 const MessageFormat=require('intl-messageformat').default;
-const MessageParser=require('intl-messageformat-parser');
 const lookupClosestLocale=require('lookup-closest-locale');
 const LOCALES=require('./locales.js');
 
+
+
+
 const LH_ROOT=path.join(__dirname,'../../../');
 const MESSAGE_INSTANCE_ID_REGEX=/(.* \| .*) # (\d+)$/;
 
@@ -28816,16 +27609,24 @@
 (()=>{
 
 
-try{
+
+
+
+require('intl-pluralrules');
+
 
 const IntlPolyfill=require('intl');
 
+
 if(!IntlPolyfill.NumberFormat)return;
 
+
+const minimumLocales=['en','es','ru','zh'];
+const supportedLocales=Intl.NumberFormat.supportedLocalesOf(minimumLocales);
+
+if(supportedLocales.length!==minimumLocales.length){
 Intl.NumberFormat=IntlPolyfill.NumberFormat;
 Intl.DateTimeFormat=IntlPolyfill.DateTimeFormat;
-}catch(_){
-log.warn('i18n','Failed to install `intl` polyfill');
 }
 })();
 
@@ -28860,6 +27661,8 @@
 
 columnTransferSize:'Transfer Size',
 
+columnName:'Name',
+
 totalResourceType:'Total',
 
 documentResourceType:'Document',
@@ -28922,37 +27725,106 @@
 
 
 
-function _preprocessMessageValues(icuMessage,values={}){
-const clonedValues=JSON.parse(JSON.stringify(values));
-const parsed=MessageParser.parse(icuMessage);
 
-parsed.elements.
-filter(el=>el.type==='argumentElement').
-forEach(el=>{
-if(el.id&&el.id in values===false){
-throw new Error(`ICU Message contains a value reference ("${el.id}") that wasn't provided`);
+
+
+
+
+
+
+
+
+
+
+
+
+
+function collectAllCustomElementsFromICU(icuElements,seenElementsById=new Map()){
+for(const el of icuElements){
+
+if(el.type!=='argumentElement')continue;
+
+seenElementsById.set(el.id,el);
+
+
+if(!el.format||el.format.type!=='pluralFormat')continue;
+
+for(const option of el.format.options){
+
+collectAllCustomElementsFromICU(option.value.elements,seenElementsById);
 }
-});
+}
+
+return seenElementsById;
+}
 
 
-parsed.elements.
-filter(el=>el.format&&el.format.style==='milliseconds').
-
-forEach(el=>clonedValues[el.id]=Math.round(clonedValues[el.id]/10)*10);
 
 
-parsed.elements.
-filter(el=>el.format&&el.format.style==='seconds'&&el.id==='timeInMs').
-
-forEach(el=>clonedValues[el.id]=Math.round(clonedValues[el.id]/100)/10);
 
 
-parsed.elements.
-filter(el=>el.format&&el.format.style==='bytes').
 
-forEach(el=>clonedValues[el.id]=clonedValues[el.id]/1024);
 
-return clonedValues;
+
+
+function _preformatValues(icuMessage,messageFormatter,values){
+const elementMap=collectAllCustomElementsFromICU(messageFormatter.getAst().elements);
+const argumentElements=[...elementMap.values()];
+
+
+const formattedValues={};
+
+for(const{id,format}of argumentElements){
+
+if(id&&id in values===false){
+throw new Error(`ICU Message "${icuMessage}" contains a value reference ("${id}") `+
+`that wasn't provided`);
+}
+
+const value=values[id];
+
+
+if(!format||format.type!=='numberFormat'){
+formattedValues[id]=value;
+continue;
+}
+
+if(typeof value!=='number'){
+throw new Error(`ICU Message "${icuMessage}" contains a numeric reference ("${id}") `+
+'but provided value was not a number');
+}
+
+
+if(format.style==='milliseconds'){
+
+formattedValues[id]=Math.round(value/10)*10;
+}else if(format.style==='seconds'&&id==='timeInMs'){
+
+formattedValues[id]=Math.round(value/100)/10;
+}else if(format.style==='bytes'){
+
+formattedValues[id]=value/1024;
+}else{
+
+formattedValues[id]=value;
+}
+}
+
+
+for(const valueId of Object.keys(values)){
+if(valueId in formattedValues)continue;
+
+
+if(valueId==='errorCode'){
+formattedValues.errorCode=values.errorCode;
+continue;
+}
+
+throw new Error(`Provided value "${valueId}" does not match any placeholder in `+
+`ICU message "${icuMessage}"`);
+}
+
+return formattedValues;
 }
 
 
@@ -28974,24 +27846,38 @@
 
 
 
-function _formatIcuMessage(locale,icuMessageId,fallbackMessage,values){
+function _formatIcuMessage(locale,icuMessageId,uiStringMessage,values={}){
 const localeMessages=LOCALES[locale];
 if(!localeMessages)throw new Error(`Unsupported locale '${locale}'`);
-
-const localeMessage=localeMessages[icuMessageId]&&localeMessages[icuMessageId].message;
+let localeMessage=localeMessages[icuMessageId]&&localeMessages[icuMessageId].message;
 
 
-const messageForMessageFormat=localeMessage||fallbackMessage;
-if(messageForMessageFormat===undefined)throw new Error(_ICUMsgNotFoundMsg);
+
+if(!localeMessage&&uiStringMessage){
+
+localeMessage=uiStringMessage;
+
+
+if(!LOCALES.en[icuMessageId]||localeMessage!==LOCALES.en[icuMessageId].message){
+log.warn('i18n',`Message "${icuMessageId}" does not match its 'en' counterpart. `+
+`Run 'i18n' to update.`);
+}
+}
+
+if(!localeMessage){
+throw new Error(_ICUMsgNotFoundMsg);
+}
+
 
 const localeForMessageFormat=locale==='en-XA'||locale==='en-XL'?'de-DE':locale;
 
-const valuesForMessageFormat=_preprocessMessageValues(messageForMessageFormat,values);
+const formatter=new MessageFormat(localeMessage,localeForMessageFormat,formats);
 
-const formatter=new MessageFormat(messageForMessageFormat,localeForMessageFormat,formats);
+
+const valuesForMessageFormat=_preformatValues(localeMessage,formatter,values);
+
 const formattedString=formatter.format(valuesForMessageFormat);
-
-return{formattedString,icuMessage:messageForMessageFormat};
+return{formattedString,icuMessage:localeMessage};
 }
 
 
@@ -29034,11 +27920,20 @@
 
 
 
+
+
+
 function createMessageInstanceIdFn(filename,fileStrings){
 
 const mergedStrings={...UIStrings,...fileStrings};
 
 
+
+
+
+
+
+
 const getMessageInstanceIdFn=(icuMessage,values)=>{
 const keyname=Object.keys(mergedStrings).find(key=>mergedStrings[key]===icuMessage);
 if(!keyname)throw new Error(`Could not locate: ${icuMessage}`);
@@ -29164,6 +28059,19 @@
 return icuMessagePaths;
 }
 
+
+
+
+
+
+
+
+
+
+function registerLocaleData(locale,lhlMessages){
+LOCALES[locale]=lhlMessages;
+}
+
 module.exports={
 _formatPathAsString,
 _ICUMsgNotFoundMsg,
@@ -29174,11 +28082,13 @@
 getFormatted,
 getFormattedFromIdAndValues,
 replaceIcuMessageInstanceIds,
-isIcuMessage};
+isIcuMessage,
+collectAllCustomElementsFromICU,
+registerLocaleData};
 
 
 }).call(this,"/lighthouse-core/lib/i18n/i18n.js","/lighthouse-core/lib/i18n");
-},{"./locales.js":69,"intl":101,"intl-messageformat":116,"intl-messageformat-parser":114,"lighthouse-logger":128,"lodash.isequal":129,"lookup-closest-locale":130,"path":143}],69:[function(require,module,exports){
+},{"./locales.js":68,"intl":100,"intl-messageformat":118,"intl-pluralrules":100,"lighthouse-logger":124,"lodash.isequal":125,"lookup-closest-locale":126,"path":139}],68:[function(require,module,exports){
 
 
 
@@ -29197,9 +28107,12 @@
 
 
 
+
+
+
 const locales={
-'en-US':require('./en-US.json'),
-'en':require('./en-US.json'),
+'en-US':require('./locales/en-US.json'),
+'en':require('./locales/en-US.json'),
 
 
 'en-AU':require('./locales/en-GB.json'),
@@ -29292,7 +28205,7 @@
 
 module.exports=locales;
 
-},{"./en-US.json":67,"./locales/ar-XB.json":101,"./locales/ar.json":101,"./locales/bg.json":101,"./locales/ca.json":101,"./locales/cs.json":101,"./locales/da.json":101,"./locales/de.json":101,"./locales/el.json":101,"./locales/en-GB.json":101,"./locales/en-XA.json":101,"./locales/en-XL.json":101,"./locales/es-419.json":101,"./locales/es.json":101,"./locales/fi.json":101,"./locales/fil.json":101,"./locales/fr.json":101,"./locales/he.json":101,"./locales/hi.json":101,"./locales/hr.json":101,"./locales/hu.json":101,"./locales/id.json":101,"./locales/it.json":101,"./locales/ja.json":101,"./locales/ko.json":101,"./locales/lt.json":101,"./locales/lv.json":101,"./locales/nl.json":101,"./locales/no.json":101,"./locales/pl.json":101,"./locales/pt-PT.json":101,"./locales/pt.json":101,"./locales/ro.json":101,"./locales/ru.json":101,"./locales/sk.json":101,"./locales/sl.json":101,"./locales/sr-Latn.json":101,"./locales/sr.json":101,"./locales/sv.json":101,"./locales/ta.json":101,"./locales/te.json":101,"./locales/th.json":101,"./locales/tr.json":101,"./locales/uk.json":101,"./locales/vi.json":101,"./locales/zh-HK.json":101,"./locales/zh-TW.json":101,"./locales/zh.json":101}],70:[function(require,module,exports){
+},{"./locales/ar-XB.json":100,"./locales/ar.json":100,"./locales/bg.json":100,"./locales/ca.json":100,"./locales/cs.json":100,"./locales/da.json":100,"./locales/de.json":100,"./locales/el.json":100,"./locales/en-GB.json":100,"./locales/en-US.json":100,"./locales/en-XA.json":100,"./locales/en-XL.json":100,"./locales/es-419.json":100,"./locales/es.json":100,"./locales/fi.json":100,"./locales/fil.json":100,"./locales/fr.json":100,"./locales/he.json":100,"./locales/hi.json":100,"./locales/hr.json":100,"./locales/hu.json":100,"./locales/id.json":100,"./locales/it.json":100,"./locales/ja.json":100,"./locales/ko.json":100,"./locales/lt.json":100,"./locales/lv.json":100,"./locales/nl.json":100,"./locales/no.json":100,"./locales/pl.json":100,"./locales/pt-PT.json":100,"./locales/pt.json":100,"./locales/ro.json":100,"./locales/ru.json":100,"./locales/sk.json":100,"./locales/sl.json":100,"./locales/sr-Latn.json":100,"./locales/sr.json":100,"./locales/sv.json":100,"./locales/ta.json":100,"./locales/te.json":100,"./locales/th.json":100,"./locales/tr.json":100,"./locales/uk.json":100,"./locales/vi.json":100,"./locales/zh-HK.json":100,"./locales/zh-TW.json":100,"./locales/zh.json":100}],69:[function(require,module,exports){
 
 
 
@@ -29366,7 +28279,7 @@
 pngSizedAtLeast};
 
 
-},{"./url-shim.js":"url"}],71:[function(require,module,exports){
+},{"./url-shim.js":"url"}],70:[function(require,module,exports){
 
 
 
@@ -29574,7 +28487,7 @@
 convertNodeTimingsToTrace};
 
 
-},{}],72:[function(require,module,exports){
+},{}],71:[function(require,module,exports){
 (function(__filename){
 
 
@@ -29588,18 +28501,36 @@
 
 const UIStrings={
 
+
+
+
 didntCollectScreenshots:`Chrome didn't collect any screenshots during the page load. Please make sure there is content visible on the page, and then try re-running Lighthouse. ({errorCode})`,
 
+
+
+
 badTraceRecording:'Something went wrong with recording the trace over your page load. Please run Lighthouse again. ({errorCode})',
 
+
+
+
 pageLoadTookTooLong:'Your page took too long to load. Please follow the opportunities in the report to reduce your page load time, and then try re-running Lighthouse. ({errorCode})',
 
 pageLoadFailed:'Lighthouse was unable to reliably load the page you requested. Make sure you are testing the correct URL and that the server is properly responding to all requests.',
 
+
+
+
 pageLoadFailedWithStatusCode:'Lighthouse was unable to reliably load the page you requested. Make sure you are testing the correct URL and that the server is properly responding to all requests. (Status code: {statusCode})',
 
+
+
+
 pageLoadFailedWithDetails:'Lighthouse was unable to reliably load the page you requested. Make sure you are testing the correct URL and that the server is properly responding to all requests. (Details: {errorDetails})',
 
+
+
+
 pageLoadFailedInsecure:'The URL you have provided does not have a valid security certificate. {securityMessages}',
 
 pageLoadFailedInterstitial:'Chrome prevented page load with an interstitial. Make sure you are testing the correct URL and that the server is properly responding to all requests.',
@@ -29610,6 +28541,9 @@
 
 urlInvalid:'The URL you have provided appears to be invalid.',
 
+
+
+
 protocolTimeout:'Waiting for DevTools protocol response has exceeded the allotted time. (Method: {protocolMethod})',
 
 dnsFailure:'DNS servers could not resolve the provided domain.',
@@ -29618,8 +28552,15 @@
 
 criTimeout:'Timeout waiting for initial Debugger Protocol connection.',
 
+
+
+
 missingRequiredArtifact:'Required {artifactName} gatherer did not run.',
 
+
+
+
+
 erroredRequiredArtifact:'Required {artifactName} gatherer encountered an error: {errorMessage}'};
 
 
@@ -29651,6 +28592,7 @@
 this.name='LHError';
 this.code=errorDefinition.code;
 
+
 this.friendlyMessage=str_(errorDefinition.message,{errorCode:this.code,...properties});
 this.lhrRuntimeError=!!errorDefinition.lhrRuntimeError;
 if(properties)Object.assign(this,properties);
@@ -29934,7 +28876,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/lighthouse-core/lib/lh-error.js");
-},{"./i18n/i18n.js":68}],73:[function(require,module,exports){
+},{"./i18n/i18n.js":67}],72:[function(require,module,exports){
 
 
 
@@ -30435,7 +29377,7 @@
 
 module.exports=parse;
 
-},{"./url-shim.js":"url","cssstyle/lib/parsers":105}],74:[function(require,module,exports){
+},{"./url-shim.js":"url","cssstyle/lib/parsers":104}],73:[function(require,module,exports){
 
 
 
@@ -30606,7 +29548,7 @@
 
 module.exports={computeJSTokenLength,computeCSSTokenLength};
 
-},{}],75:[function(require,module,exports){
+},{}],74:[function(require,module,exports){
 
 
 
@@ -30998,7 +29940,7 @@
 
 module.exports=NetworkRecorder;
 
-},{"./network-request.js":76,"events":108,"lighthouse-logger":128}],76:[function(require,module,exports){
+},{"./network-request.js":75,"events":107,"lighthouse-logger":124}],75:[function(require,module,exports){
 (function(global){
 
 
@@ -31480,7 +30422,7 @@
 module.exports=NetworkRequest;
 
 }).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{});
-},{"./url-shim.js":"url"}],77:[function(require,module,exports){
+},{"./url-shim.js":"url"}],76:[function(require,module,exports){
 
 
 
@@ -31716,6 +30658,43 @@
 
 
 
+function isPositionFixed(element){
+
+
+
+
+
+function getStyleAttrValue(element,attr){
+
+return element.style[attr]||window.getComputedStyle(element)[attr];
+}
+
+
+const htmlEl=document.querySelector('html');
+if(htmlEl.scrollHeight<=htmlEl.clientHeight||
+!['scroll','auto','visible'].includes(getStyleAttrValue(htmlEl,'overflowY'))){
+return false;
+}
+
+let currentEl=element;
+while(currentEl){
+const position=getStyleAttrValue(currentEl,'position');
+if(position==='fixed'||position==='sticky'){
+return true;
+}
+currentEl=currentEl.parentElement;
+}
+return false;
+}
+
+
+
+
+
+
+
+
+
 function getNodeLabel(node){
 
 
@@ -31762,10 +30741,11 @@
 getNodeSelectorString:getNodeSelector.toString(),
 getNodeSelector:getNodeSelector,
 getNodeLabel:getNodeLabel,
-getNodeLabelString:getNodeLabel.toString()};
+getNodeLabelString:getNodeLabel.toString(),
+isPositionFixedString:isPositionFixed.toString()};
 
 
-},{}],78:[function(require,module,exports){
+},{}],77:[function(require,module,exports){
 
 
 
@@ -32015,7 +30995,7 @@
 filterOutTinyRects};
 
 
-},{}],79:[function(require,module,exports){
+},{}],78:[function(require,module,exports){
 
 
 
@@ -32142,7 +31122,7 @@
 
 module.exports=sentryDelegate;
 
-},{"lighthouse-logger":128,"raven":101}],80:[function(require,module,exports){
+},{"lighthouse-logger":124,"raven":100}],79:[function(require,module,exports){
 
 
 
@@ -32233,7 +31213,7 @@
 
 module.exports=collectStacks;
 
-},{}],81:[function(require,module,exports){
+},{}],80:[function(require,module,exports){
 
 
 
@@ -32293,7 +31273,7 @@
 getStackPacks};
 
 
-},{"../../stack-packs/index.js":180,"lighthouse-logger":128}],82:[function(require,module,exports){
+},{"../../stack-packs/index.js":176,"lighthouse-logger":124}],81:[function(require,module,exports){
 
 
 
@@ -32374,7 +31354,7 @@
 getLogNormalDistribution};
 
 
-},{}],83:[function(require,module,exports){
+},{}],82:[function(require,module,exports){
 
 
 
@@ -32482,7 +31462,7 @@
 getTappableRectsFromClientRects};
 
 
-},{"./rect-helpers.js":78}],84:[function(require,module,exports){
+},{"./rect-helpers.js":77}],83:[function(require,module,exports){
 
 
 
@@ -32750,7 +31730,7 @@
 
 module.exports=MainThreadTasks;
 
-},{"./task-groups.js":85}],85:[function(require,module,exports){
+},{"./task-groups.js":84}],84:[function(require,module,exports){
 
 
 
@@ -32863,7 +31843,7 @@
 taskNameToGroup};
 
 
-},{}],86:[function(require,module,exports){
+},{}],85:[function(require,module,exports){
 
 
 
@@ -32931,7 +31911,111 @@
 
 
 
-static _filteredStableSort(traceEvents,filter){
+
+
+
+
+
+
+
+
+
+
+
+
+
+static _sortTimestampEventGroup(
+tsGroupIndices,
+timestampSortedIndices,
+indexOfTsGroupIndicesStart,
+traceEvents)
+{
+
+
+
+
+
+
+
+
+
+
+
+
+const lookupArrayIndexByTsIndex=i=>timestampSortedIndices[i];
+
+const lookupEventByTsIndex=i=>traceEvents[lookupArrayIndexByTsIndex(i)];
+
+
+const eEventIndices=[];
+
+const bxEventIndices=[];
+
+const otherEventIndices=[];
+
+for(const tsIndex of tsGroupIndices){
+
+const arrayIndex=lookupArrayIndexByTsIndex(tsIndex);
+const event=lookupEventByTsIndex(tsIndex);
+if(event.ph==='E')eEventIndices.push(arrayIndex);else
+if(event.ph==='X'||event.ph==='B')bxEventIndices.push(arrayIndex);else
+otherEventIndices.push(arrayIndex);
+}
+
+
+const effectiveDuration=new Map();
+for(const index of bxEventIndices){
+const event=traceEvents[index];
+if(event.ph==='X'){
+effectiveDuration.set(index,event.dur);
+}else{
+
+let duration=Number.MAX_SAFE_INTEGER;
+
+let additionalNestedEventsWithSameName=0;
+const startIndex=indexOfTsGroupIndicesStart+tsGroupIndices.length;
+for(let j=startIndex;j<timestampSortedIndices.length;j++){
+const potentialMatchingEvent=lookupEventByTsIndex(j);
+const eventMatches=potentialMatchingEvent.name===event.name&&
+potentialMatchingEvent.pid===event.pid&&
+potentialMatchingEvent.tid===event.tid;
+
+
+if(!eventMatches)continue;
+
+if(potentialMatchingEvent.ph==='E'&&additionalNestedEventsWithSameName===0){
+
+duration=potentialMatchingEvent.ts-event.ts;
+break;
+}else if(potentialMatchingEvent.ph==='E'){
+
+additionalNestedEventsWithSameName--;
+}else if(potentialMatchingEvent.ph==='B'){
+
+additionalNestedEventsWithSameName++;
+}
+}
+
+effectiveDuration.set(index,duration);
+}
+}
+
+bxEventIndices.sort((indexA,indexB)=>(effectiveDuration.get(indexB)||0)-(
+effectiveDuration.get(indexA)||0)||indexA-indexB);
+
+otherEventIndices.sort((indexA,indexB)=>indexA-indexB);
+
+return[...eEventIndices,...bxEventIndices,...otherEventIndices];
+}
+
+
+
+
+
+
+
+
+static filteredTraceSort(traceEvents,filter){
 
 const indices=[];
 for(let srcIndex=0;srcIndex<traceEvents.length;srcIndex++){
@@ -32941,10 +32025,32 @@
 }
 
 
-indices.sort((indexA,indexB)=>{
-const result=traceEvents[indexA].ts-traceEvents[indexB].ts;
-return result?result:indexA-indexB;
-});
+indices.sort((indexA,indexB)=>traceEvents[indexA].ts-traceEvents[indexB].ts);
+
+
+for(let i=0;i<indices.length-1;i++){
+const ts=traceEvents[indices[i]].ts;
+const tsGroupIndices=[i];
+for(let j=i+1;j<indices.length;j++){
+if(traceEvents[indices[j]].ts!==ts)break;
+tsGroupIndices.push(j);
+}
+
+
+if(tsGroupIndices.length===1)continue;
+
+
+const finalIndexOrder=TraceProcessor._sortTimestampEventGroup(
+tsGroupIndices,
+indices,
+i,
+traceEvents);
+
+indices.splice(i,finalIndexOrder.length,...finalIndexOrder);
+
+
+i+=tsGroupIndices.length-1;
+}
 
 
 const sorted=[];
@@ -33218,7 +32324,7 @@
 static computeTraceOfTab(trace){
 
 
-const keyEvents=this._filteredStableSort(trace.traceEvents,e=>{
+const keyEvents=this.filteredTraceSort(trace.traceEvents,e=>{
 return e.cat.includes('blink.user_timing')||
 e.cat.includes('loading')||
 e.cat.includes('devtools.timeline')||
@@ -33272,7 +32378,7 @@
 
 
 const processEvents=TraceProcessor.
-_filteredStableSort(trace.traceEvents,e=>e.pid===mainFrameIds.pid);
+filteredTraceSort(trace.traceEvents,e=>e.pid===mainFrameIds.pid);
 
 const mainThreadEvents=processEvents.
 filter(e=>e.tid===mainFrameIds.tid);
@@ -33338,7 +32444,7 @@
 
 
 
-},{"lighthouse-logger":128}],87:[function(require,module,exports){
+},{"lighthouse-logger":124}],86:[function(require,module,exports){
 
 
 
@@ -33543,7 +32649,7 @@
 
 module.exports=Metrics;
 
-},{"lighthouse-logger":128}],88:[function(require,module,exports){
+},{"lighthouse-logger":124}],87:[function(require,module,exports){
 
 
 
@@ -33738,7 +32844,7 @@
 
 static formatNumber(number,granularity=0.1){
 const coarseValue=Math.round(number/granularity)*granularity;
-return coarseValue.toLocaleString(Util.numberDateLocale);
+return Util.numberFormatter.format(coarseValue);
 }
 
 
@@ -33747,8 +32853,7 @@
 
 
 static formatBytesToKB(size,granularity=0.1){
-const kbs=(Math.round(size/1024/granularity)*granularity).
-toLocaleString(Util.numberDateLocale);
+const kbs=Util.numberFormatter.format(Math.round(size/1024/granularity)*granularity);
 return`${kbs}${NBSP}KB`;
 }
 
@@ -33759,7 +32864,7 @@
 
 static formatMilliseconds(ms,granularity=10){
 const coarseTime=Math.round(ms/granularity)*granularity;
-return`${coarseTime.toLocaleString(Util.numberDateLocale)}${NBSP}ms`;
+return`${Util.numberFormatter.format(coarseTime)}${NBSP}ms`;
 }
 
 
@@ -33769,7 +32874,7 @@
 
 static formatSeconds(ms,granularity=0.1){
 const coarseTime=Math.round(ms/1000/granularity)*granularity;
-return`${coarseTime.toLocaleString(Util.numberDateLocale)}${NBSP}s`;
+return`${Util.numberFormatter.format(coarseTime)}${NBSP}s`;
 }
 
 
@@ -33831,6 +32936,73 @@
 
 
 
+
+
+static splitMarkdownCodeSpans(text){
+
+const segments=[];
+
+
+const parts=text.split(/`(.*?)`/g);
+for(let i=0;i<parts.length;i++){
+const text=parts[i];
+
+
+if(!text)continue;
+
+
+const isCode=i%2!==0;
+segments.push({
+isCode,
+text});
+
+}
+
+return segments;
+}
+
+
+
+
+
+
+
+
+
+static splitMarkdownLink(text){
+
+const segments=[];
+
+const parts=text.split(/\[([^\]]+?)\]\((https?:\/\/.*?)\)/g);
+while(parts.length){
+
+const[preambleText,linkText,linkHref]=parts.splice(0,3);
+
+if(preambleText){
+segments.push({
+isLink:false,
+text:preambleText});
+
+}
+
+
+if(linkText&&linkHref){
+segments.push({
+isLink:true,
+text:linkText,
+linkHref});
+
+}
+}
+
+return segments;
+}
+
+
+
+
+
+
 static getURLDisplayName(parsedUrl,options){
 
 options=options||{numPathParts:undefined,preserveQuery:undefined,
@@ -34035,10 +33207,11 @@
 
 
 static setNumberDateLocale(locale){
+
+if(locale==='en-XA')locale='de';
+
 Util.numberDateLocale=locale;
-
-
-if(Util.numberDateLocale==='en-XA')Util.numberDateLocale='de';
+Util.numberFormatter=new Intl.NumberFormat(locale);
 }
 
 
@@ -34101,6 +33274,12 @@
 
 
 
+Util.numberFormatter=new Intl.NumberFormat(Util.numberDateLocale);
+
+
+
+
+
 Util.UIStrings={
 
 varianceDisclaimer:'Values are estimated and may vary. The performance score is [based only on these metrics](https://github.com/GoogleChrome/lighthouse/blob/d2ec9ffbb21de9ad1a0f86ed24575eda32c796f0/docs/scoring.md#how-are-the-scores-weighted).',
@@ -34154,7 +33333,7 @@
 self.Util=Util;
 }
 
-},{}],89:[function(require,module,exports){
+},{}],88:[function(require,module,exports){
 
 
 
@@ -34274,7 +33453,7 @@
 
 module.exports=ReportGenerator;
 
-},{"./html/html-report-assets.js":101}],90:[function(require,module,exports){
+},{"./html/html-report-assets.js":100}],89:[function(require,module,exports){
 (function(process){
 
 
@@ -34699,7 +33878,7 @@
 
 static getGathererList(){
 const fileList=[
-...["accessibility.js","anchor-elements.js","cache-contents.js","console-messages.js","css-usage.js","dobetterweb","gatherer.js","html-without-javascript.js","http-redirect.js","image-elements.js","js-usage.js","link-elements.js","meta-elements.js","mixed-content.js","offline.js","runtime-exceptions.js","script-elements.js","seo","service-worker.js","start-url.js","viewport-dimensions.js"],
+...["accessibility.js","anchor-elements.js","cache-contents.js","console-messages.js","css-usage.js","dobetterweb","gatherer.js","html-without-javascript.js","http-redirect.js","iframe-elements.js","image-elements.js","js-usage.js","link-elements.js","meta-elements.js","mixed-content.js","offline.js","runtime-exceptions.js","script-elements.js","seo","service-worker.js","source-maps.js","start-url.js","viewport-dimensions.js"],
 ...["embedded-content.js","font-size.js","robots-txt.js","tap-targets.js"].map(f=>`seo/${f}`),
 ...["appcache.js","doctype.js","domstats.js","optimized-images.js","password-inputs-with-prevented-paste.js","response-compression.js","tags-blocking-first-paint.js"].
 map(f=>`dobetterweb/${f}`)];
@@ -34726,7 +33905,7 @@
 module.exports=Runner;
 
 }).call(this,require('_process'));
-},{"../package.json":179,"./audits/audit.js":3,"./gather/driver.js":51,"./gather/gather-runner.js":52,"./lib/asset-saver.js":56,"./lib/i18n/i18n.js":68,"./lib/lh-error.js":72,"./lib/sentry.js":79,"./lib/stack-packs.js":81,"./lib/url-shim.js":"url","./report/report-generator.js":89,"./scoring.js":91,"_process":145,"lighthouse-logger":128,"lodash.isequal":129,"path":143}],91:[function(require,module,exports){
+},{"../package.json":175,"./audits/audit.js":3,"./gather/driver.js":51,"./gather/gather-runner.js":52,"./lib/asset-saver.js":56,"./lib/i18n/i18n.js":67,"./lib/lh-error.js":71,"./lib/sentry.js":78,"./lib/stack-packs.js":80,"./lib/url-shim.js":"url","./report/report-generator.js":88,"./scoring.js":90,"_process":141,"lighthouse-logger":124,"lodash.isequal":125,"path":139}],90:[function(require,module,exports){
 
 
 
@@ -34821,7 +34000,7 @@
 
 module.exports=ReportScoring;
 
-},{"./audits/audit.js":3}],92:[function(require,module,exports){
+},{"./audits/audit.js":3}],91:[function(require,module,exports){
 (function(global){
 'use strict';
 
@@ -35315,7 +34494,7 @@
 };
 
 }).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{});
-},{"util/":95}],93:[function(require,module,exports){
+},{"util/":94}],92:[function(require,module,exports){
 if(typeof Object.create==='function'){
 
 module.exports=function inherits(ctor,superCtor){
@@ -35340,14 +34519,14 @@
 };
 }
 
-},{}],94:[function(require,module,exports){
+},{}],93:[function(require,module,exports){
 module.exports=function isBuffer(arg){
 return arg&&typeof arg==='object'&&
 typeof arg.copy==='function'&&
 typeof arg.fill==='function'&&
 typeof arg.readUInt8==='function';
 };
-},{}],95:[function(require,module,exports){
+},{}],94:[function(require,module,exports){
 (function(process,global){
 
 
@@ -35937,7 +35116,7 @@
 }
 
 }).call(this,require('_process'),typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{});
-},{"./support/isBuffer":94,"_process":145,"inherits":93}],96:[function(require,module,exports){
+},{"./support/isBuffer":93,"_process":141,"inherits":92}],95:[function(require,module,exports){
 
 const langs=[
 'aa',
@@ -44091,7 +43270,7 @@
 return langs;
 };
 
-},{}],97:[function(require,module,exports){
+},{}],96:[function(require,module,exports){
 'use strict';
 
 exports.byteLength=byteLength;
@@ -44244,9 +43423,9 @@
 return parts.join('');
 }
 
-},{}],98:[function(require,module,exports){
+},{}],97:[function(require,module,exports){
 
-},{}],99:[function(require,module,exports){
+},{}],98:[function(require,module,exports){
 (function(process,Buffer){
 'use strict';
 
@@ -44658,7 +43837,7 @@
 
 exports.Zlib=Zlib;
 }).call(this,require('_process'),require("buffer").Buffer);
-},{"_process":145,"assert":92,"buffer":102,"pako/lib/zlib/constants":136,"pako/lib/zlib/deflate.js":138,"pako/lib/zlib/inflate.js":101,"pako/lib/zlib/zstream":141}],100:[function(require,module,exports){
+},{"_process":141,"assert":91,"buffer":101,"pako/lib/zlib/constants":132,"pako/lib/zlib/deflate.js":134,"pako/lib/zlib/inflate.js":100,"pako/lib/zlib/zstream":137}],99:[function(require,module,exports){
 (function(process){
 'use strict';
 
@@ -45270,9 +44449,9 @@
 util.inherits(InflateRaw,Zlib);
 util.inherits(Unzip,Zlib);
 }).call(this,require('_process'));
-},{"./binding":99,"_process":145,"assert":92,"buffer":102,"stream":170,"util":178}],101:[function(require,module,exports){
-arguments[4][98][0].apply(exports,arguments);
-},{"dup":98}],102:[function(require,module,exports){
+},{"./binding":98,"_process":141,"assert":91,"buffer":101,"stream":166,"util":174}],100:[function(require,module,exports){
+arguments[4][97][0].apply(exports,arguments);
+},{"dup":97}],101:[function(require,module,exports){
 (function(Buffer){
 
 
@@ -47053,7 +46232,7 @@
 }
 
 }).call(this,require("buffer").Buffer);
-},{"base64-js":97,"buffer":102,"ieee754":111}],103:[function(require,module,exports){
+},{"base64-js":96,"buffer":101,"ieee754":110}],102:[function(require,module,exports){
 (function(Buffer){
 
 
@@ -47164,7 +46343,7 @@
 }
 
 }).call(this,{"isBuffer":require("../../is-buffer/index.js")});
-},{"../../is-buffer/index.js":123}],104:[function(require,module,exports){
+},{"../../is-buffer/index.js":119}],103:[function(require,module,exports){
 module.exports=[
 "aliceblue",
 "antiquewhite",
@@ -47316,7 +46495,7 @@
 "yellowgreen"];
 
 
-},{}],105:[function(require,module,exports){
+},{}],104:[function(require,module,exports){
 
 
 
@@ -48015,7 +47194,7 @@
 return dashed;
 };
 
-},{"./named_colors.json":104}],106:[function(require,module,exports){
+},{"./named_colors.json":103}],105:[function(require,module,exports){
 (function(process){
 
 
@@ -48204,7 +47383,7 @@
 }
 
 }).call(this,require('_process'));
-},{"./debug":107,"_process":145}],107:[function(require,module,exports){
+},{"./debug":106,"_process":141}],106:[function(require,module,exports){
 
 
 
@@ -48408,7 +47587,7 @@
 return val;
 }
 
-},{"ms":133}],108:[function(require,module,exports){
+},{"ms":129}],107:[function(require,module,exports){
 
 
 
@@ -48933,7 +48112,7 @@
 };
 }
 
-},{}],109:[function(require,module,exports){
+},{}],108:[function(require,module,exports){
 (function(Buffer){
 var querystring=require('querystring');
 var trim=require('./trim');
@@ -49240,12 +48419,12 @@
 module.exports=Link;
 
 }).call(this,{"isBuffer":require("../../is-buffer/index.js")});
-},{"../../is-buffer/index.js":123,"./trim":110,"querystring":148}],110:[function(require,module,exports){
+},{"../../is-buffer/index.js":119,"./trim":109,"querystring":144}],109:[function(require,module,exports){
 module.exports=function trim(value){
 return value.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,'');
 };
 
-},{}],111:[function(require,module,exports){
+},{}],110:[function(require,module,exports){
 exports.read=function(buffer,offset,isLE,mLen,nBytes){
 var e,m;
 var eLen=nBytes*8-mLen-1;
@@ -49331,7 +48510,7 @@
 buffer[offset+i-d]|=s*128;
 };
 
-},{}],112:[function(require,module,exports){
+},{}],111:[function(require,module,exports){
 
 
 
@@ -49476,26 +48655,25 @@
 })(ImageSSIM||(ImageSSIM={}));
 module.exports=ImageSSIM;
 
-},{}],113:[function(require,module,exports){
-arguments[4][93][0].apply(exports,arguments);
-},{"dup":93}],114:[function(require,module,exports){
+},{}],112:[function(require,module,exports){
+arguments[4][92][0].apply(exports,arguments);
+},{"dup":92}],113:[function(require,module,exports){
 'use strict';
 
-exports=module.exports=require('./lib/parser')['default'];
-exports['default']=exports;
+var parser=require('./lib/parser');
 
-},{"./lib/parser":115}],115:[function(require,module,exports){
+module.exports=parser;
+module.exports['default']=parser;
+
+},{"./lib/parser":114}],114:[function(require,module,exports){
+
+
+
+
+
+
 "use strict";
 
-exports["default"]=function(){
-"use strict";
-
-
-
-
-
-
-
 function peg$subclass(child,parent){
 function ctor(){this.constructor=child;}
 ctor.prototype=parent.prototype;
@@ -49516,11 +48694,117 @@
 
 peg$subclass(peg$SyntaxError,Error);
 
-function peg$parse(input){
-var options=arguments.length>1?arguments[1]:{},
-parser=this,
+peg$SyntaxError.buildMessage=function(expected,found){
+var DESCRIBE_EXPECTATION_FNS={
+literal:function(expectation){
+return"\""+literalEscape(expectation.text)+"\"";
+},
 
-peg$FAILED={},
+"class":function(expectation){
+var escapedParts="",
+i;
+
+for(i=0;i<expectation.parts.length;i++){
+escapedParts+=expectation.parts[i]instanceof Array?
+classEscape(expectation.parts[i][0])+"-"+classEscape(expectation.parts[i][1]):
+classEscape(expectation.parts[i]);
+}
+
+return"["+(expectation.inverted?"^":"")+escapedParts+"]";
+},
+
+any:function(expectation){
+return"any character";
+},
+
+end:function(expectation){
+return"end of input";
+},
+
+other:function(expectation){
+return expectation.description;
+}};
+
+
+function hex(ch){
+return ch.charCodeAt(0).toString(16).toUpperCase();
+}
+
+function literalEscape(s){
+return s.
+replace(/\\/g,'\\\\').
+replace(/"/g,'\\"').
+replace(/\0/g,'\\0').
+replace(/\t/g,'\\t').
+replace(/\n/g,'\\n').
+replace(/\r/g,'\\r').
+replace(/[\x00-\x0F]/g,function(ch){return'\\x0'+hex(ch);}).
+replace(/[\x10-\x1F\x7F-\x9F]/g,function(ch){return'\\x'+hex(ch);});
+}
+
+function classEscape(s){
+return s.
+replace(/\\/g,'\\\\').
+replace(/\]/g,'\\]').
+replace(/\^/g,'\\^').
+replace(/-/g,'\\-').
+replace(/\0/g,'\\0').
+replace(/\t/g,'\\t').
+replace(/\n/g,'\\n').
+replace(/\r/g,'\\r').
+replace(/[\x00-\x0F]/g,function(ch){return'\\x0'+hex(ch);}).
+replace(/[\x10-\x1F\x7F-\x9F]/g,function(ch){return'\\x'+hex(ch);});
+}
+
+function describeExpectation(expectation){
+return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation);
+}
+
+function describeExpected(expected){
+var descriptions=new Array(expected.length),
+i,j;
+
+for(i=0;i<expected.length;i++){
+descriptions[i]=describeExpectation(expected[i]);
+}
+
+descriptions.sort();
+
+if(descriptions.length>0){
+for(i=1,j=1;i<descriptions.length;i++){
+if(descriptions[i-1]!==descriptions[i]){
+descriptions[j]=descriptions[i];
+j++;
+}
+}
+descriptions.length=j;
+}
+
+switch(descriptions.length){
+case 1:
+return descriptions[0];
+
+case 2:
+return descriptions[0]+" or "+descriptions[1];
+
+default:
+return descriptions.slice(0,-1).join(", ")+
+", or "+
+descriptions[descriptions.length-1];}
+
+}
+
+function describeFound(found){
+return found?"\""+literalEscape(found)+"\"":"end of input";
+}
+
+return"Expected "+describeExpected(expected)+" but "+describeFound(found)+" found.";
+};
+
+function peg$parse(input,options){
+options=options!==void 0?options:{};
+
+var peg$FAILED={},
 
 peg$startRuleFunctions={start:peg$parsestart},
 peg$startRuleFunction=peg$parsestart,
@@ -49532,19 +48816,10 @@
 location:location()};
 
 },
-peg$c1=function(text){
-var string='',
-i,j,outerLen,inner,innerLen;
-
-for(i=0,outerLen=text.length;i<outerLen;i+=1){
-inner=text[i];
-
-for(j=0,innerLen=inner.length;j<innerLen;j+=1){
-string+=inner[j];
-}
-}
-
-return string;
+peg$c1=function(chunks){
+return chunks.reduce(function(all,chunk){
+return all.concat(chunk);
+},[]).join('');
 },
 peg$c2=function(messageText){
 return{
@@ -49553,15 +48828,14 @@
 location:location()};
 
 },
-peg$c3=/^[^ \t\n\r,.+={}#]/,
-peg$c4={type:"class",value:"[^ \\t\\n\\r,.+={}#]",description:"[^ \\t\\n\\r,.+={}#]"},
-peg$c5="{",
-peg$c6={type:"literal",value:"{",description:"\"{\""},
-peg$c7=",",
-peg$c8={type:"literal",value:",",description:"\",\""},
-peg$c9="}",
-peg$c10={type:"literal",value:"}",description:"\"}\""},
-peg$c11=function(id,format){
+peg$c3=function(chars){return chars.join('');},
+peg$c4="{",
+peg$c5=peg$literalExpectation("{",false),
+peg$c6=",",
+peg$c7=peg$literalExpectation(",",false),
+peg$c8="}",
+peg$c9=peg$literalExpectation("}",false),
+peg$c10=function(id,format){
 return{
 type:'argumentElement',
 id:id,
@@ -49569,22 +48843,22 @@
 location:location()};
 
 },
-peg$c12="number",
-peg$c13={type:"literal",value:"number",description:"\"number\""},
-peg$c14="date",
-peg$c15={type:"literal",value:"date",description:"\"date\""},
-peg$c16="time",
-peg$c17={type:"literal",value:"time",description:"\"time\""},
-peg$c18=function(type,style){
+peg$c11="number",
+peg$c12=peg$literalExpectation("number",false),
+peg$c13="date",
+peg$c14=peg$literalExpectation("date",false),
+peg$c15="time",
+peg$c16=peg$literalExpectation("time",false),
+peg$c17=function(type,style){
 return{
 type:type+'Format',
 style:style&&style[2],
 location:location()};
 
 },
-peg$c19="plural",
-peg$c20={type:"literal",value:"plural",description:"\"plural\""},
-peg$c21=function(pluralStyle){
+peg$c18="plural",
+peg$c19=peg$literalExpectation("plural",false),
+peg$c20=function(pluralStyle){
 return{
 type:pluralStyle.type,
 ordinal:false,
@@ -49593,9 +48867,9 @@
 location:location()};
 
 },
-peg$c22="selectordinal",
-peg$c23={type:"literal",value:"selectordinal",description:"\"selectordinal\""},
-peg$c24=function(pluralStyle){
+peg$c21="selectordinal",
+peg$c22=peg$literalExpectation("selectordinal",false),
+peg$c23=function(pluralStyle){
 return{
 type:pluralStyle.type,
 ordinal:true,
@@ -49604,18 +48878,18 @@
 location:location()};
 
 },
-peg$c25="select",
-peg$c26={type:"literal",value:"select",description:"\"select\""},
-peg$c27=function(options){
+peg$c24="select",
+peg$c25=peg$literalExpectation("select",false),
+peg$c26=function(options){
 return{
 type:'selectFormat',
 options:options,
 location:location()};
 
 },
-peg$c28="=",
-peg$c29={type:"literal",value:"=",description:"\"=\""},
-peg$c30=function(selector,pattern){
+peg$c27="=",
+peg$c28=peg$literalExpectation("=",false),
+peg$c29=function(selector,pattern){
 return{
 type:'optionalFormatPattern',
 selector:selector,
@@ -49623,12 +48897,12 @@
 location:location()};
 
 },
-peg$c31="offset:",
-peg$c32={type:"literal",value:"offset:",description:"\"offset:\""},
-peg$c33=function(number){
+peg$c30="offset:",
+peg$c31=peg$literalExpectation("offset:",false),
+peg$c32=function(number){
 return number;
 },
-peg$c34=function(offset,options){
+peg$c33=function(offset,options){
 return{
 type:'pluralFormat',
 offset:offset,
@@ -49636,45 +48910,51 @@
 location:location()};
 
 },
-peg$c35={type:"other",description:"whitespace"},
-peg$c36=/^[ \t\n\r]/,
-peg$c37={type:"class",value:"[ \\t\\n\\r]",description:"[ \\t\\n\\r]"},
-peg$c38={type:"other",description:"optionalWhitespace"},
-peg$c39=/^[0-9]/,
-peg$c40={type:"class",value:"[0-9]",description:"[0-9]"},
-peg$c41=/^[0-9a-f]/i,
-peg$c42={type:"class",value:"[0-9a-f]i",description:"[0-9a-f]i"},
-peg$c43="0",
-peg$c44={type:"literal",value:"0",description:"\"0\""},
-peg$c45=/^[1-9]/,
-peg$c46={type:"class",value:"[1-9]",description:"[1-9]"},
-peg$c47=function(digits){
+peg$c34=peg$otherExpectation("whitespace"),
+peg$c35=/^[ \t\n\r]/,
+peg$c36=peg$classExpectation([" ","\t","\n","\r"],false,false),
+peg$c37=peg$otherExpectation("optionalWhitespace"),
+peg$c38=/^[0-9]/,
+peg$c39=peg$classExpectation([["0","9"]],false,false),
+peg$c40=/^[0-9a-f]/i,
+peg$c41=peg$classExpectation([["0","9"],["a","f"]],false,true),
+peg$c42="0",
+peg$c43=peg$literalExpectation("0",false),
+peg$c44=/^[1-9]/,
+peg$c45=peg$classExpectation([["1","9"]],false,false),
+peg$c46=function(digits){
 return parseInt(digits,10);
 },
-peg$c48=/^[^{}\\\0-\x1F \t\n\r]/,
-peg$c49={type:"class",value:"[^{}\\\\\\0-\\x1F\\x7f \\t\\n\\r]",description:"[^{}\\\\\\0-\\x1F\\x7f \\t\\n\\r]"},
-peg$c50="\\\\",
-peg$c51={type:"literal",value:"\\\\",description:"\"\\\\\\\\\""},
-peg$c52=function(){return'\\';},
-peg$c53="\\#",
-peg$c54={type:"literal",value:"\\#",description:"\"\\\\#\""},
-peg$c55=function(){return'\\#';},
-peg$c56="\\{",
-peg$c57={type:"literal",value:"\\{",description:"\"\\\\{\""},
-peg$c58=function(){return'\u007B';},
-peg$c59="\\}",
-peg$c60={type:"literal",value:"\\}",description:"\"\\\\}\""},
-peg$c61=function(){return'\u007D';},
-peg$c62="\\u",
-peg$c63={type:"literal",value:"\\u",description:"\"\\\\u\""},
-peg$c64=function(digits){
+peg$c47="'",
+peg$c48=peg$literalExpectation("'",false),
+peg$c49=/^[ \t\n\r,.+={}#]/,
+peg$c50=peg$classExpectation([" ","\t","\n","\r",",",".","+","=","{","}","#"],false,false),
+peg$c51=peg$anyExpectation(),
+peg$c52=function(char){return char;},
+peg$c53=function(sequence){return sequence;},
+peg$c54=/^[^{}\\\0-\x1F\x7F \t\n\r]/,
+peg$c55=peg$classExpectation(["{","}","\\",["\0","\x1F"],"\x7F"," ","\t","\n","\r"],true,false),
+peg$c56="\\\\",
+peg$c57=peg$literalExpectation("\\\\",false),
+peg$c58=function(){return'\\';},
+peg$c59="\\#",
+peg$c60=peg$literalExpectation("\\#",false),
+peg$c61=function(){return'\\#';},
+peg$c62="\\{",
+peg$c63=peg$literalExpectation("\\{",false),
+peg$c64=function(){return'\u007B';},
+peg$c65="\\}",
+peg$c66=peg$literalExpectation("\\}",false),
+peg$c67=function(){return'\u007D';},
+peg$c68="\\u",
+peg$c69=peg$literalExpectation("\\u",false),
+peg$c70=function(digits){
 return String.fromCharCode(parseInt(digits,16));
 },
-peg$c65=function(chars){return chars.join('');},
 
 peg$currPos=0,
 peg$savedPos=0,
-peg$posDetailsCache=[{line:1,column:1,seenCR:false}],
+peg$posDetailsCache=[{line:1,column:1}],
 peg$maxFailPos=0,
 peg$maxFailExpected=[],
 peg$silentFails=0,
@@ -49697,27 +48977,44 @@
 return peg$computeLocation(peg$savedPos,peg$currPos);
 }
 
-function expected(description){
-throw peg$buildException(
-null,
-[{type:"other",description:description}],
+function expected(description,location){
+location=location!==void 0?location:peg$computeLocation(peg$savedPos,peg$currPos);
+
+throw peg$buildStructuredError(
+[peg$otherExpectation(description)],
 input.substring(peg$savedPos,peg$currPos),
-peg$computeLocation(peg$savedPos,peg$currPos));
+location);
 
 }
 
-function error(message){
-throw peg$buildException(
-message,
-null,
-input.substring(peg$savedPos,peg$currPos),
-peg$computeLocation(peg$savedPos,peg$currPos));
+function error(message,location){
+location=location!==void 0?location:peg$computeLocation(peg$savedPos,peg$currPos);
 
+throw peg$buildSimpleError(message,location);
+}
+
+function peg$literalExpectation(text,ignoreCase){
+return{type:"literal",text:text,ignoreCase:ignoreCase};
+}
+
+function peg$classExpectation(parts,inverted,ignoreCase){
+return{type:"class",parts:parts,inverted:inverted,ignoreCase:ignoreCase};
+}
+
+function peg$anyExpectation(){
+return{type:"any"};
+}
+
+function peg$endExpectation(){
+return{type:"end"};
+}
+
+function peg$otherExpectation(description){
+return{type:"other",description:description};
 }
 
 function peg$computePosDetails(pos){
-var details=peg$posDetailsCache[pos],
-p,ch;
+var details=peg$posDetailsCache[pos],p;
 
 if(details){
 return details;
@@ -49730,23 +49027,15 @@
 details=peg$posDetailsCache[p];
 details={
 line:details.line,
-column:details.column,
-seenCR:details.seenCR};
+column:details.column};
 
 
 while(p<pos){
-ch=input.charAt(p);
-if(ch==="\n"){
-if(!details.seenCR){details.line++;}
-details.column=1;
-details.seenCR=false;
-}else if(ch==="\r"||ch==="\u2028"||ch==="\u2029"){
+if(input.charCodeAt(p)===10){
 details.line++;
 details.column=1;
-details.seenCR=true;
 }else{
 details.column++;
-details.seenCR=false;
 }
 
 p++;
@@ -49786,71 +49075,13 @@
 peg$maxFailExpected.push(expected);
 }
 
-function peg$buildException(message,expected,found,location){
-function cleanupExpected(expected){
-var i=1;
-
-expected.sort(function(a,b){
-if(a.description<b.description){
-return-1;
-}else if(a.description>b.description){
-return 1;
-}else{
-return 0;
-}
-});
-
-while(i<expected.length){
-if(expected[i-1]===expected[i]){
-expected.splice(i,1);
-}else{
-i++;
-}
-}
+function peg$buildSimpleError(message,location){
+return new peg$SyntaxError(message,null,null,location);
 }
 
-function buildMessage(expected,found){
-function stringEscape(s){
-function hex(ch){return ch.charCodeAt(0).toString(16).toUpperCase();}
-
-return s.
-replace(/\\/g,'\\\\').
-replace(/"/g,'\\"').
-replace(/\x08/g,'\\b').
-replace(/\t/g,'\\t').
-replace(/\n/g,'\\n').
-replace(/\f/g,'\\f').
-replace(/\r/g,'\\r').
-replace(/[\x00-\x07\x0B\x0E\x0F]/g,function(ch){return'\\x0'+hex(ch);}).
-replace(/[\x10-\x1F\x80-\xFF]/g,function(ch){return'\\x'+hex(ch);}).
-replace(/[\u0100-\u0FFF]/g,function(ch){return'\\u0'+hex(ch);}).
-replace(/[\u1000-\uFFFF]/g,function(ch){return'\\u'+hex(ch);});
-}
-
-var expectedDescs=new Array(expected.length),
-expectedDesc,foundDesc,i;
-
-for(i=0;i<expected.length;i++){
-expectedDescs[i]=expected[i].description;
-}
-
-expectedDesc=expected.length>1?
-expectedDescs.slice(0,-1).join(", ")+
-" or "+
-expectedDescs[expected.length-1]:
-expectedDescs[0];
-
-foundDesc=found?"\""+stringEscape(found)+"\"":"end of input";
-
-return"Expected "+expectedDesc+" but "+foundDesc+" found.";
-}
-
-if(expected!==null){
-cleanupExpected(expected);
-}
-
+function peg$buildStructuredError(expected,found,location){
 return new peg$SyntaxError(
-message!==null?message:buildMessage(expected,found),
+peg$SyntaxError.buildMessage(expected,found),
 expected,
 found,
 location);
@@ -49988,32 +49219,16 @@
 if(s0===peg$FAILED){
 s0=peg$currPos;
 s1=[];
-if(peg$c3.test(input.charAt(peg$currPos))){
-s2=input.charAt(peg$currPos);
-peg$currPos++;
-}else{
-s2=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c4);}
-}
-if(s2!==peg$FAILED){
+s2=peg$parsequoteEscapedChar();
 while(s2!==peg$FAILED){
 s1.push(s2);
-if(peg$c3.test(input.charAt(peg$currPos))){
-s2=input.charAt(peg$currPos);
-peg$currPos++;
-}else{
-s2=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c4);}
-}
-}
-}else{
-s1=peg$FAILED;
+s2=peg$parsequoteEscapedChar();
 }
 if(s1!==peg$FAILED){
-s0=input.substring(s0,peg$currPos);
-}else{
-s0=s1;
+peg$savedPos=s0;
+s1=peg$c3(s1);
 }
+s0=s1;
 }
 
 return s0;
@@ -50024,11 +49239,11 @@
 
 s0=peg$currPos;
 if(input.charCodeAt(peg$currPos)===123){
-s1=peg$c5;
+s1=peg$c4;
 peg$currPos++;
 }else{
 s1=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c6);}
+if(peg$silentFails===0){peg$fail(peg$c5);}
 }
 if(s1!==peg$FAILED){
 s2=peg$parse_();
@@ -50039,11 +49254,11 @@
 if(s4!==peg$FAILED){
 s5=peg$currPos;
 if(input.charCodeAt(peg$currPos)===44){
-s6=peg$c7;
+s6=peg$c6;
 peg$currPos++;
 }else{
 s6=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c8);}
+if(peg$silentFails===0){peg$fail(peg$c7);}
 }
 if(s6!==peg$FAILED){
 s7=peg$parse_();
@@ -50071,15 +49286,15 @@
 s6=peg$parse_();
 if(s6!==peg$FAILED){
 if(input.charCodeAt(peg$currPos)===125){
-s7=peg$c9;
+s7=peg$c8;
 peg$currPos++;
 }else{
 s7=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c10);}
+if(peg$silentFails===0){peg$fail(peg$c9);}
 }
 if(s7!==peg$FAILED){
 peg$savedPos=s0;
-s1=peg$c11(s3,s5);
+s1=peg$c10(s3,s5);
 s0=s1;
 }else{
 peg$currPos=s0;
@@ -50134,28 +49349,28 @@
 var s0,s1,s2,s3,s4,s5,s6;
 
 s0=peg$currPos;
-if(input.substr(peg$currPos,6)===peg$c12){
-s1=peg$c12;
+if(input.substr(peg$currPos,6)===peg$c11){
+s1=peg$c11;
 peg$currPos+=6;
 }else{
 s1=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c13);}
+if(peg$silentFails===0){peg$fail(peg$c12);}
 }
 if(s1===peg$FAILED){
-if(input.substr(peg$currPos,4)===peg$c14){
-s1=peg$c14;
+if(input.substr(peg$currPos,4)===peg$c13){
+s1=peg$c13;
 peg$currPos+=4;
 }else{
 s1=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c15);}
+if(peg$silentFails===0){peg$fail(peg$c14);}
 }
 if(s1===peg$FAILED){
-if(input.substr(peg$currPos,4)===peg$c16){
-s1=peg$c16;
+if(input.substr(peg$currPos,4)===peg$c15){
+s1=peg$c15;
 peg$currPos+=4;
 }else{
 s1=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c17);}
+if(peg$silentFails===0){peg$fail(peg$c16);}
 }
 }
 }
@@ -50164,11 +49379,11 @@
 if(s2!==peg$FAILED){
 s3=peg$currPos;
 if(input.charCodeAt(peg$currPos)===44){
-s4=peg$c7;
+s4=peg$c6;
 peg$currPos++;
 }else{
 s4=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c8);}
+if(peg$silentFails===0){peg$fail(peg$c7);}
 }
 if(s4!==peg$FAILED){
 s5=peg$parse_();
@@ -50194,7 +49409,7 @@
 }
 if(s3!==peg$FAILED){
 peg$savedPos=s0;
-s1=peg$c18(s1,s3);
+s1=peg$c17(s1,s3);
 s0=s1;
 }else{
 peg$currPos=s0;
@@ -50216,22 +49431,22 @@
 var s0,s1,s2,s3,s4,s5;
 
 s0=peg$currPos;
-if(input.substr(peg$currPos,6)===peg$c19){
-s1=peg$c19;
+if(input.substr(peg$currPos,6)===peg$c18){
+s1=peg$c18;
 peg$currPos+=6;
 }else{
 s1=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c20);}
+if(peg$silentFails===0){peg$fail(peg$c19);}
 }
 if(s1!==peg$FAILED){
 s2=peg$parse_();
 if(s2!==peg$FAILED){
 if(input.charCodeAt(peg$currPos)===44){
-s3=peg$c7;
+s3=peg$c6;
 peg$currPos++;
 }else{
 s3=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c8);}
+if(peg$silentFails===0){peg$fail(peg$c7);}
 }
 if(s3!==peg$FAILED){
 s4=peg$parse_();
@@ -50239,7 +49454,7 @@
 s5=peg$parsepluralStyle();
 if(s5!==peg$FAILED){
 peg$savedPos=s0;
-s1=peg$c21(s5);
+s1=peg$c20(s5);
 s0=s1;
 }else{
 peg$currPos=s0;
@@ -50269,22 +49484,22 @@
 var s0,s1,s2,s3,s4,s5;
 
 s0=peg$currPos;
-if(input.substr(peg$currPos,13)===peg$c22){
-s1=peg$c22;
+if(input.substr(peg$currPos,13)===peg$c21){
+s1=peg$c21;
 peg$currPos+=13;
 }else{
 s1=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c23);}
+if(peg$silentFails===0){peg$fail(peg$c22);}
 }
 if(s1!==peg$FAILED){
 s2=peg$parse_();
 if(s2!==peg$FAILED){
 if(input.charCodeAt(peg$currPos)===44){
-s3=peg$c7;
+s3=peg$c6;
 peg$currPos++;
 }else{
 s3=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c8);}
+if(peg$silentFails===0){peg$fail(peg$c7);}
 }
 if(s3!==peg$FAILED){
 s4=peg$parse_();
@@ -50292,7 +49507,7 @@
 s5=peg$parsepluralStyle();
 if(s5!==peg$FAILED){
 peg$savedPos=s0;
-s1=peg$c24(s5);
+s1=peg$c23(s5);
 s0=s1;
 }else{
 peg$currPos=s0;
@@ -50322,22 +49537,22 @@
 var s0,s1,s2,s3,s4,s5,s6;
 
 s0=peg$currPos;
-if(input.substr(peg$currPos,6)===peg$c25){
-s1=peg$c25;
+if(input.substr(peg$currPos,6)===peg$c24){
+s1=peg$c24;
 peg$currPos+=6;
 }else{
 s1=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c26);}
+if(peg$silentFails===0){peg$fail(peg$c25);}
 }
 if(s1!==peg$FAILED){
 s2=peg$parse_();
 if(s2!==peg$FAILED){
 if(input.charCodeAt(peg$currPos)===44){
-s3=peg$c7;
+s3=peg$c6;
 peg$currPos++;
 }else{
 s3=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c8);}
+if(peg$silentFails===0){peg$fail(peg$c7);}
 }
 if(s3!==peg$FAILED){
 s4=peg$parse_();
@@ -50354,7 +49569,7 @@
 }
 if(s5!==peg$FAILED){
 peg$savedPos=s0;
-s1=peg$c27(s5);
+s1=peg$c26(s5);
 s0=s1;
 }else{
 peg$currPos=s0;
@@ -50386,11 +49601,11 @@
 s0=peg$currPos;
 s1=peg$currPos;
 if(input.charCodeAt(peg$currPos)===61){
-s2=peg$c28;
+s2=peg$c27;
 peg$currPos++;
 }else{
 s2=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c29);}
+if(peg$silentFails===0){peg$fail(peg$c28);}
 }
 if(s2!==peg$FAILED){
 s3=peg$parsenumber();
@@ -50418,7 +49633,7 @@
 }
 
 function peg$parseoptionalFormatPattern(){
-var s0,s1,s2,s3,s4,s5,s6,s7,s8;
+var s0,s1,s2,s3,s4,s5,s6;
 
 s0=peg$currPos;
 s1=peg$parse_();
@@ -50428,29 +49643,25 @@
 s3=peg$parse_();
 if(s3!==peg$FAILED){
 if(input.charCodeAt(peg$currPos)===123){
-s4=peg$c5;
+s4=peg$c4;
 peg$currPos++;
 }else{
 s4=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c6);}
+if(peg$silentFails===0){peg$fail(peg$c5);}
 }
 if(s4!==peg$FAILED){
-s5=peg$parse_();
+s5=peg$parsemessageFormatPattern();
 if(s5!==peg$FAILED){
-s6=peg$parsemessageFormatPattern();
-if(s6!==peg$FAILED){
-s7=peg$parse_();
-if(s7!==peg$FAILED){
 if(input.charCodeAt(peg$currPos)===125){
-s8=peg$c9;
+s6=peg$c8;
 peg$currPos++;
 }else{
-s8=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c10);}
+s6=peg$FAILED;
+if(peg$silentFails===0){peg$fail(peg$c9);}
 }
-if(s8!==peg$FAILED){
+if(s6!==peg$FAILED){
 peg$savedPos=s0;
-s1=peg$c30(s2,s6);
+s1=peg$c29(s2,s5);
 s0=s1;
 }else{
 peg$currPos=s0;
@@ -50476,14 +49687,6 @@
 peg$currPos=s0;
 s0=peg$FAILED;
 }
-}else{
-peg$currPos=s0;
-s0=peg$FAILED;
-}
-}else{
-peg$currPos=s0;
-s0=peg$FAILED;
-}
 
 return s0;
 }
@@ -50492,12 +49695,12 @@
 var s0,s1,s2,s3;
 
 s0=peg$currPos;
-if(input.substr(peg$currPos,7)===peg$c31){
-s1=peg$c31;
+if(input.substr(peg$currPos,7)===peg$c30){
+s1=peg$c30;
 peg$currPos+=7;
 }else{
 s1=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c32);}
+if(peg$silentFails===0){peg$fail(peg$c31);}
 }
 if(s1!==peg$FAILED){
 s2=peg$parse_();
@@ -50505,7 +49708,7 @@
 s3=peg$parsenumber();
 if(s3!==peg$FAILED){
 peg$savedPos=s0;
-s1=peg$c33(s3);
+s1=peg$c32(s3);
 s0=s1;
 }else{
 peg$currPos=s0;
@@ -50546,7 +49749,7 @@
 }
 if(s3!==peg$FAILED){
 peg$savedPos=s0;
-s1=peg$c34(s1,s3);
+s1=peg$c33(s1,s3);
 s0=s1;
 }else{
 peg$currPos=s0;
@@ -50569,22 +49772,22 @@
 
 peg$silentFails++;
 s0=[];
-if(peg$c36.test(input.charAt(peg$currPos))){
+if(peg$c35.test(input.charAt(peg$currPos))){
 s1=input.charAt(peg$currPos);
 peg$currPos++;
 }else{
 s1=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c37);}
+if(peg$silentFails===0){peg$fail(peg$c36);}
 }
 if(s1!==peg$FAILED){
 while(s1!==peg$FAILED){
 s0.push(s1);
-if(peg$c36.test(input.charAt(peg$currPos))){
+if(peg$c35.test(input.charAt(peg$currPos))){
 s1=input.charAt(peg$currPos);
 peg$currPos++;
 }else{
 s1=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c37);}
+if(peg$silentFails===0){peg$fail(peg$c36);}
 }
 }
 }else{
@@ -50593,7 +49796,7 @@
 peg$silentFails--;
 if(s0===peg$FAILED){
 s1=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c35);}
+if(peg$silentFails===0){peg$fail(peg$c34);}
 }
 
 return s0;
@@ -50618,7 +49821,7 @@
 peg$silentFails--;
 if(s0===peg$FAILED){
 s1=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c38);}
+if(peg$silentFails===0){peg$fail(peg$c37);}
 }
 
 return s0;
@@ -50627,12 +49830,12 @@
 function peg$parsedigit(){
 var s0;
 
-if(peg$c39.test(input.charAt(peg$currPos))){
+if(peg$c38.test(input.charAt(peg$currPos))){
 s0=input.charAt(peg$currPos);
 peg$currPos++;
 }else{
 s0=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c40);}
+if(peg$silentFails===0){peg$fail(peg$c39);}
 }
 
 return s0;
@@ -50641,12 +49844,12 @@
 function peg$parsehexDigit(){
 var s0;
 
-if(peg$c41.test(input.charAt(peg$currPos))){
+if(peg$c40.test(input.charAt(peg$currPos))){
 s0=input.charAt(peg$currPos);
 peg$currPos++;
 }else{
 s0=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c42);}
+if(peg$silentFails===0){peg$fail(peg$c41);}
 }
 
 return s0;
@@ -50657,21 +49860,21 @@
 
 s0=peg$currPos;
 if(input.charCodeAt(peg$currPos)===48){
-s1=peg$c43;
+s1=peg$c42;
 peg$currPos++;
 }else{
 s1=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c44);}
+if(peg$silentFails===0){peg$fail(peg$c43);}
 }
 if(s1===peg$FAILED){
 s1=peg$currPos;
 s2=peg$currPos;
-if(peg$c45.test(input.charAt(peg$currPos))){
+if(peg$c44.test(input.charAt(peg$currPos))){
 s3=input.charAt(peg$currPos);
 peg$currPos++;
 }else{
 s3=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c46);}
+if(peg$silentFails===0){peg$fail(peg$c45);}
 }
 if(s3!==peg$FAILED){
 s4=[];
@@ -50699,53 +49902,156 @@
 }
 if(s1!==peg$FAILED){
 peg$savedPos=s0;
-s1=peg$c47(s1);
+s1=peg$c46(s1);
 }
 s0=s1;
 
 return s0;
 }
 
+function peg$parsequoteEscapedChar(){
+var s0,s1,s2;
+
+s0=peg$currPos;
+s1=peg$currPos;
+peg$silentFails++;
+if(input.charCodeAt(peg$currPos)===39){
+s2=peg$c47;
+peg$currPos++;
+}else{
+s2=peg$FAILED;
+if(peg$silentFails===0){peg$fail(peg$c48);}
+}
+if(s2===peg$FAILED){
+if(peg$c49.test(input.charAt(peg$currPos))){
+s2=input.charAt(peg$currPos);
+peg$currPos++;
+}else{
+s2=peg$FAILED;
+if(peg$silentFails===0){peg$fail(peg$c50);}
+}
+}
+peg$silentFails--;
+if(s2===peg$FAILED){
+s1=void 0;
+}else{
+peg$currPos=s1;
+s1=peg$FAILED;
+}
+if(s1!==peg$FAILED){
+if(input.length>peg$currPos){
+s2=input.charAt(peg$currPos);
+peg$currPos++;
+}else{
+s2=peg$FAILED;
+if(peg$silentFails===0){peg$fail(peg$c51);}
+}
+if(s2!==peg$FAILED){
+peg$savedPos=s0;
+s1=peg$c52(s2);
+s0=s1;
+}else{
+peg$currPos=s0;
+s0=peg$FAILED;
+}
+}else{
+peg$currPos=s0;
+s0=peg$FAILED;
+}
+if(s0===peg$FAILED){
+s0=peg$currPos;
+if(input.charCodeAt(peg$currPos)===39){
+s1=peg$c47;
+peg$currPos++;
+}else{
+s1=peg$FAILED;
+if(peg$silentFails===0){peg$fail(peg$c48);}
+}
+if(s1!==peg$FAILED){
+s2=peg$parseescape();
+if(s2!==peg$FAILED){
+peg$savedPos=s0;
+s1=peg$c53(s2);
+s0=s1;
+}else{
+peg$currPos=s0;
+s0=peg$FAILED;
+}
+}else{
+peg$currPos=s0;
+s0=peg$FAILED;
+}
+}
+
+return s0;
+}
+
+function peg$parseapostrophe(){
+var s0;
+
+if(input.charCodeAt(peg$currPos)===39){
+s0=peg$c47;
+peg$currPos++;
+}else{
+s0=peg$FAILED;
+if(peg$silentFails===0){peg$fail(peg$c48);}
+}
+
+return s0;
+}
+
+function peg$parseescape(){
+var s0;
+
+if(peg$c49.test(input.charAt(peg$currPos))){
+s0=input.charAt(peg$currPos);
+peg$currPos++;
+}else{
+s0=peg$FAILED;
+if(peg$silentFails===0){peg$fail(peg$c50);}
+}
+if(s0===peg$FAILED){
+s0=peg$parseapostrophe();
+}
+
+return s0;
+}
+
 function peg$parsechar(){
 var s0,s1,s2,s3,s4,s5,s6,s7;
 
-if(peg$c48.test(input.charAt(peg$currPos))){
+s0=peg$currPos;
+if(input.charCodeAt(peg$currPos)===39){
+s1=peg$c47;
+peg$currPos++;
+}else{
+s1=peg$FAILED;
+if(peg$silentFails===0){peg$fail(peg$c48);}
+}
+if(s1!==peg$FAILED){
+s2=peg$parseapostrophe();
+if(s2!==peg$FAILED){
+peg$savedPos=s0;
+s1=peg$c53(s2);
+s0=s1;
+}else{
+peg$currPos=s0;
+s0=peg$FAILED;
+}
+}else{
+peg$currPos=s0;
+s0=peg$FAILED;
+}
+if(s0===peg$FAILED){
+if(peg$c54.test(input.charAt(peg$currPos))){
 s0=input.charAt(peg$currPos);
 peg$currPos++;
 }else{
 s0=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c49);}
+if(peg$silentFails===0){peg$fail(peg$c55);}
 }
 if(s0===peg$FAILED){
 s0=peg$currPos;
-if(input.substr(peg$currPos,2)===peg$c50){
-s1=peg$c50;
-peg$currPos+=2;
-}else{
-s1=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c51);}
-}
-if(s1!==peg$FAILED){
-peg$savedPos=s0;
-s1=peg$c52();
-}
-s0=s1;
-if(s0===peg$FAILED){
-s0=peg$currPos;
-if(input.substr(peg$currPos,2)===peg$c53){
-s1=peg$c53;
-peg$currPos+=2;
-}else{
-s1=peg$FAILED;
-if(peg$silentFails===0){peg$fail(peg$c54);}
-}
-if(s1!==peg$FAILED){
-peg$savedPos=s0;
-s1=peg$c55();
-}
-s0=s1;
-if(s0===peg$FAILED){
-s0=peg$currPos;
 if(input.substr(peg$currPos,2)===peg$c56){
 s1=peg$c56;
 peg$currPos+=2;
@@ -50782,6 +50088,34 @@
 if(peg$silentFails===0){peg$fail(peg$c63);}
 }
 if(s1!==peg$FAILED){
+peg$savedPos=s0;
+s1=peg$c64();
+}
+s0=s1;
+if(s0===peg$FAILED){
+s0=peg$currPos;
+if(input.substr(peg$currPos,2)===peg$c65){
+s1=peg$c65;
+peg$currPos+=2;
+}else{
+s1=peg$FAILED;
+if(peg$silentFails===0){peg$fail(peg$c66);}
+}
+if(s1!==peg$FAILED){
+peg$savedPos=s0;
+s1=peg$c67();
+}
+s0=s1;
+if(s0===peg$FAILED){
+s0=peg$currPos;
+if(input.substr(peg$currPos,2)===peg$c68){
+s1=peg$c68;
+peg$currPos+=2;
+}else{
+s1=peg$FAILED;
+if(peg$silentFails===0){peg$fail(peg$c69);}
+}
+if(s1!==peg$FAILED){
 s2=peg$currPos;
 s3=peg$currPos;
 s4=peg$parsehexDigit();
@@ -50817,7 +50151,7 @@
 }
 if(s2!==peg$FAILED){
 peg$savedPos=s0;
-s1=peg$c64(s2);
+s1=peg$c70(s2);
 s0=s1;
 }else{
 peg$currPos=s0;
@@ -50832,6 +50166,7 @@
 }
 }
 }
+}
 
 return s0;
 }
@@ -50852,7 +50187,7 @@
 }
 if(s1!==peg$FAILED){
 peg$savedPos=s0;
-s1=peg$c65(s1);
+s1=peg$c3(s1);
 }
 s0=s1;
 
@@ -50865,11 +50200,10 @@
 return peg$result;
 }else{
 if(peg$result!==peg$FAILED&&peg$currPos<input.length){
-peg$fail({type:"end",description:"end of input"});
+peg$fail(peg$endExpectation());
 }
 
-throw peg$buildException(
-null,
+throw peg$buildStructuredError(
 peg$maxFailExpected,
 peg$maxFailPos<input.length?input.charAt(peg$maxFailPos):null,
 peg$maxFailPos<input.length?
@@ -50879,86 +50213,73 @@
 }
 }
 
-return{
+module.exports={
 SyntaxError:peg$SyntaxError,
 parse:peg$parse};
 
-}();
 
-
-},{}],116:[function(require,module,exports){
-
-
-'use strict';
-
-var IntlMessageFormat=require('./lib/main')['default'];
-
-
-
-require('./lib/locales');
-
-
-
-
-exports=module.exports=IntlMessageFormat;
-exports['default']=exports;
-
-},{"./lib/locales":98,"./lib/main":121}],117:[function(require,module,exports){
-
-
-
-
-
-
-
-
+},{}],115:[function(require,module,exports){
 "use strict";
-exports["default"]=Compiler;
 
-function Compiler(locales,formats,pluralFn){
+
+
+
+
+var __extends=this&&this.__extends||function(){
+var extendStatics=function(d,b){
+extendStatics=Object.setPrototypeOf||
+{__proto__:[]}instanceof Array&&function(d,b){d.__proto__=b;}||
+function(d,b){for(var p in b)if(b.hasOwnProperty(p))d[p]=b[p];};
+return extendStatics(d,b);
+};
+return function(d,b){
+extendStatics(d,b);
+function __(){this.constructor=d;}
+d.prototype=b===null?Object.create(b):(__.prototype=b.prototype,new __());
+};
+}();
+Object.defineProperty(exports,"__esModule",{value:true});
+var Compiler=function(){
+function Compiler(locales,formats,formatters){
+this.locales=[];
+this.formats={
+number:{},
+date:{},
+time:{}};
+
+this.pluralNumberFormat=null;
+this.currentPlural=null;
+this.pluralStack=[];
 this.locales=locales;
 this.formats=formats;
-this.pluralFn=pluralFn;
+this.formatters=formatters;
 }
-
 Compiler.prototype.compile=function(ast){
 this.pluralStack=[];
 this.currentPlural=null;
 this.pluralNumberFormat=null;
-
 return this.compileMessage(ast);
 };
-
 Compiler.prototype.compileMessage=function(ast){
+var _this=this;
 if(!(ast&&ast.type==='messageFormatPattern')){
 throw new Error('Message AST is not of type: "messageFormatPattern"');
 }
-
-var elements=ast.elements,
-pattern=[];
-
-var i,len,element;
-
-for(i=0,len=elements.length;i<len;i+=1){
-element=elements[i];
-
-switch(element.type){
-case'messageTextElement':
-pattern.push(this.compileMessageText(element));
-break;
-
-case'argumentElement':
-pattern.push(this.compileArgument(element));
-break;
-
-default:
-throw new Error('Message element does not have a valid type');}
-
+var elements=ast.elements;
+var pattern=elements.
+filter(function(el){
+return el.type==='messageTextElement'||el.type==='argumentElement';
+}).
+map(function(el){
+return el.type==='messageTextElement'?
+_this.compileMessageText(el):
+_this.compileArgument(el);
+});
+if(pattern.length!==elements.length){
+throw new Error('Message element does not have a valid type');
 }
-
 return pattern;
 };
-
 Compiler.prototype.compileMessageText=function(element){
 
 
@@ -50969,531 +50290,412 @@
 if(!this.pluralNumberFormat){
 this.pluralNumberFormat=new Intl.NumberFormat(this.locales);
 }
-
-return new PluralOffsetString(
-this.currentPlural.id,
-this.currentPlural.format.offset,
-this.pluralNumberFormat,
-element.value);
+return new PluralOffsetString(this.currentPlural.id,this.currentPlural.format.offset,this.pluralNumberFormat,element.value);
 }
 
-
 return element.value.replace(/\\#/g,'#');
 };
-
 Compiler.prototype.compileArgument=function(element){
-var format=element.format;
-
+var format=element.format,id=element.id;
+var formatters=this.formatters;
 if(!format){
-return new StringFormat(element.id);
+return new StringFormat(id);
 }
-
-var formats=this.formats,
-locales=this.locales,
-pluralFn=this.pluralFn,
-options;
-
+var _a=this,formats=_a.formats,locales=_a.locales;
 switch(format.type){
 case'numberFormat':
-options=formats.number[format.style];
 return{
-id:element.id,
-format:new Intl.NumberFormat(locales,options).format};
-
+id:id,
+format:formatters.getNumberFormat(locales,formats.number[format.style]).format};
 
 case'dateFormat':
-options=formats.date[format.style];
 return{
-id:element.id,
-format:new Intl.DateTimeFormat(locales,options).format};
-
+id:id,
+format:formatters.getDateTimeFormat(locales,formats.date[format.style]).format};
 
 case'timeFormat':
-options=formats.time[format.style];
 return{
-id:element.id,
-format:new Intl.DateTimeFormat(locales,options).format};
-
+id:id,
+format:formatters.getDateTimeFormat(locales,formats.time[format.style]).format};
 
 case'pluralFormat':
-options=this.compileOptions(element);
-return new PluralFormat(
-element.id,format.ordinal,format.offset,options,pluralFn);
-
+return new PluralFormat(id,format.offset,this.compileOptions(element),formatters.getPluralRules(locales,{
+type:format.ordinal?'ordinal':'cardinal'}));
 
 case'selectFormat':
-options=this.compileOptions(element);
-return new SelectFormat(element.id,options);
-
+return new SelectFormat(id,this.compileOptions(element));
 default:
 throw new Error('Message element does not have a valid format type');}
 
 };
-
 Compiler.prototype.compileOptions=function(element){
-var format=element.format,
-options=format.options,
-optionsHash={};
-
+var _this=this;
+var format=element.format;
+var options=format.options;
 
 
 
 this.pluralStack.push(this.currentPlural);
 this.currentPlural=format.type==='pluralFormat'?element:null;
+var optionsHash=options.reduce(function(all,option){
 
-var i,len,option;
-
-for(i=0,len=options.length;i<len;i+=1){
-option=options[i];
-
-
-optionsHash[option.selector]=this.compileMessage(option.value);
-}
-
+all[option.selector]=_this.compileMessage(option.value);
+return all;
+},{});
 
 this.currentPlural=this.pluralStack.pop();
-
 return optionsHash;
 };
+return Compiler;
+}();
+exports.default=Compiler;
 
-
-
-function StringFormat(id){
+var Formatter=function(){
+function Formatter(id){
 this.id=id;
 }
-
+return Formatter;
+}();
+var StringFormat=function(_super){
+__extends(StringFormat,_super);
+function StringFormat(){
+return _super!==null&&_super.apply(this,arguments)||this;
+}
 StringFormat.prototype.format=function(value){
 if(!value&&typeof value!=='number'){
 return'';
 }
-
 return typeof value==='string'?value:String(value);
 };
-
-function PluralFormat(id,useOrdinal,offset,options,pluralFn){
+return StringFormat;
+}(Formatter);
+var PluralFormat=function(){
+function PluralFormat(id,offset,options,pluralRules){
 this.id=id;
-this.useOrdinal=useOrdinal;
 this.offset=offset;
 this.options=options;
-this.pluralFn=pluralFn;
+this.pluralRules=pluralRules;
 }
-
 PluralFormat.prototype.getOption=function(value){
 var options=this.options;
-
 var option=options['='+value]||
-options[this.pluralFn(value-this.offset,this.useOrdinal)];
-
+options[this.pluralRules.select(value-this.offset)];
 return option||options.other;
 };
-
+return PluralFormat;
+}();
+var PluralOffsetString=function(_super){
+__extends(PluralOffsetString,_super);
 function PluralOffsetString(id,offset,numberFormat,string){
-this.id=id;
-this.offset=offset;
-this.numberFormat=numberFormat;
-this.string=string;
+var _this=_super.call(this,id)||this;
+_this.offset=offset;
+_this.numberFormat=numberFormat;
+_this.string=string;
+return _this;
 }
-
 PluralOffsetString.prototype.format=function(value){
 var number=this.numberFormat.format(value-this.offset);
-
 return this.string.
 replace(/(^|[^\\])#/g,'$1'+number).
 replace(/\\#/g,'#');
 };
-
+return PluralOffsetString;
+}(Formatter);
+exports.PluralOffsetString=PluralOffsetString;
+var SelectFormat=function(){
 function SelectFormat(id,options){
 this.id=id;
 this.options=options;
 }
-
 SelectFormat.prototype.getOption=function(value){
 var options=this.options;
 return options[value]||options.other;
 };
-
-
-},{}],118:[function(require,module,exports){
-
-
-
-
-
-
-
-
-"use strict";
-var src$utils$$=require("./utils"),src$es5$$=require("./es5"),src$compiler$$=require("./compiler"),intl$messageformat$parser$$=require("intl-messageformat-parser");
-exports["default"]=MessageFormat;
-
-
-
-function MessageFormat(message,locales,formats){
-
-var ast=typeof message==='string'?
-MessageFormat.__parse(message):message;
-
-if(!(ast&&ast.type==='messageFormatPattern')){
-throw new TypeError('A message must be provided as a String or AST.');
+return SelectFormat;
+}();
+exports.SelectFormat=SelectFormat;
+function isSelectOrPluralFormat(f){
+return!!f.options;
 }
+exports.isSelectOrPluralFormat=isSelectOrPluralFormat;
 
-
-
-formats=this._mergeFormats(MessageFormat.formats,formats);
-
-
-src$es5$$.defineProperty(this,'_locale',{value:this._resolveLocale(locales)});
+},{}],116:[function(require,module,exports){
+"use strict";
 
 
 
 
-var pluralFn=this._findPluralRuleFunction(this._locale);
-var pattern=this._compilePattern(ast,locales,formats,pluralFn);
+
+var __extends=this&&this.__extends||function(){
+var extendStatics=function(d,b){
+extendStatics=Object.setPrototypeOf||
+{__proto__:[]}instanceof Array&&function(d,b){d.__proto__=b;}||
+function(d,b){for(var p in b)if(b.hasOwnProperty(p))d[p]=b[p];};
+return extendStatics(d,b);
+};
+return function(d,b){
+extendStatics(d,b);
+function __(){this.constructor=d;}
+d.prototype=b===null?Object.create(b):(__.prototype=b.prototype,new __());
+};
+}();
+var __assign=this&&this.__assign||function(){
+__assign=Object.assign||function(t){
+for(var s,i=1,n=arguments.length;i<n;i++){
+s=arguments[i];
+for(var p in s)if(Object.prototype.hasOwnProperty.call(s,p))
+t[p]=s[p];
+}
+return t;
+};
+return __assign.apply(this,arguments);
+};
+Object.defineProperty(exports,"__esModule",{value:true});
+
+var compiler_1=require("./compiler");
+
+function resolveLocale(locales){
+if(typeof locales==='string'){
+locales=[locales];
+}
+try{
+return Intl.NumberFormat.supportedLocalesOf(locales,{
+
+
+localeMatcher:'best fit'})[
+0];
+}
+catch(e){
+return IntlMessageFormat.defaultLocale;
+}
+}
+function formatPatterns(pattern,values){
+var result='';
+for(var _i=0,pattern_1=pattern;_i<pattern_1.length;_i++){
+var part=pattern_1[_i];
+
+if(typeof part==='string'){
+result+=part;
+continue;
+}
+var id=part.id;
+
+if(!(values&&id in values)){
+throw new FormatError("A value must be provided for: "+id,id);
+}
+var value=values[id];
 
 
 
-var messageFormat=this;
+if(compiler_1.isSelectOrPluralFormat(part)){
+result+=formatPatterns(part.getOption(value),values);
+}else
+{
+result+=part.format(value);
+}
+}
+return result;
+}
+function mergeConfig(c1,c2){
+if(!c2){
+return c1;
+}
+return __assign({},c1||{},c2||{},Object.keys(c1).reduce(function(all,k){
+all[k]=__assign({},c1[k],c2[k]||{});
+return all;
+},{}));
+}
+function mergeConfigs(defaultConfig,configs){
+if(!configs){
+return defaultConfig;
+}
+return Object.keys(defaultConfig).reduce(function(all,k){
+all[k]=mergeConfig(defaultConfig[k],configs[k]);
+return all;
+},__assign({},defaultConfig));
+}
+var FormatError=function(_super){
+__extends(FormatError,_super);
+function FormatError(msg,variableId){
+var _this=_super.call(this,msg)||this;
+_this.variableId=variableId;
+return _this;
+}
+return FormatError;
+}(Error);
+function createDefaultFormatters(){
+return{
+getNumberFormat:function(){
+var _a;
+var args=[];
+for(var _i=0;_i<arguments.length;_i++){
+args[_i]=arguments[_i];
+}
+return new((_a=Intl.NumberFormat).bind.apply(_a,[void 0].concat(args)))();
+},
+getDateTimeFormat:function(){
+var _a;
+var args=[];
+for(var _i=0;_i<arguments.length;_i++){
+args[_i]=arguments[_i];
+}
+return new((_a=Intl.DateTimeFormat).bind.apply(_a,[void 0].concat(args)))();
+},
+getPluralRules:function(){
+var _a;
+var args=[];
+for(var _i=0;_i<arguments.length;_i++){
+args[_i]=arguments[_i];
+}
+return new((_a=Intl.PluralRules).bind.apply(_a,[void 0].concat(args)))();
+}};
+
+}
+exports.createDefaultFormatters=createDefaultFormatters;
+var IntlMessageFormat=function(){
+function IntlMessageFormat(message,locales,overrideFormats,opts){
+var _this=this;
+if(locales===void 0){locales=IntlMessageFormat.defaultLocale;}
 this.format=function(values){
 try{
-return messageFormat._format(pattern,values);
-}catch(e){
+return formatPatterns(_this.pattern,values);
+}
+catch(e){
 if(e.variableId){
-throw new Error(
-'The intl string context variable \''+e.variableId+'\''+
-' was not provided to the string \''+message+'\'');
-
-}else{
+throw new Error("The intl string context variable '"+e.variableId+"' was not provided to the string '"+_this.message+"'");
+}else
+{
 throw e;
 }
 }
 };
+if(typeof message==='string'){
+if(!IntlMessageFormat.__parse){
+throw new TypeError('IntlMessageFormat.__parse must be set to process `message` of type `string`');
+}
+
+this.ast=IntlMessageFormat.__parse(message);
+}else
+{
+this.ast=message;
+}
+this.message=message;
+if(!(this.ast&&this.ast.type==='messageFormatPattern')){
+throw new TypeError('A message must be provided as a String or AST.');
 }
 
 
+var formats=mergeConfigs(IntlMessageFormat.formats,overrideFormats);
+
+this.locale=resolveLocale(locales||[]);
+var formatters=opts&&opts.formatters||createDefaultFormatters();
 
 
-src$es5$$.defineProperty(MessageFormat,'formats',{
-enumerable:true,
 
-value:{
+this.pattern=new compiler_1.default(locales,formats,formatters).compile(this.ast);
+
+
+}
+IntlMessageFormat.prototype.resolvedOptions=function(){
+return{locale:this.locale};
+};
+IntlMessageFormat.prototype.getAst=function(){
+return this.ast;
+};
+IntlMessageFormat.defaultLocale='en';
+IntlMessageFormat.__parse=undefined;
+
+
+
+IntlMessageFormat.formats={
 number:{
-'currency':{
+currency:{
 style:'currency'},
 
-
-'percent':{
+percent:{
 style:'percent'}},
 
 
-
 date:{
-'short':{
+short:{
 month:'numeric',
 day:'numeric',
 year:'2-digit'},
 
-
-'medium':{
+medium:{
 month:'short',
 day:'numeric',
 year:'numeric'},
 
-
-'long':{
+long:{
 month:'long',
 day:'numeric',
 year:'numeric'},
 
-
-'full':{
+full:{
 weekday:'long',
 month:'long',
 day:'numeric',
 year:'numeric'}},
 
 
-
 time:{
-'short':{
+short:{
 hour:'numeric',
 minute:'numeric'},
 
-
-'medium':{
+medium:{
 hour:'numeric',
 minute:'numeric',
 second:'numeric'},
 
-
-'long':{
+long:{
 hour:'numeric',
 minute:'numeric',
 second:'numeric',
 timeZoneName:'short'},
 
-
-'full':{
+full:{
 hour:'numeric',
 minute:'numeric',
 second:'numeric',
-timeZoneName:'short'}}}});
+timeZoneName:'short'}}};
 
 
 
-
-
-
-src$es5$$.defineProperty(MessageFormat,'__localeData__',{value:src$es5$$.objCreate(null)});
-src$es5$$.defineProperty(MessageFormat,'__addLocaleData',{value:function(data){
-if(!(data&&data.locale)){
-throw new Error(
-'Locale data provided to IntlMessageFormat is missing a '+
-'`locale` property');
-
-}
-
-MessageFormat.__localeData__[data.locale.toLowerCase()]=data;
-}});
-
-
-src$es5$$.defineProperty(MessageFormat,'__parse',{value:intl$messageformat$parser$$["default"].parse});
-
-
-
-src$es5$$.defineProperty(MessageFormat,'defaultLocale',{
-enumerable:true,
-writable:true,
-value:undefined});
-
-
-MessageFormat.prototype.resolvedOptions=function(){
-
-return{
-locale:this._locale};
-
-};
-
-MessageFormat.prototype._compilePattern=function(ast,locales,formats,pluralFn){
-var compiler=new src$compiler$$["default"](locales,formats,pluralFn);
-return compiler.compile(ast);
-};
-
-MessageFormat.prototype._findPluralRuleFunction=function(locale){
-var localeData=MessageFormat.__localeData__;
-var data=localeData[locale.toLowerCase()];
-
-
-
-while(data){
-if(data.pluralRuleFunction){
-return data.pluralRuleFunction;
-}
-
-data=data.parentLocale&&localeData[data.parentLocale.toLowerCase()];
-}
-
-throw new Error(
-'Locale data added to IntlMessageFormat is missing a '+
-'`pluralRuleFunction` for :'+locale);
-
-};
-
-MessageFormat.prototype._format=function(pattern,values){
-var result='',
-i,len,part,id,value,err;
-
-for(i=0,len=pattern.length;i<len;i+=1){
-part=pattern[i];
-
-
-if(typeof part==='string'){
-result+=part;
-continue;
-}
-
-id=part.id;
-
-
-if(!(values&&src$utils$$.hop.call(values,id))){
-err=new Error('A value must be provided for: '+id);
-err.variableId=id;
-throw err;
-}
-
-value=values[id];
-
-
-
-
-if(part.options){
-result+=this._format(part.getOption(value),values);
-}else{
-result+=part.format(value);
-}
-}
-
-return result;
-};
-
-MessageFormat.prototype._mergeFormats=function(defaults,formats){
-var mergedFormats={},
-type,mergedType;
-
-for(type in defaults){
-if(!src$utils$$.hop.call(defaults,type)){continue;}
-
-mergedFormats[type]=mergedType=src$es5$$.objCreate(defaults[type]);
-
-if(formats&&src$utils$$.hop.call(formats,type)){
-src$utils$$.extend(mergedType,formats[type]);
-}
-}
-
-return mergedFormats;
-};
-
-MessageFormat.prototype._resolveLocale=function(locales){
-if(typeof locales==='string'){
-locales=[locales];
-}
-
-
-locales=(locales||[]).concat(MessageFormat.defaultLocale);
-
-var localeData=MessageFormat.__localeData__;
-var i,len,localeParts,data;
-
-
-
-
-
-
-for(i=0,len=locales.length;i<len;i+=1){
-localeParts=locales[i].toLowerCase().split('-');
-
-while(localeParts.length){
-data=localeData[localeParts.join('-')];
-if(data){
-
-
-return data.locale;
-}
-
-localeParts.pop();
-}
-}
-
-var defaultLocale=locales.pop();
-throw new Error(
-'No locale data has been added to IntlMessageFormat for: '+
-locales.join(', ')+', or the default locale: '+defaultLocale);
-
-};
-
-
-},{"./compiler":117,"./es5":120,"./utils":122,"intl-messageformat-parser":114}],119:[function(require,module,exports){
-
-"use strict";
-exports["default"]={"locale":"en","pluralRuleFunction":function(n,ord){var s=String(n).split("."),v0=!s[1],t0=Number(s[0])==n,n10=t0&&s[0].slice(-1),n100=t0&&s[0].slice(-2);if(ord)return n10==1&&n100!=11?"one":n10==2&&n100!=12?"two":n10==3&&n100!=13?"few":"other";return n==1&&v0?"one":"other";}};
-
-
-},{}],120:[function(require,module,exports){
-
-
-
-
-
-
-
-
-"use strict";
-var src$utils$$=require("./utils");
-
-
-
-
-var realDefineProp=function(){
-try{return!!Object.defineProperty({},'a',{});}
-catch(e){return false;}
+return IntlMessageFormat;
 }();
+exports.IntlMessageFormat=IntlMessageFormat;
+exports.default=IntlMessageFormat;
 
-var es3=!realDefineProp&&!Object.prototype.__defineGetter__;
-
-var defineProperty=realDefineProp?Object.defineProperty:
-function(obj,name,desc){
-
-if('get'in desc&&obj.__defineGetter__){
-obj.__defineGetter__(name,desc.get);
-}else if(!src$utils$$.hop.call(obj,name)||'value'in desc){
-obj[name]=desc.value;
-}
-};
-
-var objCreate=Object.create||function(proto,props){
-var obj,k;
-
-function F(){}
-F.prototype=proto;
-obj=new F();
-
-for(k in props){
-if(src$utils$$.hop.call(props,k)){
-defineProperty(obj,k,props[k]);
-}
-}
-
-return obj;
-};
-
-exports.defineProperty=defineProperty,exports.objCreate=objCreate;
-
-
-},{"./utils":122}],121:[function(require,module,exports){
-
-
+},{"./compiler":115}],117:[function(require,module,exports){
 "use strict";
-var src$core$$=require("./core"),src$en$$=require("./en");
-
-src$core$$["default"].__addLocaleData(src$en$$["default"]);
-src$core$$["default"].defaultLocale='en';
-
-exports["default"]=src$core$$["default"];
-
-
-},{"./core":118,"./en":119}],122:[function(require,module,exports){
 
 
 
 
 
-
-
-
-"use strict";
-exports.extend=extend;
-var hop=Object.prototype.hasOwnProperty;
-
-function extend(obj){
-var sources=Array.prototype.slice.call(arguments,1),
-i,len,source,key;
-
-for(i=0,len=sources.length;i<len;i+=1){
-source=sources[i];
-if(!source){continue;}
-
-for(key in source){
-if(hop.call(source,key)){
-obj[key]=source[key];
+function __export(m){
+for(var p in m)if(!exports.hasOwnProperty(p))exports[p]=m[p];
 }
-}
-}
+Object.defineProperty(exports,"__esModule",{value:true});
+var intl_messageformat_parser_1=require("intl-messageformat-parser");
+var core_1=require("./core");
+core_1.default.__parse=intl_messageformat_parser_1.default.parse;
+__export(require("./core"));
+exports.default=core_1.default;
 
-return obj;
-}
-exports.hop=hop;
+},{"./core":116,"intl-messageformat-parser":113}],118:[function(require,module,exports){
+'use strict';
+var IntlMessageFormat=require('./dist').default;
 
 
-},{}],123:[function(require,module,exports){
+
+
+exports=module.exports=IntlMessageFormat;
+exports['default']=exports;
+
+},{"./dist":117}],119:[function(require,module,exports){
 
 
 
@@ -51516,14 +50718,14 @@
 return typeof obj.readFloatLE==='function'&&typeof obj.slice==='function'&&isBuffer(obj.slice(0,0));
 }
 
-},{}],124:[function(require,module,exports){
+},{}],120:[function(require,module,exports){
 var toString={}.toString;
 
 module.exports=Array.isArray||function(arr){
 return toString.call(arr)=='[object Array]';
 };
 
-},{}],125:[function(require,module,exports){
+},{}],121:[function(require,module,exports){
 var encode=require('./lib/encoder'),
 decode=require('./lib/decoder');
 
@@ -51532,7 +50734,7 @@
 decode:decode};
 
 
-},{"./lib/decoder":126,"./lib/encoder":127}],126:[function(require,module,exports){
+},{"./lib/decoder":122,"./lib/encoder":123}],122:[function(require,module,exports){
 (function(Buffer){
 
 
@@ -52522,7 +51724,7 @@
 }
 
 }).call(this,require("buffer").Buffer);
-},{"buffer":102}],127:[function(require,module,exports){
+},{"buffer":101}],123:[function(require,module,exports){
 (function(Buffer){
 
 
@@ -53292,7 +52494,7 @@
 }
 
 }).call(this,require("buffer").Buffer);
-},{"buffer":102}],128:[function(require,module,exports){
+},{"buffer":101}],124:[function(require,module,exports){
 (function(process){
 
 
@@ -53537,7 +52739,7 @@
 module.exports=Log;
 
 }).call(this,require('_process'));
-},{"_process":145,"debug":106,"events":108,"marky":131}],129:[function(require,module,exports){
+},{"_process":141,"debug":105,"events":107,"marky":127}],125:[function(require,module,exports){
 (function(global){
 
 
@@ -55389,7 +54591,7 @@
 module.exports=isEqual;
 
 }).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{});
-},{}],130:[function(require,module,exports){
+},{}],126:[function(require,module,exports){
 
 
 
@@ -55406,7 +54608,7 @@
 }
 };
 
-},{}],131:[function(require,module,exports){
+},{}],127:[function(require,module,exports){
 'use strict';
 
 Object.defineProperty(exports,'__esModule',{value:true});
@@ -55486,7 +54688,7 @@
 exports.clear=function(){entries=[];};
 }
 
-},{}],132:[function(require,module,exports){
+},{}],128:[function(require,module,exports){
 exports.getRenderingDataFromViewport=function(viewportProperties,uaDeviceWidth,uaDeviceHeight,uaMaxZoom,uaMinZoom){
 
 var vw=uaDeviceWidth/100;
@@ -55829,7 +55031,7 @@
 "viewport-fit":["auto","cover"]};
 
 
-},{}],133:[function(require,module,exports){
+},{}],129:[function(require,module,exports){
 
 
 
@@ -55983,7 +55185,7 @@
 return Math.ceil(ms/n)+' '+name+'s';
 }
 
-},{}],134:[function(require,module,exports){
+},{}],130:[function(require,module,exports){
 'use strict';
 
 
@@ -56090,7 +55292,7 @@
 
 exports.setTyped(TYPED_OK);
 
-},{}],135:[function(require,module,exports){
+},{}],131:[function(require,module,exports){
 'use strict';
 
 
@@ -56143,7 +55345,7 @@
 
 module.exports=adler32;
 
-},{}],136:[function(require,module,exports){
+},{}],132:[function(require,module,exports){
 'use strict';
 
 
@@ -56213,7 +55415,7 @@
 
 
 
-},{}],137:[function(require,module,exports){
+},{}],133:[function(require,module,exports){
 'use strict';
 
 
@@ -56274,7 +55476,7 @@
 
 module.exports=crc32;
 
-},{}],138:[function(require,module,exports){
+},{}],134:[function(require,module,exports){
 'use strict';
 
 
@@ -58150,7 +57352,7 @@
 
 
 
-},{"../utils/common":134,"./adler32":135,"./crc32":137,"./messages":139,"./trees":140}],139:[function(require,module,exports){
+},{"../utils/common":130,"./adler32":131,"./crc32":133,"./messages":135,"./trees":136}],135:[function(require,module,exports){
 'use strict';
 
 
@@ -58184,7 +57386,7 @@
 '-6':'incompatible version'};
 
 
-},{}],140:[function(require,module,exports){
+},{}],136:[function(require,module,exports){
 'use strict';
 
 
@@ -59408,7 +58610,7 @@
 exports._tr_tally=_tr_tally;
 exports._tr_align=_tr_align;
 
-},{"../utils/common":134}],141:[function(require,module,exports){
+},{"../utils/common":130}],137:[function(require,module,exports){
 'use strict';
 
 
@@ -59457,7 +58659,7 @@
 
 module.exports=ZStream;
 
-},{}],142:[function(require,module,exports){
+},{}],138:[function(require,module,exports){
 module.exports=function parseCacheControl(field){
 
 if(typeof field!=='string'){
@@ -59496,7 +58698,7 @@
 return err?null:header;
 };
 
-},{}],143:[function(require,module,exports){
+},{}],139:[function(require,module,exports){
 (function(process){
 
 
@@ -59802,7 +59004,7 @@
 
 
 }).call(this,require('_process'));
-},{"_process":145}],144:[function(require,module,exports){
+},{"_process":141}],140:[function(require,module,exports){
 (function(process){
 'use strict';
 
@@ -59850,7 +59052,7 @@
 
 
 }).call(this,require('_process'));
-},{"_process":145}],145:[function(require,module,exports){
+},{"_process":141}],141:[function(require,module,exports){
 
 var process=module.exports={};
 
@@ -60036,7 +59238,7 @@
 };
 process.umask=function(){return 0;};
 
-},{}],146:[function(require,module,exports){
+},{}],142:[function(require,module,exports){
 
 
 
@@ -60122,7 +59324,7 @@
 return Object.prototype.toString.call(xs)==='[object Array]';
 };
 
-},{}],147:[function(require,module,exports){
+},{}],143:[function(require,module,exports){
 
 
 
@@ -60209,16 +59411,16 @@
 return res;
 };
 
-},{}],148:[function(require,module,exports){
+},{}],144:[function(require,module,exports){
 'use strict';
 
 exports.decode=exports.parse=require('./decode');
 exports.encode=exports.stringify=require('./encode');
 
-},{"./decode":146,"./encode":147}],149:[function(require,module,exports){
+},{"./decode":142,"./encode":143}],145:[function(require,module,exports){
 module.exports=require('./lib/_stream_duplex.js');
 
-},{"./lib/_stream_duplex.js":150}],150:[function(require,module,exports){
+},{"./lib/_stream_duplex.js":146}],146:[function(require,module,exports){
 
 
 
@@ -60350,7 +59552,7 @@
 
 pna.nextTick(cb,err);
 };
-},{"./_stream_readable":152,"./_stream_writable":154,"core-util-is":103,"inherits":113,"process-nextick-args":144}],151:[function(require,module,exports){
+},{"./_stream_readable":148,"./_stream_writable":150,"core-util-is":102,"inherits":112,"process-nextick-args":140}],147:[function(require,module,exports){
 
 
 
@@ -60398,7 +59600,7 @@
 PassThrough.prototype._transform=function(chunk,encoding,cb){
 cb(null,chunk);
 };
-},{"./_stream_transform":153,"core-util-is":103,"inherits":113}],152:[function(require,module,exports){
+},{"./_stream_transform":149,"core-util-is":102,"inherits":112}],148:[function(require,module,exports){
 (function(process,global){
 
 
@@ -61420,7 +60622,7 @@
 return-1;
 }
 }).call(this,require('_process'),typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{});
-},{"./_stream_duplex":150,"./internal/streams/BufferList":155,"./internal/streams/destroy":156,"./internal/streams/stream":157,"_process":145,"core-util-is":103,"events":108,"inherits":113,"isarray":124,"process-nextick-args":144,"safe-buffer":165,"string_decoder/":158,"util":98}],153:[function(require,module,exports){
+},{"./_stream_duplex":146,"./internal/streams/BufferList":151,"./internal/streams/destroy":152,"./internal/streams/stream":153,"_process":141,"core-util-is":102,"events":107,"inherits":112,"isarray":120,"process-nextick-args":140,"safe-buffer":161,"string_decoder/":154,"util":97}],149:[function(require,module,exports){
 
 
 
@@ -61635,7 +60837,7 @@
 
 return stream.push(null);
 }
-},{"./_stream_duplex":150,"core-util-is":103,"inherits":113}],154:[function(require,module,exports){
+},{"./_stream_duplex":146,"core-util-is":102,"inherits":112}],150:[function(require,module,exports){
 (function(process,global,setImmediate){
 
 
@@ -62325,7 +61527,7 @@
 cb(err);
 };
 }).call(this,require('_process'),typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{},require("timers").setImmediate);
-},{"./_stream_duplex":150,"./internal/streams/destroy":156,"./internal/streams/stream":157,"_process":145,"core-util-is":103,"inherits":113,"process-nextick-args":144,"safe-buffer":165,"timers":175,"util-deprecate":176}],155:[function(require,module,exports){
+},{"./_stream_duplex":146,"./internal/streams/destroy":152,"./internal/streams/stream":153,"_process":141,"core-util-is":102,"inherits":112,"process-nextick-args":140,"safe-buffer":161,"timers":171,"util-deprecate":172}],151:[function(require,module,exports){
 'use strict';
 
 function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function");}}
@@ -62405,7 +61607,7 @@
 return this.constructor.name+' '+obj;
 };
 }
-},{"safe-buffer":165,"util":98}],156:[function(require,module,exports){
+},{"safe-buffer":161,"util":97}],152:[function(require,module,exports){
 'use strict';
 
 
@@ -62480,10 +61682,10 @@
 destroy:destroy,
 undestroy:undestroy};
 
-},{"process-nextick-args":144}],157:[function(require,module,exports){
+},{"process-nextick-args":140}],153:[function(require,module,exports){
 module.exports=require('events').EventEmitter;
 
-},{"events":108}],158:[function(require,module,exports){
+},{"events":107}],154:[function(require,module,exports){
 
 
 
@@ -62780,10 +61982,10 @@
 function simpleEnd(buf){
 return buf&&buf.length?this.write(buf):'';
 }
-},{"safe-buffer":165}],159:[function(require,module,exports){
+},{"safe-buffer":161}],155:[function(require,module,exports){
 module.exports=require('./readable').PassThrough;
 
-},{"./readable":160}],160:[function(require,module,exports){
+},{"./readable":156}],156:[function(require,module,exports){
 exports=module.exports=require('./lib/_stream_readable.js');
 exports.Stream=exports;
 exports.Readable=exports;
@@ -62792,13 +61994,13 @@
 exports.Transform=require('./lib/_stream_transform.js');
 exports.PassThrough=require('./lib/_stream_passthrough.js');
 
-},{"./lib/_stream_duplex.js":150,"./lib/_stream_passthrough.js":151,"./lib/_stream_readable.js":152,"./lib/_stream_transform.js":153,"./lib/_stream_writable.js":154}],161:[function(require,module,exports){
+},{"./lib/_stream_duplex.js":146,"./lib/_stream_passthrough.js":147,"./lib/_stream_readable.js":148,"./lib/_stream_transform.js":149,"./lib/_stream_writable.js":150}],157:[function(require,module,exports){
 module.exports=require('./readable').Transform;
 
-},{"./readable":160}],162:[function(require,module,exports){
+},{"./readable":156}],158:[function(require,module,exports){
 module.exports=require('./lib/_stream_writable.js');
 
-},{"./lib/_stream_writable.js":154}],163:[function(require,module,exports){
+},{"./lib/_stream_writable.js":150}],159:[function(require,module,exports){
 var URL=require('url').URL;
 
 
@@ -63221,13 +62423,13 @@
 
 module.exports=Robots;
 
-},{"url":"url"}],164:[function(require,module,exports){
+},{"url":"url"}],160:[function(require,module,exports){
 var Robots=require('./Robots');
 
 module.exports=function(url,contents){
 return new Robots(url,contents);
 };
-},{"./Robots":163}],165:[function(require,module,exports){
+},{"./Robots":159}],161:[function(require,module,exports){
 
 var buffer=require('buffer');
 var Buffer=buffer.Buffer;
@@ -63291,7 +62493,7 @@
 return buffer.SlowBuffer(size);
 };
 
-},{"buffer":102}],166:[function(require,module,exports){
+},{"buffer":101}],162:[function(require,module,exports){
 (function(process){
 exports=module.exports=SemVer;
 
@@ -64778,7 +63980,7 @@
 }
 
 }).call(this,require('_process'));
-},{"_process":145}],167:[function(require,module,exports){
+},{"_process":141}],163:[function(require,module,exports){
 (function(Buffer){
 'use strict';
 
@@ -65020,7 +64222,7 @@
 
 
 }).call(this,require("buffer").Buffer);
-},{"buffer":102,"fs":101,"jpeg-js":125}],168:[function(require,module,exports){
+},{"buffer":101,"fs":100,"jpeg-js":121}],164:[function(require,module,exports){
 'use strict';
 
 const frame=require('./frame');
@@ -65090,7 +64292,7 @@
 });
 };
 
-},{"./frame":167,"./speed-index":169}],169:[function(require,module,exports){
+},{"./frame":163,"./speed-index":165}],165:[function(require,module,exports){
 'use strict';
 
 const imageSSIM=require('image-ssim');
@@ -65362,7 +64564,7 @@
 calculateSpeedIndexes};
 
 
-},{"image-ssim":112}],170:[function(require,module,exports){
+},{"image-ssim":111}],166:[function(require,module,exports){
 
 
 
@@ -65491,12 +64693,12 @@
 return dest;
 };
 
-},{"events":108,"inherits":113,"readable-stream/duplex.js":149,"readable-stream/passthrough.js":159,"readable-stream/readable.js":160,"readable-stream/transform.js":161,"readable-stream/writable.js":162}],171:[function(require,module,exports){
+},{"events":107,"inherits":112,"readable-stream/duplex.js":145,"readable-stream/passthrough.js":155,"readable-stream/readable.js":156,"readable-stream/transform.js":157,"readable-stream/writable.js":158}],167:[function(require,module,exports){
 module.exports=[{"name":"Google Analytics","company":"Google","homepage":"https://www.google.com/analytics/analytics/","categories":["analytics"],"domains":["www.google-analytics.com","ssl.google-analytics.com","google-analytics.com","urchin.com"]},{"name":"Facebook","homepage":"https://www.facebook.com","categories":["social"],"domains":["www.facebook.com","connect.facebook.net","staticxx.facebook.com","static.xx.fbcdn.net","m.facebook.com","atlassbx.com","fbcdn-photos-e-a.akamaihd.net","23.62.3.183","akamai.net","akamaiedge.net","akamaitechnologies.com","akamaitechnologies.fr","akamaized.net","edgefcs.net","edgekey.net","edgesuite.net","srip.net","cquotient.com","demandware.net","platform-lookaside.fbsbx.com"]},{"name":"Google CDN","company":"Google","homepage":"https://developers.google.com/speed/libraries/","categories":["library"],"domains":["ajax.googleapis.com","www.gstatic.com","commondatastorage.googleapis.com"]},{"name":"Google/Doubleclick Ads","company":"Google","homepage":"https://www.doubleclickbygoogle.com/","categories":["ad"],"domains":["pagead2.googlesyndication.com","tpc.googlesyndication.com","googleads.g.doubleclick.net","securepubads.g.doubleclick.net","cm.g.doubleclick.net","s0.2mdn.net","stats.g.doubleclick.net","survey.g.doubleclick.net","fls.doubleclick.net","ad.doubleclick.net","www.googleadservices.com","adservice.google.se","adservice.google.com","adservice.google.de","www.googletagservices.com"]},{"name":"Google Tag Manager","company":"Google","homepage":"https://marketingplatform.google.com/about/tag-manager/","categories":["tag-manager"],"domains":["www.googletagmanager.com"]},{"name":"Other Google APIs/SDKs","company":"Google","homepage":"https://developers.google.com/apis-explorer/#p/","categories":["utility"],"domains":["www.google.com","apis.google.com","cse.google.com","translate.googleapis.com","storage.googleapis.com","imasdk.googleapis.com","lh3.googleusercontent.com","calendar.google.com","pay.google.com","news.google.com","payments.google.com","clients2.google.com"]},{"name":"Twitter","homepage":"https://twitter.com","categories":["social"],"domains":["platform.twitter.com","cdn.syndication.twimg.com","twitpic.com","vine.co"]},{"name":"Yandex Metrica","company":"Yandex","homepage":"https://metrica.yandex.com/about?","categories":["analytics"],"domains":["mc.yandex.ru","d31j93rd8oukbv.cloudfront.net"]},{"name":"jQuery CDN","homepage":"https://code.jquery.com/","categories":["library"],"domains":["code.jquery.com"]},{"name":"AddThis","homepage":"http://www.addthis.com/","categories":["social"],"domains":["s7.addthis.com","addthiscdn.com","addthisedge.com","r.dlx.addthis.com","su.addthis.com","x.dlx.addthis.com"]},{"name":"Google Maps","company":"Google","homepage":"https://www.google.com/maps","categories":["utility"],"domains":["maps-api-ssl.google.com","maps.google.com","maps.gstatic.com","maps.googleapis.com","mts.googleapis.com"]},{"name":"Hotjar","homepage":"https://www.hotjar.com/","categories":["analytics"],"domains":["script.hotjar.com","static.hotjar.com","in.hotjar.com","vc.hotjar.io","vars.hotjar.com"]},{"name":"Cloudflare CDN","homepage":"https://cdnjs.com/","categories":["library"],"domains":["cdnjs.cloudflare.com","amp.cloudflare.com"]},{"name":"WordPress","company":"Automattic","homepage":"https://wp.com/","categories":["hosting"],"domains":["s0.wp.com","s2.wp.com","s.w.org","wordpress.com","stats.wp.com"]},{"name":"Shopify","homepage":"https://www.shopify.com/","categories":["hosting"],"domains":["cdn.shopify.com","productreviews.shopifycdn.com","shopifyapps.com"]},{"name":"Criteo","homepage":"https://www.criteo.com/","categories":["ad"],"domains":["static.criteo.net","bidder.criteo.com","emailretargeting.com"]},{"name":"ZenDesk","homepage":"https://zendesk.com/","categories":["customer-success"],"domains":["assets.zendesk.com","static.zdassets.com","v2.zopim.com"]},{"name":"Tawk.to","homepage":"https://www.tawk.to/","categories":["customer-success"],"domains":["embed.tawk.to"]},{"name":"AMP","homepage":"https://www.ampproject.org/","categories":["content"],"domains":["cdn.ampproject.org"]},{"name":"Wix","homepage":"https://www.wix.com/","categories":["hosting"],"domains":["static.parastorage.com","static.wixstatic.com","www.wix.com"]},{"name":"Squarespace","homepage":"https://www.squarespace.com/","categories":["hosting"],"domains":["static.squarespace.com","static1.squarespace.com"]},{"name":"YouTube","homepage":"https://youtube.com","categories":["video"],"domains":["www.youtube.com","s.ytimg.com","yt3.ggpht.com","youtube-nocookie.com"]},{"name":"Jivochat","homepage":"https://www.jivochat.com/","categories":["customer-success"],"domains":["cdn-ca.jivosite.com","code.jivosite.com"]},{"name":"Amazon Web Services","homepage":"https://aws.amazon.com/s3/","categories":["other"],"domains":["s3.amazonaws.com","amazonwebservices.com","api-cdn.amazon.com","ecx.images-amazon.com","elasticbeanstalk.com","amazonwebapps.com","ws.amazon.co.uk","payments-amazon.com"]},{"name":"Adobe Tag Manager","company":"Adobe","homepage":"https://www.adobe.com/experience-platform/","categories":["tag-manager"],"domains":["assets.adobedtm.com","dpm.demdex.net","sync-tm.everesttech.net"]},{"name":"PIXNET","homepage":"https://www.pixnet.net/","categories":["social"],"domains":["front.pixfs.net","falcon-asset.pixfs.net","pixgame-asset.pixfs.net"]},{"name":"JSDelivr CDN","homepage":"https://www.jsdelivr.com/","categories":["library"],"domains":["cdn.jsdelivr.net"]},{"name":"Yandex Ads","company":"Yandex","homepage":"https://yandex.com/adv/","categories":["ad"],"domains":["an.yandex.ru"]},{"name":"Yandex Share","company":"Yandex","homepage":"https://yastatic.net/share2/share.js","categories":["social"],"domains":["yastatic.net"]},{"name":"Yandex APIs","company":"Yandex","homepage":"https://yandex.ru/","categories":["utility"],"domains":["api-maps.yandex.ru","money.yandex.ru"]},{"name":"Salesforce","homepage":"https://www.salesforce.com/products/marketing-cloud/","categories":["analytics"],"domains":["cdn.krxd.net","beacon.krxd.net","consumer.krxd.net","usermatch.krxd.net"]},{"name":"Sumo","homepage":"https://sumo.com/","categories":["marketing"],"domains":["sumo.b-cdn.net","load.sumo.com","load.sumome.com"]},{"name":"Beeketing","homepage":"https://beeketing.com/","categories":["marketing"],"domains":["sdk-cdn.beeketing.com","sdk.beeketing.com"]},{"name":"FontAwesome CDN","homepage":"https://fontawesome.com/","categories":["library"],"domains":["use.fontawesome.com"]},{"name":"ShareThis","homepage":"https://www.sharethis.com/","categories":["social"],"domains":["w.sharethis.com","ws.sharethis.com","t.sharethis.com"]},{"name":"Hatena Blog","homepage":"https://hatenablog.com/","categories":["hosting"],"domains":["cdn.blog.st-hatena.com","cdn.pool.st-hatena.com","cdn7.www.st-hatena.com","s.hatena.ne.jp","b.st-hatena.com"]},{"name":"Mailchimp","homepage":"https://mailchimp.com/","categories":["marketing"],"domains":["downloads.mailchimp.com","chimpstatic.com","list-manage.com"]},{"name":"Amazon Ads","homepage":"https://ad.amazon.com/","categories":["ad"],"domains":["s.amazon-adsystem.com","c.amazon-adsystem.com","aax.amazon-adsystem.com","z-na.amazon-adsystem.com"]},{"name":"Sentry","homepage":"https://sentry.io/","categories":["utility"],"domains":["cdn.ravenjs.com","browser.sentry-cdn.com","getsentry.com"]},{"name":"Pinterest","homepage":"https://pinterest.com/","categories":["social"],"domains":["assets.pinterest.com","pinimg.com","ct.pinterest.com"]},{"name":"Hubspot","homepage":"https://hubspot.com/","categories":["marketing"],"domains":["forms.hubspot.com","js.hsforms.net","js.hs-analytics.net","hscollectedforms.net","hscta.net","hsleadflows.net","js.leadin.com","hs-scripts.com","hsstatic.net","hubspot.net"]},{"name":"LinkedIn","homepage":"https://www.linkedin.com/","categories":["social"],"domains":["platform.linkedin.com","bizographics.com","slideshare.com","slidesharecdn.com"]},{"name":"Taboola","homepage":"https://www.taboola.com/","categories":["ad"],"domains":["cdn.taboola.com","trc.taboola.com","vidstat.taboola.com","taboolasyndication.com"]},{"name":"Intercom","homepage":"https://www.intercom.com/","categories":["customer-success"],"domains":["js.intercomcdn.com"]},{"name":"Histats","homepage":"http://histats.com/","categories":["analytics"],"domains":["s10.histats.com"]},{"name":"Optimizely","homepage":"https://www.optimizely.com/","categories":["analytics"],"domains":["cdn.optimizely.com","cdn-pci.optimizely.com","logx.optimizely.com","cdn3.optimizely.com"]},{"name":"Moat","homepage":"https://moat.com/","categories":["ad"],"domains":["z.moatads.com","moatpixel.com","px.moatads.com","geo.moatads.com","sejs.moatads.com","mb.moatads.com","v4.moatads.com"]},{"name":"Tealium","homepage":"https://tealium.com/","categories":["tag-manager"],"domains":["tags.tiqcdn.com","tealium.hs.llnwd.net","aniview.com","delvenetworks.com","link.videoplatform.limelight.com"]},{"name":"Distil Networks","homepage":"https://www.distilnetworks.com/","categories":["utility"],"domains":["n-cdn.areyouahuman.com"]},{"name":"Scorecard Research","homepage":"https://www.scorecardresearch.com/","categories":["ad"],"domains":["sb.scorecardresearch.com"]},{"name":"Blogger","homepage":"http://www.blogger.com/","categories":["hosting"],"domains":["1.bp.blogspot.com","www.blogger.com","images-blogger-opensocial.googleusercontent.com"]},{"name":"Wistia","homepage":"https://wistia.com/","categories":["video"],"domains":["fast.wistia.com","fast.wistia.net"]},{"name":"LiveChat","homepage":"https://www.livechatinc.com/","categories":["customer-success"],"domains":["cdn.livechatinc.com","secure.livechatinc.com"]},{"name":"Adobe TypeKit","company":"Adobe","homepage":"https://fonts.adobe.com/","categories":["library"],"domains":["use.typekit.net","typekit.com","p.typekit.net"]},{"name":"Shareaholic","homepage":"https://www.shareaholic.com/","categories":["social"],"domains":["dsms0mj1bbhn4.cloudfront.net","shareaholic.com"]},{"name":"OneSignal","homepage":"https://onesignal.com/","categories":["utility"],"domains":["cdn.onesignal.com"]},{"name":"Cookiebot","homepage":"https://www.cookiebot.com/","categories":["utility"],"domains":["consent.cookiebot.com"]},{"name":"Tumblr","homepage":"https://tumblr.com/","categories":["social"],"domains":["assets.tumblr.com","static.tumblr.com"]},{"name":"Cloudflare","homepage":"https://www.cloudflare.com/website-optimization/","categories":["utility"],"domains":["ajax.cloudflare.com"]},{"name":"Integral Ad Science","homepage":"https://integralads.com/uk/","categories":["ad"],"domains":["pixel.adsafeprotected.com","static.adsafeprotected.com","fw.adsafeprotected.com","cdn.adsafeprotected.com","dt.adsafeprotected.com","iasds01.com"]},{"name":"PayPal","homepage":"https://paypal.com","categories":["utility"],"domains":["www.paypalobjects.com","paypal.com"]},{"name":"Segment","homepage":"https://segment.com/","categories":["analytics"],"domains":["cdn.segment.com","api.segment.io"]},{"name":"Baidu Analytics","homepage":"https://tongji.baidu.com/web/welcome/login","categories":["analytics"],"domains":["hm.baidu.com"]},{"name":"Dealer","homepage":"https://www.dealer.com/","categories":["hosting"],"domains":["static.dealer.com"]},{"name":"Olark","homepage":"https://www.olark.com/","categories":["customer-success"],"domains":["static.olark.com"]},{"name":"VK","homepage":"https://vk.com/","categories":["social"],"domains":["vk.com"]},{"name":"OpenX","homepage":"https://www.openx.com/","categories":["ad"],"domains":["uk-ads.openx.net","us-ads.openx.net","33across-d.openx.net","rtb.openx.net","us-u.openx.net","eu-u.openx.net","u.openx.net","deliverimp.com","jump-time.net","openxadexchange.com","servedbyopenx.com"]},{"name":"Lucky Orange","homepage":"https://www.luckyorange.com/","categories":["analytics"],"domains":["d10lpsik1i8c69.cloudfront.net","luckyorange.com","luckyorange.net"]},{"name":"OptinMonster","homepage":"https://optinmonster.com/","categories":["marketing"],"domains":["a.optmstr.com","api.opmnstr.com","a.optmnstr.com"]},{"name":"Snapchat","homepage":"https://www.snapchat.com","categories":["analytics"],"domains":["tr.snapchat.com","sc-static.net"]},{"name":"33 Across","homepage":"https://33across.com/","categories":["ad"],"domains":["sic.33across.com","cdn-sic.33across.com"]},{"name":"Ensighten","homepage":"https://www.ensighten.com/","categories":["tag-manager"],"domains":["nexus.ensighten.com"]},{"name":"WordAds","company":"Automattic","homepage":"https://wordads.co/","categories":["ad"],"domains":["s.pubmine.com"]},{"name":"Snowplow","homepage":"https://snowplowanalytics.com/","categories":["analytics"],"domains":["d32hwlnfiv2gyn.cloudfront.net"]},{"name":"Brightcove","homepage":"https://www.brightcove.com/en/","categories":["video"],"domains":["vjs.zencdn.net","players.brightcove.net","brightcove.com"]},{"name":"Drift","homepage":"https://www.drift.com/","categories":["marketing"],"domains":["js.driftt.com","api.drift.com"]},{"name":"Microsoft Hosted Libs","company":"Microsoft","categories":["library"],"domains":["ajax.aspnetcdn.com"]},{"name":"Stripe","homepage":"https://stripe.com","categories":["utility"],"domains":["m.stripe.network","js.stripe.com","stripecdn.com"]},{"name":"Parking Crew","homepage":"http://parkingcrew.net/","categories":["other"],"domains":["d1lxhc4jvstzrp.cloudfront.net","parkingcrew.net"]},{"name":"Popads","homepage":"https://www.popads.net/","categories":["ad"],"domains":["serve.popads.net","c1.popads.net"]},{"name":"DigiTrust","homepage":"http://www.digitru.st/","categories":["analytics"],"domains":["cdn.digitru.st"]},{"name":"Mixpanel","homepage":"https://mixpanel.com/","categories":["analytics"],"domains":["cdn.mxpnl.com","mixpanel.com"]},{"name":"MediaVine","homepage":"https://www.mediavine.com/","categories":["ad"],"domains":["scripts.mediavine.com","video.mediavine.com"]},{"name":"FullStory","categories":["analytics"],"domains":["fullstory.com","rs.fullstory.com"]},{"name":"Sizmek","homepage":"https://www.sizmek.com/","categories":["ad"],"domains":["secure-ds.serving-sys.com","ds.serving-sys.com","peer39.net","bs.serving-sys.com"]},{"name":"CDK Dealer Management","company":"CDK Global","homepage":"https://www.cdkglobal.com/us","categories":["hosting"],"domains":["media-cf.assets-cdk.com"]},{"name":"Quantcast","homepage":"https://www.quantcast.com","categories":["analytics"],"domains":["pixel.quantserve.com","secure.quantserve.com","rules.quantcount.com","brtstats.com","ntv.io","semantictec.com"]},{"name":"Pubmatic","homepage":"https://pubmatic.com/","categories":["ad"],"domains":["image6.pubmatic.com","ads.pubmatic.com","image2.pubmatic.com","simage2.pubmatic.com","image4.pubmatic.com","simage4.pubmatic.com"]},{"name":"RD Station","homepage":"https://www.rdstation.com/en/","categories":["marketing"],"domains":["d335luupugsy2.cloudfront.net"]},{"name":"MGID","homepage":"https://www.mgid.com/","categories":["ad"],"domains":["servicer.mgid.com","dt07.net"]},{"name":"Media.net","homepage":"https://www.media.net/","categories":["ad"],"domains":["contextual.media.net","mnet-ad.net"]},{"name":"New Relic","homepage":"https://newrelic.com/","categories":["utility"],"domains":["js-agent.newrelic.com","bam.nr-data.net"]},{"name":"Rubicon Project","homepage":"https://rubiconproject.com/","categories":["ad"],"domains":["pixel.rubiconproject.com","fastlane.rubiconproject.com","secure-assets.rubiconproject.com","eus.rubiconproject.com","pixel-us-east.rubiconproject.com","token.rubiconproject.com","ads.rubiconproject.com","chango.com","fimserve.com"]},{"name":"VWO","homepage":"https://vwo.com","categories":["analytics"],"domains":["dev.visualwebsiteoptimizer.com"]},{"name":"Keen","company":"Keen","homepage":"https://keen.io/","categories":["analytics"],"domains":["d26b395fwzu5fz.cloudfront.net","keen.io"]},{"name":"Adroll","homepage":"https://www.adroll.com/","categories":["ad"],"domains":["d.adroll.com","s.adroll.com"]},{"name":"Unpkg","homepage":"https://unpkg.com","categories":["library"],"domains":["unpkg.com"]},{"name":"Parse.ly","categories":["analytics"],"domains":["d1z2jf7jlzjs58.cloudfront.net","parsely.com"]},{"name":"Searchanise","categories":["analytics"],"domains":["www.searchanise.com"]},{"name":"AppNexus","homepage":"https://www.appnexus.com/","categories":["ad"],"domains":["acdn.adnxs.com","secure.adnxs.com","ctasnet.com","ib.adnxs.com"]},{"name":"uLogin","categories":["other"],"domains":["ulogin.ru"]},{"name":"fam","company":"Fing Co Ltd.","homepage":"http://admin.fam-ad.com/report/","categories":["ad"],"domains":["fam-ad.com","img.fam-ad.com"]},{"name":"Yandex CDN","company":"Yandex","homepage":"https://yandex.ru/","categories":["library"],"domains":["yandex.st"]},{"name":"Albacross","homepage":"https://albacross.com/","categories":["marketing"],"domains":["serve.albacross.com"]},{"name":"CreateJS CDN","homepage":"http://code.createjs.com/","categories":["library"],"domains":["code.createjs.com"]},{"name":"Help Scout","homepage":"https://www.helpscout.net/","categories":["customer-success"],"domains":["djtflbt20bdde.cloudfront.net","beacon-v2.helpscout.net"]},{"name":"AppDynamics","homepage":"https://www.appdynamics.com/","categories":["utility"],"domains":["cdn.appdynamics.com","d3tjaysgumg9lf.cloudfront.net","eum-appdynamics.com"]},{"name":"Siteimprove","categories":["utility"],"domains":["siteimprove.com","siteimproveanalytics.com"]},{"name":"DoubleVerify","homepage":"https://www.doubleverify.com/","categories":["ad"],"domains":["cdn.doubleverify.com","rtb0.doubleverify.com","rtbcdn.doubleverify.com","dvtps.com","tm.iqfp1.com"]},{"name":"AOL / Oath / Verizon Media","homepage":"https://www.oath.com/","categories":["ad"],"domains":["pixel.advertising.com","dtm.advertising.com","tag.sp.advertising.com","service.sp.advertising.com","adtech.advertising.com","aol.co.uk","aol.com","aolcdn.com","blogsmithmedia.com","mighty.aol.net","tacoda.net","consent.cmp.oath.com"]},{"name":"Alexa","homepage":"https://www.alexa.com/","categories":["analytics"],"domains":["d31qbv1cthcecs.cloudfront.net"]},{"name":"SessionCam","company":"ServiceTick","categories":["analytics"],"domains":["d2oh4tlt9mrke9.cloudfront.net","sessioncam.com"]},{"name":"Foursixty","categories":["customer-success"],"domains":["foursixty.com"]},{"name":"Listrak","homepage":"https://www.listrak.com/","categories":["marketing"],"domains":["cdn.listrakbi.com","s1.listrakbi.com","listrak.com"]},{"name":"mPulse","homepage":"https://developer.akamai.com/akamai-mpulse","categories":["analytics"],"domains":["c.go-mpulse.net","0211c83c.akstat.io","mpstat.us","mpulse.net"]},{"name":"Opentag","company":"Qubit","categories":["tag-manager"],"domains":["d3c3cq33003psk.cloudfront.net","opentag-stats.qutics.com"]},{"name":"Market GID","homepage":"https://www.marketgid.com/","categories":["ad"],"domains":["jsc.marketgid.com"]},{"name":"Freshdesk","homepage":"https://freshdesk.com/","categories":["customer-success"],"domains":["d36mpcpuzc4ztk.cloudfront.net"]},{"name":"IBM Digital Analytics","company":"IBM","categories":["analytics"],"domains":["cmcore.com","coremetrics.com","data.coremetrics.com","data.coremetrics.eu","data.de.coremetrics.com","iocdn.coremetrics.com","libs.coremetrics.com","libs.de.coremetrics.com","s81c.com","tmscdn.coremetrics.com","tmscdn.de.coremetrics.com","unica.com"]},{"name":"Usabilla","homepage":"https://usabilla.com","categories":["analytics"],"domains":["w.usabilla.com","d6tizftlrpuof.cloudfront.net"]},{"name":"Bugsnag","categories":["utility"],"domains":["d2wy8f7a9ursnm.cloudfront.net","notify.bugsnag.com"]},{"name":"AddShoppers","categories":["social"],"domains":["addshoppers.com","d3rr3d0n31t48m.cloudfront.net","shop.pe"]},{"name":"Po.st","company":"RadiumOne","categories":["utility"],"domains":["po.st"]},{"name":"Disqus","homepage":"http://disqus.com/","categories":["social"],"domains":["c.disquscdn.com","disqus.com"]},{"name":"PhotoBucket","categories":["content"],"domains":["photobucket.com"]},{"name":"GitHub","categories":["utility"],"domains":["cdn.rawgit.com"]},{"name":"Bootstrap CDN","homepage":"https://www.bootstrapcdn.com/","categories":["library"],"domains":["maxcdn.bootstrapcdn.com","stackpath.bootstrapcdn.com"]},{"name":"Radar","company":"Cedexis","homepage":"https://www.cedexis.com/radar/","categories":["analytics"],"domains":["radar.cedexis.com","rpt.cedexis.com","2-01-49cd-0002.cdx.cedexis.net","a-cedexis.msedge.net","bench.cedexis-test.com","cedexis-radar.net","cedexis-test01.insnw.net","cedexis.leasewebcdn.com","cedexisakamaitest.azureedge.net","cedexispub.cdnetworks.net","cs600.wac.alphacdn.net","cs600.wpc.edgecastdns.net","global2.cmdolb.com","img-cedexis.mncdn.com","zn3vgszfh.fastestcdn.net","edgecastcdn.net"]},{"name":"SnapWidget","categories":["content"],"domains":["snapwidget.com"]},{"name":"Media Math","homepage":"http://www.mediamath.com/","categories":["ad"],"domains":["mathid.mathtag.com","mathads.com","sync.mathtag.com","pixel.mathtag.com"]},{"name":"ClickDesk","categories":["customer-success"],"domains":["clickdesk.com","d1gwclp1pmzk26.cloudfront.net"]},{"name":"Google Plus","company":"Google","categories":["social"],"domains":["plus.google.com"]},{"name":"LinkedIn Ads","categories":["ad"],"domains":["ads.linkedin.com","snap.licdn.com","www.linkedin.com"]},{"name":"Madison Logic","categories":["marketing"],"domains":["ml314.com"]},{"name":"Smarter Click","categories":["ad"],"domains":["smarterclick.co.uk","smct.co"]},{"name":"Bing Ads","homepage":"https://bingads.microsoft.com","categories":["ad"],"domains":["bat.bing.com","c.bing.com","bat.r.msn.com","ajax.microsoft.com","msads.net","msecnd.net","s-msft.com","s-msn.com","windows.net"]},{"name":"Fresh Relevance","categories":["analytics"],"domains":["d1y9qtn9cuc3xw.cloudfront.ne","d1y9qtn9cuc3xw.cloudfront.net","d81mfvml8p5ml.cloudfront.net","dkpklk99llpj0.cloudfront.net","freshrelevance.com"]},{"name":"Browsealoud","homepage":"https://www.texthelp.com/en-gb/products/browsealoud/","categories":["other"],"domains":["www.browsealoud.com","texthelp.com"]},{"name":"Qubit Deliver","company":"Qubit","categories":["analytics"],"domains":["d1m54pdnjzjnhe.cloudfront.net","d22rutvoghj3db.cloudfront.net","dd6zx4ibq538k.cloudfront.net"]},{"name":"Crazy Egg","homepage":"https://www.crazyegg.com/","categories":["analytics"],"domains":["dnn506yrbagrg.cloudfront.net","cetrk.com","crazyegg.com","hellobar.com"]},{"name":"Vox Media","homepage":"https://www.voxmedia.com/","categories":["content"],"domains":["cdn.vox-cdn.com","voxmedia.com"]},{"name":"SaleCycle","categories":["ad"],"domains":["d16fk4ms6rqz1v.cloudfront.net","d22j4fzzszoii2.cloudfront.net","d30ke5tqu2tkyx.cloudfront.net","salecycle.com"]},{"name":"Sidecar","categories":["other"],"domains":["d3v27wwd40f0xu.cloudfront.net","getsidecar.com"]},{"name":"Concert","homepage":"https://concert.io/","categories":["ad"],"domains":["cdn.concert.io"]},{"name":"Kaizen Platform","categories":["analytics"],"domains":["cdn.kaizenplatform.net","log-v4.kaizenplatform.net"]},{"name":"unpkg","categories":["utility"],"domains":["npmcdn.com"]},{"name":"Edge Web Fonts","company":"Adobe Systems","categories":["library"],"domains":["use.edgefonts.net"]},{"name":"The Trade Desk","homepage":"https://www.thetradedesk.com/","categories":["ad"],"domains":["js.adsrvr.org","match.adsrvr.org","d1eoo1tco6rr5e.cloudfront.net","snap.adsrvr.org"]},{"name":"Ipify","homepage":"https://www.ipify.org","categories":["utility"],"domains":["api.ipify.org","geo.ipify.org"]},{"name":"Permutive","categories":["ad"],"domains":["d3alqb8vzo7fun.cloudfront.net","permutive.com"]},{"name":"Pingdom RUM","homepage":"https://www.pingdom.com/product/performance-monitoring/","categories":["analytics"],"domains":["rum-static.pingdom.net","rum-collector-2.pingdom.net"]},{"name":"Clicktripz","categories":["content"],"domains":["static.clicktripz.com","www.clicktripz.com"]},{"name":"Talkable","categories":["ad"],"domains":["d2jjzw81hqbuqv.cloudfront.net","www.talkable.com"]},{"name":"GoSquared","homepage":"https://www.gosquared.com","categories":["analytics"],"domains":["data.gosquared.com","d1l6p2sc9645hc.cloudfront.net","data2.gosquared.com"]},{"name":"Republer","categories":["ad"],"domains":["sync.republer.com"]},{"name":"TrackJS","categories":["analytics"],"domains":["d2zah9y47r7bi2.cloudfront.net","usage.trackjs.com"]},{"name":"Vimeo","homepage":"http://vimeo.com/","categories":["video"],"domains":["f.vimeocdn.com","player.vimeo.com"]},{"name":"Yieldify","categories":["ad"],"domains":["d33wq5gej88ld6.cloudfront.net","dwmvwp56lzq5t.cloudfront.net","geo.yieldifylabs.com","yieldify.com"]},{"name":"DialogTech SourceTrak","company":"DialogTech","categories":["ad"],"domains":["d31y97ze264gaa.cloudfront.net"]},{"name":"Twitter Online Conversion Tracking","company":"Twitter","categories":["ad"],"domains":["static.ads-twitter.com","analytics.twitter.com"]},{"name":"CleverTap","categories":["analytics"],"domains":["d2r1yp2w7bby2u.cloudfront.net"]},{"name":"Omniconvert","categories":["analytics"],"domains":["d2tgfbvjf3q6hn.cloudfront.net","d3vbj265bmdenw.cloudfront.net","omniconvert.com"]},{"name":"Underdog Media","categories":["ad"],"domains":["udmserve.net","underdog.media"]},{"name":"[24]7","categories":["customer-success"],"domains":["247-inc.net","247inc.net","d1af033869koo7.cloudfront.net"]},{"name":"Reflektion","categories":["analytics"],"domains":["d26opx5dl8t69i.cloudfront.net","reflektion.com"]},{"name":"Auto Link Maker","company":"Apple","categories":["ad"],"domains":["autolinkmaker.itunes.apple.com"]},{"name":"Friendbuy","categories":["ad"],"domains":["djnf6e5yyirys.cloudfront.net","friendbuy.com"]},{"name":"Opinion Stage","categories":["analytics"],"domains":["www.opinionstage.com"]},{"name":"LongTail Ad Solutions","categories":["ad"],"domains":["jwpcdn.com","jwplatform.com","jwplayer.com","jwpltx.com","jwpsrv.com","longtailvideo.com"]},{"name":"Marketo","homepage":"https://www.marketo.com","categories":["analytics"],"domains":["munchkin.marketo.net","marketo.com","mktoresp.com"]},{"name":"Sourcepoint","categories":["ad"],"domains":["d2lv4zbk7v5f93.cloudfront.net","d3qxwzhswv93jk.cloudfront.net","www.decenthat.com","www.fallingfalcon.com"]},{"name":"Net Reviews","categories":["analytics"],"domains":["www.avis-verifies.com"]},{"name":"Tag Inspector","company":"InfoTrust","categories":["analytics"],"domains":["d22xmn10vbouk4.cloudfront.net"]},{"name":"Polldaddy","company":"Automattic","categories":["analytics"],"domains":["polldaddy.com"]},{"name":"Freespee","categories":["customer-success"],"domains":["analytics.freespee.com"]},{"name":"KISSmetrics","categories":["analytics"],"domains":["doug1izaerwt3.cloudfront.net","dsyszv14g9ymi.cloudfront.net","kissmetrics.com"]},{"name":"Adthink","company":"Adthink Media","categories":["ad"],"domains":["d.adxcore.com","dcoengine.com"]},{"name":"Extole","categories":["ad"],"domains":["extole.com","origin.extole.io"]},{"name":"AnswerDash","categories":["customer-success"],"domains":["p1.answerdash.com"]},{"name":"Cookie-Script.com","categories":["utility"],"domains":["cookie-script.com"]},{"name":"Fastly Insights","homepage":"https://insights.fastlylabs.com","categories":["analytics"],"domains":["www.fastly-insights.com"]},{"name":"Amplitude Mobile Analytics","company":"Amplitude","categories":["analytics"],"domains":["amplitude.com","d24n15hnbwhuhn.cloudfront.net"]},{"name":"MLveda","categories":["utility"],"domains":["www.mlveda.com"]},{"name":"CNET Content Solutions","company":"CBS Interactive","categories":["content"],"domains":["cdn.cnetcontent.com","cnetcontent.com","ws.cnetcontent.com"]},{"name":"Browser-Update.org","categories":["other"],"domains":["browser-update.org"]},{"name":"Triblio","categories":["marketing"],"domains":["tribl.io"]},{"name":"Fonecall","categories":["analytics"],"domains":["web-call-analytics.com"]},{"name":"LoyaltyLion","categories":["ad"],"domains":["dg1f2pfrgjxdq.cloudfront.net","loyaltylion.com"]},{"name":"StatCounter","categories":["analytics"],"domains":["statcounter.com"]},{"name":"Curalate","categories":["marketing"],"domains":["curalate.com","d116tqlcqfmz3v.cloudfront.net"]},{"name":"piano","categories":["ad"],"domains":["tinypass.com","www.npttech.com"]},{"name":"UpSellit","categories":["analytics"],"domains":["www.upsellit.com"]},{"name":"Soundest","categories":["ad"],"domains":["soundest.net","soundestlink.com"]},{"name":"Micropat","categories":["social"],"domains":["addtoany.com"]},{"name":"Weebly","homepage":"https://www.weebly.com/","categories":["hosting"],"domains":["editmysite.com"]},{"name":"Tynt","company":"33 Across","categories":["ad"],"domains":["tynt.com"]},{"name":"Datacamp","categories":["utility"],"domains":["cdn77.org"]},{"name":"Treasure Data","categories":["analytics"],"domains":["treasuredata.com"]},{"name":"Nielsen NetRatings SiteCensus","company":"The Nielsen Company","homepage":"http://www.nielsen-online.com/intlpage.html","categories":["analytics"],"domains":["imrworldwide.com"]},{"name":"iubenda","categories":["utility"],"domains":["www.iubenda.com"]},{"name":"Yotpo","homepage":"https://www.yotpo.com/","categories":["marketing"],"domains":["yotpo.com"]},{"name":"Privy","categories":["ad"],"domains":["privy.com","privymktg.com"]},{"name":"VigLink","categories":["ad"],"domains":["viglink.com"]},{"name":"Kakao","categories":["social"],"domains":["daum.net","daumcdn.net"]},{"name":"Chartbeat","categories":["analytics"],"domains":["chartbeat.com","chartbeat.net"]},{"name":"Klaviyo","categories":["ad"],"domains":["klaviyo.com"]},{"name":"BrightTag / Signal","company":"Signal","homepage":"https://www.signal.co","categories":["tag-manager"],"domains":["btstatic.com","thebrighttag.com"]},{"name":"fluct","categories":["ad"],"domains":["adingo.jp"]},{"name":"AudienceSearch","company":"Intimate Merger","categories":["ad"],"domains":["im-apps.net"]},{"name":"Inspectlet","categories":["analytics"],"domains":["inspectlet.com"]},{"name":"Skimbit","categories":["ad"],"domains":["redirectingat.com","skimresources.com","skimresources.net"]},{"name":"DTSCOUT","categories":["ad"],"domains":["dtscout.com"]},{"name":"Rambler","company":"Rambler & Co","categories":["utility"],"domains":["rambler.ru"]},{"name":"Bigcommerce","categories":["marketing"],"domains":["bigcommerce.com"]},{"name":"Tidio Live Chat","company":"Tidio","homepage":"https://www.tidiochat.com/en/","categories":["customer-success"],"domains":["tidiochat.com"]},{"name":"CallRail","categories":["analytics"],"domains":["callrail.com"]},{"name":"Teads","categories":["ad"],"domains":["teads.tv"]},{"name":"Instagram","homepage":"https://www.instagram.com","categories":["social"],"domains":["scontent.cdninstagram.com","instagram.com"]},{"name":"Fastly","categories":["utility"],"domains":["fastly.net"]},{"name":"MailMunch","categories":["ad"],"domains":["mailmunch.co"]},{"name":"LivePerson","categories":["customer-success"],"homepage":"https://www.liveperson.com/","domains":["liveperson.com","liveperson.net","look.io","lpsnmedia.net"]},{"name":"Monotype","categories":["library"],"domains":["fonts.com","fonts.net"]},{"name":"Outbrain","homepage":"https://www.outbrain.com/","categories":["ad"],"domains":["outbrain.com","visualrevenue.com"]},{"name":"Pure Chat","categories":["customer-success"],"domains":["purechat.com"]},{"name":"LiveJournal","categories":["social"],"domains":["livejournal.com","livejournal.net"]},{"name":"PushCrew","categories":["ad"],"domains":["pushcrew.com"]},{"name":"Index Exchange","company":"WPP","categories":["ad"],"domains":["casalemedia.com","indexww.com"]},{"name":"StickyADS.tv","categories":["ad"],"domains":["stickyadstv.com"]},{"name":"GumGum","categories":["ad"],"domains":["gumgum.com"]},{"name":"AB Tasty","categories":["analytics"],"domains":["abtasty.com","d1447tq2m68ekg.cloudfront.net"]},{"name":"Trust Pilot","categories":["analytics"],"domains":["trustpilot.com"]},{"name":"Embedly","categories":["content"],"domains":["embed.ly","embedly.com"]},{"name":"Adform","categories":["ad"],"domains":["adform.net","adformdsp.net"]},{"name":"sovrn","categories":["ad"],"domains":["lijit.com"]},{"name":"iPerceptions","categories":["customer-success"],"domains":["iperceptions.com"]},{"name":"Cxense","categories":["ad"],"domains":["cxense.com","cxpublic.com","emediate.dk","emediate.eu"]},{"name":"Bold Commerce","categories":["utility"],"domains":["boldapps.net","shappify-cdn.com","shappify.com"]},{"name":"Marchex","categories":["analytics"],"domains":["marchex.io","voicestar.com"]},{"name":"BlueKai","company":"Oracle","categories":["ad"],"domains":["bkrtx.com","bluekai.com"]},{"name":"PubNation","categories":["ad"],"domains":["pubnation.com"]},{"name":"Infolinks","categories":["ad"],"domains":["infolinks.com"]},{"name":"Ezoic","categories":["analytics"],"domains":["ezoic.net"]},{"name":"Sharethrough","categories":["ad"],"domains":["sharethrough.com"]},{"name":"Ve","company":"Ve Interactive","categories":["marketing"],"domains":["veinteractive.com"]},{"name":"Roxr Software","categories":["analytics"],"domains":["getclicky.com"]},{"name":"LiveTex","categories":["customer-success"],"domains":["livetex.ru"]},{"name":"Crowd Control","company":"Lotame","categories":["ad"],"domains":["crwdcntrl.net"]},{"name":"Digital ad Consortium","categories":["ad"],"domains":["impact-ad.jp"]},{"name":"GetSiteControl","company":"GetWebCraft","categories":["utility"],"domains":["getsitecontrol.com"]},{"name":"Mapbox","categories":["utility"],"domains":["mapbox.com"]},{"name":"Adloox","categories":["ad"],"domains":["adlooxtracking.com"]},{"name":"Nosto","categories":["analytics"],"domains":["nosto.com"]},{"name":"Gigya","categories":["analytics"],"domains":["gigya.com"]},{"name":"Heap","categories":["analytics"],"domains":["heapanalytics.com"]},{"name":"ExoClick","categories":["ad"],"domains":["exoclick.com"]},{"name":"OpenTable","company":"Priceline Group","categories":["content"],"domains":["opentable.co.uk","opentable.com","www.toptable.co.uk"]},{"name":"iBillboard","categories":["ad"],"domains":["ibillboard.com"]},{"name":"Microad","categories":["ad"],"domains":["microad.jp"]},{"name":"Rakuten Marketing","company":"Rakuten","categories":["ad"],"domains":["rakuten-static.com","rmtag.com"]},{"name":"JuicyAds","categories":["ad"],"domains":["juicyads.com"]},{"name":"Mouseflow","categories":["analytics"],"domains":["mouseflow.com"]},{"name":"Swiftype","categories":["utility"],"domains":["swiftype.com","swiftypecdn.com"]},{"name":"Yahoo!","homepage":"https://www.yahoo.com/","categories":["ad"],"domains":["ads.yahoo.com","analytics.yahoo.com","bluelithium.com","hostingprod.com","lexity.com","yahoo.net","yahooapis.com","yimg.com","zenfs.com","geo.yahoo.com"]},{"name":"etracker","categories":["analytics"],"domains":["etracker.de","www.etracker.com"]},{"name":"Accuweather","categories":["content"],"domains":["accuweather.com"]},{"name":"Feefo.com","company":"Feefo","categories":["analytics"],"domains":["feefo.com"]},{"name":"Smart AdServer","categories":["ad"],"domains":["sasqos.com","smartadserver.com"]},{"name":"Medium","categories":["content"],"domains":["medium.com"]},{"name":"Trusted Shops","categories":["utility"],"domains":["trustedshops.com"]},{"name":"Constant Contact","categories":["ad"],"domains":["ctctcdn.com"]},{"name":"AdMatic","categories":["ad"],"domains":["admatic.com.tr"]},{"name":"Unbounce","categories":["ad"],"domains":["d2xxq4ijfwetlm.cloudfront.net","d9hhrg4mnvzow.cloudfront.net","ubembed.com","unbounce.com"]},{"name":"Evidon","categories":["analytics"],"domains":["evidon.com"]},{"name":"SmartAdServer","categories":["ad"],"domains":["sascdn.com","securite.01net.com"]},{"name":"Gemius","categories":["ad"],"domains":["gemius.pl"]},{"name":"SocialShopWave","categories":["social"],"domains":["socialshopwave.com"]},{"name":"Sift Science","categories":["utility"],"domains":["siftscience.com"]},{"name":"Affirm","categories":["utility"],"domains":["affirm.com"]},{"name":"Admixer for Publishers","company":"Admixer","categories":["ad"],"domains":["admixer.net"]},{"name":"LKQD","categories":["ad"],"domains":["lkqd.net"]},{"name":"Hotmart","homepage":"https://www.hotmart.com/","categories":["content"],"domains":["launchermodule.hotmart.com"]},{"name":"Secomapp","categories":["utility"],"domains":["secomapp.com"]},{"name":"Sortable","categories":["ad"],"domains":["deployads.com"]},{"name":"Bazaarvoice","categories":["analytics"],"domains":["bazaarvoice.com","feedmagnet.com"]},{"name":"Seznam","categories":["utility"],"domains":["imedia.cz"]},{"name":"Vidible","categories":["ad"],"domains":["vidible.tv"]},{"name":"Affiliate Window","company":"Digital Window","categories":["ad"],"domains":["dwin1.com"]},{"name":"OptiMonk","categories":["ad"],"domains":["optimonk.com"]},{"name":"Refersion","categories":["ad"],"domains":["refersion.com"]},{"name":"Pagely","categories":["other"],"domains":["optnmstr.com"]},{"name":"BounceX","categories":["analytics"],"homepage":"https://www.bouncex.com/","domains":["bounceexchange.com","events.bouncex.net"]},{"name":"TrafficStars","categories":["ad"],"domains":["trafficstars.com","tsyndicate.com"]},{"name":"SnapEngage","categories":["customer-success"],"domains":["snapengage.com"]},{"name":"Esri ArcGIS","company":"Esri","categories":["utility"],"domains":["arcgis.com","arcgisonline.com"]},{"name":"ForeSee","company":"Answers","categories":["analytics"],"domains":["4seeresults.com","answerscloud.com","foresee.com","foreseeresults.com"]},{"name":"TagCommander","categories":["tag-manager"],"domains":["commander1.com","tagcommander.com"]},{"name":"Convert Insights","categories":["analytics"],"domains":["convertexperiments.com"]},{"name":"iovation","categories":["utility"],"domains":["iesnare.com"]},{"name":"Clicktale","categories":["analytics"],"domains":["clicktale.net","clicktalecdn.sslcs.cdngc.net"]},{"name":"Comm100","categories":["customer-success"],"domains":["comm100.com"]},{"name":"Yieldmo","categories":["ad"],"domains":["yieldmo.com"]},{"name":"IPONWEB","categories":["ad"],"domains":["company-target.com","liadm.com","p161.net","pool.udsp.iponweb.net"]},{"name":"Nend","categories":["ad"],"domains":["nend.net"]},{"name":"Perfect Market","categories":["ad"],"domains":["perfectmarket.com"]},{"name":"Fraudlogix","categories":["utility"],"domains":["yabidos.com"]},{"name":"Symantec","categories":["utility"],"domains":["extended-validation-ssl.websecurity.symantec.com","norton.com","symantec.com","symcb.com","symcd.com"]},{"name":"Bizible","categories":["ad"],"domains":["bizible.com"]},{"name":"Between Digital","categories":["ad"],"domains":["betweendigital.com"]},{"name":"Maxymiser","categories":["analytics"],"domains":["maxymiser.net"]},{"name":"Branch Metrics","categories":["ad"],"domains":["app.link","branch.io"]},{"name":"Tradelab","categories":["ad"],"domains":["tradelab.fr"]},{"name":"Digioh","categories":["ad"],"domains":["lightboxcdn.com"]},{"name":"Tail Target","company":"Tail","categories":["ad"],"domains":["tailtarget.com"]},{"name":"GetResponse","categories":["ad"],"domains":["getresponse.com"]},{"name":"OwnerIQ","categories":["ad"],"domains":["owneriq.net"]},{"name":"Dynamic Yield","categories":["customer-success"],"domains":["dynamicyield.com"]},{"name":"Fort Awesome","categories":["library"],"domains":["fortawesome.com"]},{"name":"Clerk.io ApS","categories":["analytics"],"domains":["clerk.io"]},{"name":"Adyoulike","categories":["ad"],"domains":["adyoulike.com","adyoulike.net","omnitagjs.com"]},{"name":"iAdvize SAS","categories":["customer-success"],"domains":["iadvize.com"]},{"name":"Ecwid","categories":["hosting"],"domains":["d3fi9i0jj23cau.cloudfront.net","d3j0zfs7paavns.cloudfront.net","ecwid.com","shopsettings.com"]},{"name":"issuu","categories":["content"],"domains":["issuu.com","isu.pub"]},{"name":"Effective Measure","categories":["ad"],"domains":["effectivemeasure.net"]},{"name":"Geniee","categories":["ad"],"domains":["cs.gssprt.jp","genieessp.jp","genieesspv.jp","gssprt.jp","href.asia"]},{"name":"Bronto Software","categories":["marketing"],"domains":["bm23.com","bronto.com","brontops.com"]},{"name":"eBay","categories":["ad"],"domains":["ebay.com","ebayimg.com","fetchback.com"]},{"name":"Elastic Ad","categories":["ad"],"domains":["elasticad.net"]},{"name":"PowerReviews","categories":["analytics"],"domains":["powerreviews.com"]},{"name":"Okas Concepts","categories":["utility"],"domains":["okasconcepts.com"]},{"name":"Media Management Technologies","categories":["ad"],"domains":["speedshiftmedia.com"]},{"name":"Blindado","categories":["utility"],"domains":["siteblindado.com"]},{"name":"Nativo","categories":["ad"],"domains":["postrelease.com"]},{"name":"Autopilot","categories":["ad"],"domains":["autopilothq.com"]},{"name":"Picreel","categories":["analytics"],"domains":["pcrl.co","picreel.com"]},{"name":"Celtra","categories":["ad"],"domains":["celtra.com"]},{"name":"UserReport","categories":["analytics"],"domains":["userreport.com"]},{"name":"Adverline Board","company":"Adverline","categories":["ad"],"domains":["adnext.fr","adverline.com"]},{"name":"The ADEX","categories":["ad"],"domains":["theadex.com"]},{"name":"Mather Economics","categories":["analytics"],"domains":["matheranalytics.com"]},{"name":"Decibel Insight","categories":["analytics"],"domains":["decibelinsight.net"]},{"name":"Revcontent","categories":["content"],"domains":["revcontent.com"]},{"name":"LightWidget","categories":["utility"],"domains":["lightwidget.com"]},{"name":"Wishpond Technologies","categories":["marketing"],"domains":["wishpond.com","wishpond.net"]},{"name":"Riskified","categories":["utility"],"domains":["riskified.com"]},{"name":"Kaltura Video Platform","company":"Kaltura","categories":["content"],"domains":["cdnsecakmi.kaltura.com"]},{"name":"TRUSTe","categories":["utility"],"domains":["truste.com"]},{"name":"Navegg","categories":["ad"],"domains":["navdmp.com"]},{"name":"LoopMe","categories":["ad"],"domains":["loopme.biz","loopme.com","loopme.me","vntsm.com"]},{"name":"Weborama","categories":["ad"],"domains":["weborama.com","weborama.fr"]},{"name":"Polar Mobile Group","categories":["ad"],"domains":["mediavoice.com","polarmobile.com"]},{"name":"Interpublic Group","categories":["ad"],"domains":["mbww.com"]},{"name":"Sekindo","categories":["content"],"domains":["sekindo.com"]},{"name":"WebEngage","categories":["customer-success"],"domains":["d23nd6ymopvz52.cloudfront.net","d3701cc9l7v9a6.cloudfront.net","webengage.co","webengage.com"]},{"name":"Cross Pixel Media","categories":["ad"],"domains":["crsspxl.com"]},{"name":"plista","categories":["ad"],"domains":["plista.com"]},{"name":"Kampyle","categories":["analytics"],"domains":["kampyle.com"]},{"name":"Tribal Fusion","company":"Exponential Interactive","categories":["ad"],"domains":["tribalfusion.com"]},{"name":"Gleam","categories":["marketing"],"domains":["gleam.io"]},{"name":"Forensiq","categories":["utility"],"domains":["fqtag.com"]},{"name":"Audience 360","company":"Datapoint Media","categories":["ad"],"domains":["dpmsrv.com"]},{"name":"Flowplayer","categories":["content"],"domains":["flowplayer.org"]},{"name":"Sooqr Search","company":"Sooqr","categories":["utility"],"domains":["sooqr.com"]},{"name":"MaxCDN Enterprise","company":"MaxCDN","categories":["utility"],"domains":["netdna-cdn.com","netdna-ssl.com"]},{"name":"Shopgate","categories":["utility"],"domains":["shopgate.com"]},{"name":"BoldChat","company":"LogMeIn","categories":["customer-success"],"domains":["boldchat.com"]},{"name":"smartclip","categories":["ad"],"domains":["smartclip.net"]},{"name":"rewardStyle.com","categories":["ad"],"domains":["rewardstyle.com"]},{"name":"Chitika","categories":["ad"],"domains":["chitika.net"]},{"name":"WisePops","categories":["utility"],"domains":["wisepops.com"]},{"name":"Monetate","categories":["analytics"],"domains":["monetate.net"]},{"name":"SpotXchange","categories":["ad"],"domains":["spotx.tv","spotxcdn.com","spotxchange.com"]},{"name":"Zanox","categories":["ad"],"domains":["zanox.com","zanox.ws"]},{"name":"SublimeSkinz","categories":["ad"],"domains":["ayads.co"]},{"name":"Adocean","company":"Gemius","categories":["ad"],"domains":["adocean.pl"]},{"name":"Meetrics","categories":["ad"],"domains":["meetrics.net","mxcdn.net","research.de.com"]},{"name":"Booking.com","categories":["content"],"domains":["bstatic.com"]},{"name":"Sparkflow","company":"Intercept Interactive","categories":["ad"],"domains":["sparkflow.net"]},{"name":"Lytics","categories":["ad"],"domains":["lytics.io"]},{"name":"ResponsiveVoice","categories":["other"],"domains":["responsivevoice.org"]},{"name":"Ooyala","categories":["ad"],"domains":["ooyala.com"]},{"name":"Snacktools","categories":["ad"],"domains":["bannersnack.com"]},{"name":"linkpulse","categories":["analytics"],"domains":["lp4.io"]},{"name":"Tencent","categories":["content"],"domains":["qq.com","ywxi.net"]},{"name":"Rocket Fuel","categories":["ad"],"domains":["rfihub.com","rfihub.net","ru4.com"]},{"name":"Adnium","categories":["ad"],"domains":["adnium.com"]},{"name":"Appier","categories":["ad"],"domains":["appier.net"]},{"name":"Stackla PTY","categories":["social"],"domains":["stackla.com"]},{"name":"Hupso Website Analyzer","company":"Hupso","categories":["analytics"],"domains":["hupso.com"]},{"name":"ReadSpeaker","homepage":"https://www.readspeaker.com","categories":["other"],"domains":["sf1-eu.readspeaker.com"]},{"name":"ShopiMind","company":"ShopIMind","categories":["ad"],"domains":["shopimind.com"]},{"name":"DialogTech","categories":["ad"],"domains":["dialogtech.com"]},{"name":"FoxyCart","categories":["utility"],"domains":["foxycart.com"]},{"name":"Neodata","categories":["ad"],"domains":["neodatagroup.com"]},{"name":"WebpageFX","categories":["ad"],"domains":["leadmanagerfx.com"]},{"name":"Smart Insight Tracking","company":"Emarsys","categories":["analytics"],"domains":["scarabresearch.com"]},{"name":"Feedbackify","company":"InsideMetrics","categories":["analytics"],"domains":["feedbackify.com"]},{"name":"Survicate","categories":["analytics"],"domains":["survicate.com"]},{"name":"Aggregate Knowledge","company":"Neustar","categories":["ad"],"domains":["agkn.com"]},{"name":"Exponea","categories":["analytics"],"domains":["exponea.com"]},{"name":"eXelate","categories":["ad"],"domains":["exelator.com"]},{"name":"Adition","homepage":"https://www.adition.com","categories":["ad"],"domains":["dsp.adfarm1.adition.com"]},{"name":"Highcharts","categories":["utility"],"domains":["highcharts.com"]},{"name":"FirstImpression","categories":["ad"],"domains":["firstimpression.io"]},{"name":"LiveHelpNow","categories":["customer-success"],"domains":["livehelpnow.net"]},{"name":"SearchSpring","categories":["utility"],"domains":["searchspring.net"]},{"name":"Pardot","categories":["marketing"],"domains":["pardot.com"]},{"name":"JustPremium Ads","company":"JustPremium","categories":["ad"],"domains":["justpremium.com"]},{"name":"DMD Marketing","homepage":"https://www.dmdconnects.com/","categories":["ad"],"domains":["medtargetsystem.com"]},{"name":"The Hut Group","categories":["content"],"domains":["thcdn.com"]},{"name":"Cloudinary","categories":["content"],"domains":["cloudinary.com"]},{"name":"Technorati","company":"Synacor","categories":["ad"],"domains":["technoratimedia.com"]},{"name":"Github","categories":["utility"],"domains":["github.com","github.io","raw.githubusercontent.com"]},{"name":"Bootstrap Chinese network","categories":["library"],"domains":["bootcss.com"]},{"name":"Typepad","categories":["hosting"],"domains":["typepad.com"]},{"name":"Keywee","categories":["ad"],"domains":["keywee.co"]},{"name":"Skype","categories":["other"],"domains":["skype.com"]},{"name":"Opta","company":"Perform Group","categories":["content"],"domains":["opta.net"]},{"name":"Livefyre","categories":["content"],"domains":["fyre.co","livefyre.com"]},{"name":"ReTargeter","categories":["ad"],"domains":["retargeter.com"]},{"name":"TruConversion","categories":["analytics"],"domains":["truconversion.com"]},{"name":"fifty-five","categories":["ad"],"domains":["55labs.com"]},{"name":"Time","categories":["content"],"domains":["timeinc.net"]},{"name":"Pixlee","categories":["social"],"domains":["pixlee.com"]},{"name":"Reevoo","categories":["analytics"],"domains":["reevoo.com"]},{"name":"Accordant Media","categories":["ad"],"domains":["segment.a3cloud.net"]},{"name":"Evergage","categories":["analytics"],"domains":["evergage.com"]},{"name":"Exponential Interactive","categories":["ad"],"domains":["exponential.com"]},{"name":"Best Of Media S.A.","categories":["content"],"domains":["servebom.com"]},{"name":"Steelhouse","categories":["ad"],"domains":["steelhousemedia.com"]},{"name":"Dailymotion","categories":["content"],"domains":["ad.pxlad.io","dm.gg","dmcdn.net","sublimevideo.net","www.dailymotion.com"]},{"name":"TripleLift","categories":["ad"],"domains":["3lift.com"]},{"name":"DemandBase","categories":["marketing"],"domains":["demandbase.com"]},{"name":"One by AOL","company":"AOL","categories":["ad"],"domains":["adtech.de","adtechjp.com"]},{"name":"Adkontekst","categories":["ad"],"domains":["adkontekst.pl"]},{"name":"Profitshare","categories":["ad"],"domains":["profitshare.ro"]},{"name":"Drip","company":"The Numa Group","categories":["ad"],"domains":["getdrip.com"]},{"name":"Ghostery Enterprise","company":"Ghostery","categories":["marketing"],"domains":["betrad.com"]},{"name":"Socialphotos","categories":["social"],"domains":["slpht.com"]},{"name":"SoundCloud","homepage":"https://www.soundcloud.com/","categories":["content"],"domains":["widget.sndcdn.com","soundcloud.com","stratus.sc"]},{"name":"Rackspace","categories":["hosting"],"domains":["rackcdn.com","rackspacecloud.com","raxcdn.com","websitetestlink.com"]},{"name":"NetAffiliation","company":"Kwanco","categories":["ad"],"domains":["metaffiliation.com"]},{"name":"CPEx","categories":["content"],"domains":["cpex.cz"]},{"name":"Sweet Tooth","categories":["ad"],"domains":["sweettooth.io"]},{"name":"Playbuzz","categories":["hosting"],"domains":["playbuzz.com"]},{"name":"Civic","categories":["hosting"],"domains":["civiccomputing.com"]},{"name":"Sajari Pty","categories":["utility"],"domains":["sajari.com"]},{"name":"PerimeterX Bot Defender","company":"PerimeterX","categories":["utility"],"domains":["perimeterx.net","pxi.pub"]},{"name":"Marketplace Web Service","company":"Amazon","categories":["other"],"domains":["ssl-images-amazon.com"]},{"name":"Ambassador","categories":["ad"],"domains":["getambassador.com"]},{"name":"Pictela (AOL)","categories":["analytics"],"domains":["pictela.net"]},{"name":"AdSniper","categories":["ad"],"domains":["adsniper.ru","sniperlog.ru"]},{"name":"Adscale","categories":["ad"],"domains":["adscale.de"]},{"name":"Signyfyd","categories":["utility"],"domains":["signifyd.com"]},{"name":"Connatix","categories":["ad"],"domains":["connatix.com"]},{"name":"Zarget","categories":["analytics"],"domains":["zarget.com"]},{"name":"Woopra","categories":["analytics"],"domains":["woopra.com"]},{"name":"Infinity Tracking","categories":["analytics"],"domains":["infinity-tracking.net"]},{"name":"ResponseTap","categories":["analytics"],"domains":["adinsight.com","responsetap.com"]},{"name":"Sirv","categories":["other"],"domains":["sirv.com"]},{"name":"Salesforce.com","categories":["ad"],"domains":["force.com","salesforce.com","secure.force.com"]},{"name":"Conversant Tag Manager","company":"Conversant","categories":["tag-manager"],"domains":["mplxtms.com"]},{"name":"Petametrics","categories":["analytics"],"domains":["petametrics.com"]},{"name":"BannerFlow","company":"Nordic Factory Solutions","categories":["ad"],"domains":["bannerflow.com"]},{"name":"Pixalate","categories":["utility"],"domains":["adrta.com"]},{"name":"reEmbed","categories":["other"],"domains":["reembed.com"]},{"name":"FreakOut","categories":["ad"],"domains":["fout.jp"]},{"name":"VoiceFive","categories":["analytics"],"domains":["voicefive.com"]},{"name":"Impact Radius","categories":["ad"],"domains":["7eer.net","a.impactradius-go.com","d3cxv97fi8q177.cloudfront.net","impactradius-event.com","microsoft-uk.evyy.net","ojrq.net"]},{"name":"AWeber","categories":["ad"],"domains":["aweber.com"]},{"name":"Simpli.fi","categories":["ad"],"domains":["simpli.fi"]},{"name":"Unruly Media","categories":["ad"],"domains":["unrulymedia.com"]},{"name":"Hola Networks","categories":["other"],"domains":["h-cdn.com"]},{"name":"Customer.io","categories":["ad"],"domains":["customer.io"]},{"name":"Delta Projects AB","categories":["ad"],"domains":["de17a.com"]},{"name":"Advance Magazine Group","categories":["content"],"domains":["condenast.co.uk","condenastdigital.com","condenet.com"]},{"name":"Key CDN","categories":["utility"],"domains":["kxcdn.com"]},{"name":"ThreatMetrix","categories":["utility"],"domains":["online-metrix.net"]},{"name":"Adtech (AOL)","categories":["ad"],"domains":["adtechus.com"]},{"name":"News","categories":["social"],"domains":["news-static.com","news.com.au","newsanalytics.com.au","newsapi.com.au","newscdn.com.au","newsdata.com.au","newsdiscover.com.au"]},{"name":"AvantLink","categories":["ad"],"domains":["avmws.com"]},{"name":"CyberSource (Visa)","categories":["utility"],"domains":["authorize.net"]},{"name":"Vibrant Media","categories":["ad"],"domains":["intellitxt.com","picadmedia.com"]},{"name":"FLXone","company":"Teradata","categories":["ad"],"domains":["d2hlpp31teaww3.cloudfront.net","flx1.com","pangolin.blue"]},{"name":"Adobe Marketing Cloud","company":"Adobe Systems","categories":["ad"],"domains":["adobetag.com"]},{"name":"WebSpectator","categories":["ad"],"domains":["webspectator.com"]},{"name":"Intercept Interactive","categories":["ad"],"domains":["undertone.com"]},{"name":"Simplicity Marketing","categories":["ad"],"domains":["flashtalking.com"]},{"name":"AdRiver","categories":["ad"],"domains":["adriver.ru"]},{"name":"Mobify","categories":["utility"],"domains":["mobify.com","mobify.net"]},{"name":"Apester","categories":["analytics"],"domains":["apester.com","qmerce.com"]},{"name":"Covert Pics","categories":["content"],"domains":["covet.pics"]},{"name":"CleverDATA","categories":["ad"],"domains":["1dmp.io"]},{"name":"SecuredVisit","company":"4Cite Marketing","categories":["ad"],"domains":["securedvisit.com"]},{"name":"SlimCut Media Outstream","company":"SlimCut Media","categories":["ad"],"domains":["freeskreen.com"]},{"name":"Exactag","categories":["ad"],"domains":["exactag.com"]},{"name":"Postcode Anywhere (Holdings)","categories":["utility"],"domains":["postcodeanywhere.co.uk"]},{"name":"Flickr","categories":["content"],"domains":["flickr.com","staticflickr.com"]},{"name":"bRealTime","categories":["ad"],"domains":["brealtime.com"]},{"name":"Research Online","company":"Skills Development Scotland","categories":["content"],"domains":["www.researchonline.org.uk"]},{"name":"Swoop","categories":["ad"],"domains":["swoop.com"]},{"name":"Widespace","homepage":"https://www.widespace.com","categories":["ad"],"domains":["sync.widespace.com"]},{"name":"Eyeota","categories":["ad"],"domains":["eyeota.net"]},{"name":"Pagefair","categories":["ad"],"domains":["pagefair.com","pagefair.net"]},{"name":"Wow Analytics","categories":["analytics"],"domains":["wowanalytics.co.uk"]},{"name":"Rakuten LinkShare","company":"Rakuten","categories":["ad"],"domains":["linksynergy.com"]},{"name":"Transifex","categories":["utility"],"domains":["transifex.com"]},{"name":"Ziff Davis Tech","categories":["ad"],"domains":["adziff.com","zdbb.net"]},{"name":"Betgenius","company":"Genius Sports","categories":["content"],"domains":["connextra.com"]},{"name":"AIR.TV","categories":["ad"],"domains":["air.tv"]},{"name":"MaxMind","categories":["utility"],"domains":["maxmind.com"]},{"name":"Expedia","categories":["content"],"domains":["travel-assets.com","trvl-media.com","www.trvl-px.com","www.uciservice.com"]},{"name":"ContextWeb","categories":["ad"],"domains":["contextweb.com"]},{"name":"Pusher","homepage":"https://pusher.com/","categories":["utility"],"domains":["stats.pusher.com","pusherapp.com"]},{"name":"LeasdBoxer","company":"LeadBoxer","categories":["ad"],"domains":["leadboxer.com"]},{"name":"SkyScanner","categories":["content"],"domains":["api.skyscanner.net"]},{"name":"WalkMe","categories":["customer-success"],"domains":["walkme.com"]},{"name":"AdTrue","company":"FPT AdTrue","categories":["ad"],"domains":["adtrue.com"]},{"name":"Resonance Insights","categories":["analytics"],"domains":["res-x.com"]},{"name":"Hull.js","categories":["utility"],"domains":["hull.io","hullapp.io"]},{"name":"Video Media Groep","categories":["ad"],"domains":["inpagevideo.nl","vmg.host"]},{"name":"MonetizeMore","categories":["ad"],"domains":["m2.ai"]},{"name":"Fanplayr","categories":["analytics"],"domains":["d38nbbai6u794i.cloudfront.net","fanplayr.com"]},{"name":"Onet","categories":["ad"],"domains":["onet.pl"]},{"name":"Boomtrain","categories":["ad"],"domains":["boomtrain.com","boomtrain.net"]},{"name":"Proper Media","categories":["content"],"domains":["proper.io"]},{"name":"StumbleUpon","categories":["content"],"domains":["stumble-upon.com","stumbleupon.com"]},{"name":"Zmags","categories":["marketing"],"domains":["zmags.com"]},{"name":"Vee24","categories":["customer-success"],"domains":["vee24.com"]},{"name":"Sailthru","categories":["analytics"],"domains":["sail-horizon.com","sail-personalize.com","sail-track.com"]},{"name":"Klevu Search","company":"Klevu","categories":["utility"],"domains":["klevu.com"]},{"name":"Cedato","categories":["ad"],"domains":["algovid.com","vdoserv.com"]},{"name":"Trip Advisor","categories":["content"],"domains":["tacdn.com","tripadvisor.co.uk","tripadvisor.com","viator.com","www.jscache.com","www.tamgrt.com"]},{"name":"Captify Media","categories":["ad"],"domains":["cpx.to"]},{"name":"Yottaa","categories":["hosting"],"domains":["yottaa.com","yottaa.net"]},{"name":"PERFORM","categories":["content"],"domains":["performgroup.com"]},{"name":"Vindico","company":"Viant","categories":["ad"],"domains":["vindicosuite.com"]},{"name":"Snack Media","categories":["content"],"domains":["snack-media.com"]},{"name":"FuelX","categories":["ad"],"domains":["fuelx.com"]},{"name":"OnScroll","categories":["ad"],"domains":["onscroll.com"]},{"name":"Alliance for Audited Media","categories":["ad"],"domains":["aamsitecertifier.com"]},{"name":"ShopRunner","categories":["content"],"domains":["s-9.us","shoprunner.com"]},{"name":"Janrain","categories":["analytics"],"domains":["d3hmp0045zy3cs.cloudfront.net","janrain.com","janrainbackplane.com","rpxnow.com"]},{"name":"AliveChat","company":"AYU Technology Solutions","categories":["customer-success"],"domains":["websitealive.com","websitealive7.com"]},{"name":"SpringServer","categories":["ad"],"domains":["springserve.com"]},{"name":"Global-e","categories":["hosting"],"domains":["global-e.com"]},{"name":"cloudIQ","categories":["analytics"],"domains":["cloud-iq.com"]},{"name":"ZEDO","categories":["ad"],"domains":["zedo.com"]},{"name":"Forter","categories":["utility"],"domains":["forter.com"]},{"name":"Silverpop","company":"IBM","categories":["ad"],"domains":["mkt51.net","mkt61.net","mkt912.com","mkt922.com","mkt932.com","mkt941.com","pages01.net","pages02.net","pages03.net","pages04.net","pages05.net"]},{"name":"Polyfill service","company":"Polyfill.io","categories":["other"],"domains":["polyfill.io"]},{"name":"epoq internet services","categories":["analytics"],"domains":["epoq.de"]},{"name":"CDN.net","categories":["utility"],"domains":["uk.cdn-net.com"]},{"name":"Kameleoon","categories":["analytics"],"domains":["kameleoon.com"]},{"name":"Council ad Network","categories":["ad"],"domains":["counciladvertising.net"]},{"name":"Oracle Recommendations On Demand","company":"Oracle","categories":["analytics"],"domains":["atgsvcs.com"]},{"name":"Viacom","categories":["content"],"domains":["mtvnservices.com"]},{"name":"Optimove","company":"Mobius Solutions","categories":["analytics"],"domains":["optimove.net"]},{"name":"Cookie Reports","categories":["utility"],"domains":["cookiereports.com"]},{"name":"Storygize","categories":["ad"],"domains":["www.storygize.net"]},{"name":"Revolver Maps","categories":["analytics"],"domains":["revolvermaps.com"]},{"name":"Reactful","categories":["analytics"],"domains":["reactful.com"]},{"name":"NaviStone","categories":["ad"],"domains":["murdoog.com"]},{"name":"Vertical Mass","categories":["ad"],"domains":["vmweb.net"]},{"name":"Conversant","categories":["analytics"],"domains":["dotomi.com","dtmpub.com","emjcd.com","fastclick.net","mediaplex.com","www.tqlkg.com"]},{"name":"BlueCava","categories":["ad"],"domains":["bluecava.com"]},{"name":"VidPulse","categories":["analytics"],"domains":["vidpulse.com"]},{"name":"LoginRadius","categories":["social"],"domains":["loginradius.com"]},{"name":"Byside","homepage":"http://www.byside.com","categories":["analytics"],"domains":["byce2.byside.com","wce2.byside.com"]},{"name":"MailPlus","categories":["ad"],"domains":["mailplus.nl"]},{"name":"Touch Commerce","categories":["customer-success"],"domains":["inq.com","touchcommerce.com"]},{"name":"Netlify","homepage":"https://www.netlify.com/","categories":["utility"],"domains":["netlify.com","cloud.netlifyusercontent.com"]},{"name":"Kargo","categories":["marketing"],"domains":["kargo.com"]},{"name":"SurveyMonkey","categories":["analytics"],"domains":["surveymonkey.com"]},{"name":"User Replay","categories":["analytics"],"domains":["userreplay.net"]},{"name":"Catchpoint","homepage":"https://www.catchpoint.com/","categories":["analytics"],"domains":["3gl.net"]},{"name":"Conversio","categories":["ad"],"domains":["conversio.com"]},{"name":"AdvertServe","categories":["ad"],"domains":["advertserve.com"]},{"name":"PrintFriendly","categories":["utility"],"domains":["printfriendly.com"]},{"name":"Mopinion","categories":["analytics"],"domains":["mopinion.com"]},{"name":"Barilliance","categories":["analytics"],"domains":["barilliance.net","dn3y71tq7jf07.cloudfront.net"]},{"name":"Flockler","categories":["ad"],"domains":["flockler.com"]},{"name":"Attribution","categories":["ad"],"domains":["attributionapp.com"]},{"name":"Vergic AB","categories":["customer-success"],"domains":["psplugin.com"]},{"name":"CANDDi","company":"Campaign and Digital Intelligence","categories":["ad"],"domains":["canddi.com"]},{"name":"PebblePost","categories":["ad"],"domains":["pbbl.co"]},{"name":"Braintree Payments","company":"Paypal","categories":["utility"],"domains":["braintreegateway.com"]},{"name":"InSkin Media","categories":["ad"],"domains":["inskinad.com","inskinmedia.com"]},{"name":"StreamRail","categories":["ad"],"domains":["streamrail.com","streamrail.net"]},{"name":"Site24x7 Real User Monitoring","company":"Site24x7","categories":["analytics"],"domains":["site24x7rum.com"]},{"name":"YoYo","categories":["utility"],"domains":["goadservices.com"]},{"name":"Adunity","categories":["ad"],"domains":["adunity.com"]},{"name":"PlayAd Media Group","categories":["ad"],"domains":["youplay.se"]},{"name":"BuySellAds","categories":["ad"],"domains":["buysellads.com"]},{"name":"Moovweb","categories":["utility"],"domains":["moovweb.net"]},{"name":"Bookatable","categories":["content"],"domains":["bookatable.com","livebookings.com"]},{"name":"Raygun","categories":["utility"],"domains":["raygun.io"]},{"name":"Sociomantic Labs","company":"DunnHumby","categories":["ad"],"domains":["sociomantic.com"]},{"name":"Borderfree","company":"pitney bowes","categories":["utility"],"domains":["borderfree.com","fiftyone.com"]},{"name":"Dynamic Converter","categories":["utility"],"domains":["dynamicconverter.com"]},{"name":"C3 Metrics","categories":["analytics"],"domains":["c3tag.com"]},{"name":"eGain","categories":["analytics"],"domains":["analytics-egain.com","egain.com"]},{"name":"TechTarget","categories":["content"],"domains":["techtarget.com","ttgtmedia.com"]},{"name":"Adobe Scene7","company":"Adobe Systems","categories":["content"],"domains":["everestads.net","everestjs.net","scene7.com","wwwimages.adobe.com"]},{"name":"HotelsCombined","categories":["content"],"domains":["datahc.com"]},{"name":"StackAdapt","categories":["ad"],"domains":["stackadapt.com"]},{"name":"The Publisher Desk","categories":["ad"],"domains":["206ads.com","publisherdesk.com"]},{"name":"Ekm Systems","categories":["analytics"],"domains":["ekmpinpoint.co.uk","ekmsecure.com","globalstats.ekmsecure.com"]},{"name":"DistroScale","categories":["ad"],"domains":["jsrdn.com"]},{"name":"Knight Lab","company":"Northwestern University","categories":["utility"],"domains":["knightlab.com"]},{"name":"Vergic Engage Platform","company":"Vergic","categories":["customer-success"],"domains":["vergic.com"]},{"name":"AdCurve","categories":["ad"],"domains":["shop2market.com"]},{"name":"StackExchange","categories":["social"],"domains":["sstatic.net"]},{"name":"MathJax","categories":["utility"],"domains":["mathjax.org"]},{"name":"RebelMouse","categories":["ad"],"domains":["rbl.ms","www.rebelmouse.com"]},{"name":"ShopStorm","categories":["utility"],"domains":["shopstorm.com"]},{"name":"Ad6Media","categories":["ad"],"domains":["ad6media.fr"]},{"name":"OCSP","company":"GoDaddy","categories":["utility"],"domains":["ocsp.godaddy.com","seal.godaddy.com"]},{"name":"Bluecore","categories":["analytics"],"domains":["www.bluecore.com"]},{"name":"Cachefly","categories":["utility"],"domains":["cachefly.net"]},{"name":"Nanorep","company":"Nanorep Technologies","categories":["customer-success"],"domains":["nanorep.com"]},{"name":"AdSpruce","categories":["ad"],"domains":["adspruce.com"]},{"name":"content.ad","categories":["ad"],"domains":["content.ad"]},{"name":"Improve Digital","categories":["ad"],"domains":["360yield.com"]},{"name":"Fastest Forward","categories":["analytics"],"domains":["gaug.es"]},{"name":"RichRelevance","categories":["analytics"],"domains":["richrelevance.com"]},{"name":"ARM","categories":["analytics"],"domains":["tag4arm.com"]},{"name":"Webtrends","categories":["analytics"],"domains":["d1q62gfb8siqnm.cloudfront.net","webtrends.com","webtrendslive.com"]},{"name":"Click4Assistance","categories":["customer-success"],"domains":["click4assistance.co.uk"]},{"name":"Realytics","categories":["analytics"],"domains":["dcniko1cv0rz.cloudfront.net","realytics.net"]},{"name":"Xaxis","homepage":"https://www.xaxis.com/","categories":["ad"],"domains":["t.mookie1.com","247realmedia.com","gmads.net","odr.mookie1.com"]},{"name":"UPS i-parcel","company":"UPS","categories":["other"],"domains":["i-parcel.com"]},{"name":"Qualtrics","categories":["analytics"],"domains":["qualtrics.com"]},{"name":"Adobe Test & Target","company":"Adobe Systems","categories":["analytics"],"domains":["tt.omtrdc.net"]}];
-},{}],172:[function(require,module,exports){
+},{}],168:[function(require,module,exports){
 module.exports=require("./lib/subsets/httparchive-nostats.js");
 
-},{"./lib/subsets/httparchive-nostats.js":174}],173:[function(require,module,exports){
+},{"./lib/subsets/httparchive-nostats.js":170}],169:[function(require,module,exports){
 const DOMAIN_IN_URL_REGEX=/:\/\/(.*?)(\/|$)/;
 const DOMAIN_CHARACTERS=/[a-z0-9.-]+\.[a-z0-9]+/i;
 const ROOT_DOMAIN_REGEX=/[^.]+\.([^.]+|(gov|com|co|ne)\.\w{2})$/i;
@@ -65554,12 +64756,12 @@
 
 module.exports={createAPIFromDataset};
 
-},{}],174:[function(require,module,exports){
+},{}],170:[function(require,module,exports){
 const{createAPIFromDataset}=require('../create-entity-finder-api.js');
 const entities=require('../../dist/entities-httparchive-nostats.json');
 module.exports=createAPIFromDataset(entities);
 
-},{"../../dist/entities-httparchive-nostats.json":171,"../create-entity-finder-api.js":173}],175:[function(require,module,exports){
+},{"../../dist/entities-httparchive-nostats.json":167,"../create-entity-finder-api.js":169}],171:[function(require,module,exports){
 (function(setImmediate,clearImmediate){
 var nextTick=require('process/browser.js').nextTick;
 var apply=Function.prototype.apply;
@@ -65638,7 +64840,7 @@
 delete immediateIds[id];
 };
 }).call(this,require("timers").setImmediate,require("timers").clearImmediate);
-},{"process/browser.js":145,"timers":175}],176:[function(require,module,exports){
+},{"process/browser.js":141,"timers":171}],172:[function(require,module,exports){
 (function(global){
 
 
@@ -65709,15 +64911,15 @@
 }
 
 }).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{});
-},{}],177:[function(require,module,exports){
+},{}],173:[function(require,module,exports){
+arguments[4][93][0].apply(exports,arguments);
+},{"dup":93}],174:[function(require,module,exports){
 arguments[4][94][0].apply(exports,arguments);
-},{"dup":94}],178:[function(require,module,exports){
-arguments[4][95][0].apply(exports,arguments);
-},{"./support/isBuffer":177,"_process":145,"dup":95,"inherits":113}],179:[function(require,module,exports){
+},{"./support/isBuffer":173,"_process":141,"dup":94,"inherits":112}],175:[function(require,module,exports){
 module.exports={
-"version":"5.2.0"};
+"version":"5.4.0"};
 
-},{}],180:[function(require,module,exports){
+},{}],176:[function(require,module,exports){
 
 
 
@@ -65733,7 +64935,7 @@
 
 module.exports=stackPacks;
 
-},{"./packs/wordpress.js":181}],181:[function(require,module,exports){
+},{"./packs/wordpress.js":177}],177:[function(require,module,exports){
 (function(__filename){
 
 
@@ -65805,7 +65007,7 @@
 module.exports.UIStrings=UIStrings;
 
 }).call(this,"/stack-packs/packs/wordpress.js");
-},{"../../lighthouse-core/lib/i18n/i18n.js":68}],182:[function(require,module,exports){
+},{"../../lighthouse-core/lib/i18n/i18n.js":67}],178:[function(require,module,exports){
 module.exports={
 "npm":{
 "angular":[
@@ -65960,6 +65162,7 @@
 return url.replace(/^chrome:\/\/chrome\//,'chrome://');
 }
 
+
 class URLShim extends URL{
 
 
@@ -66111,4 +65314,4 @@
 
 module.exports=URLShim;
 
-},{"../report/html/renderer/util.js":88}]},{},[1]);
\ No newline at end of file
+},{"../report/html/renderer/util.js":87}]},{},[1]);
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_reader.cc b/third_party/blink/renderer/modules/clipboard/clipboard_reader.cc
index ce39523..496ad2156 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_reader.cc
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_reader.cc
@@ -31,9 +31,12 @@
     SkPixmap pixmap;
     bitmap.peekPixels(&pixmap);
 
-    Vector<uint8_t> png_data;
+    // Set encoding options to favor speed over size.
     SkPngEncoder::Options options;
     options.fZLibLevel = 1;
+    options.fFilterFlags = SkPngEncoder::FilterFlag::kNone;
+
+    Vector<uint8_t> png_data;
     if (!ImageEncoder::Encode(&png_data, pixmap, options))
       return nullptr;
 
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source.cc b/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source.cc
index a2129ed7..e4b9b8d8 100644
--- a/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source.cc
+++ b/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source.cc
@@ -122,12 +122,15 @@
   const base::TimeTicks current_time = base::TimeTicks::Now();
   if (start_capture_time_.is_null())
     start_capture_time_ = current_time;
-  const blink::WebSize resolution = web_media_player_->NaturalSize();
 
   if (!canvas_ || is_opaque_ != web_media_player_->IsOpaque()) {
+    // TODO(crbug.com/964494): Avoid the explicit conversion to gfx::Size here.
+    // TODO(sandersd): Implement support for size changes rather than scaling.
+    if (!canvas_)
+      natural_size_ = gfx::Size(web_media_player_->NaturalSize());
     is_opaque_ = web_media_player_->IsOpaque();
     if (!bitmap_.tryAllocPixels(SkImageInfo::MakeN32(
-            resolution.width, resolution.height,
+            natural_size_.width(), natural_size_.height(),
             is_opaque_ ? kOpaque_SkAlphaType : kPremul_SkAlphaType))) {
       running_callback_.Run(false);
       return;
@@ -138,12 +141,11 @@
   cc::PaintFlags flags;
   flags.setBlendMode(SkBlendMode::kSrc);
   flags.setFilterQuality(kLow_SkFilterQuality);
-  web_media_player_->Paint(
-      canvas_.get(), blink::WebRect(0, 0, resolution.width, resolution.height),
-      flags);
+  // TODO(crbug.com/964494): Avoid the explicit conversion to blink::WebRect
+  // here.
+  web_media_player_->Paint(canvas_.get(),
+                           blink::WebRect(gfx::Rect(natural_size_)), flags);
   DCHECK_NE(kUnknown_SkColorType, canvas_->imageInfo().colorType());
-  DCHECK_EQ(canvas_->imageInfo().width(), resolution.width);
-  DCHECK_EQ(canvas_->imageInfo().height(), resolution.height);
 
   DCHECK_NE(kUnknown_SkColorType, bitmap_.colorType());
   DCHECK(!bitmap_.drawsNothing());
@@ -153,11 +155,9 @@
     return;
   }
 
-  // TODO(crbug.com/964494): Avoid the explicit convertion to gfx::Size here.
-  gfx::Size gfx_resolution = gfx::Size(resolution);
   scoped_refptr<media::VideoFrame> frame = frame_pool_.CreateFrame(
       is_opaque_ ? media::PIXEL_FORMAT_I420 : media::PIXEL_FORMAT_I420A,
-      gfx_resolution, gfx::Rect(gfx_resolution), gfx_resolution,
+      natural_size_, gfx::Rect(natural_size_), natural_size_,
       current_time - start_capture_time_);
 
   const uint32_t source_pixel_format =
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source.h b/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source.h
index 2ba1532..081ea9d 100644
--- a/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source.h
+++ b/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source.h
@@ -64,6 +64,7 @@
   media::VideoFramePool frame_pool_;
   SkBitmap bitmap_;
   std::unique_ptr<cc::PaintCanvas> canvas_;
+  gfx::Size natural_size_;
 
   const base::WeakPtr<blink::WebMediaPlayer> web_media_player_;
   const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source_unittest.cc b/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source_unittest.cc
index c2c1bb06..c0c0ffe 100644
--- a/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source_unittest.cc
+++ b/third_party/blink/renderer/modules/mediacapturefromelement/html_video_element_capturer_source_unittest.cc
@@ -58,8 +58,8 @@
                  WebSetSinkIdCompleteCallback) override {}
   bool HasVideo() const override { return true; }
   bool HasAudio() const override { return false; }
-  WebSize NaturalSize() const override { return WebSize(16, 10); }
-  WebSize VisibleRect() const override { return WebSize(16, 10); }
+  WebSize NaturalSize() const override { return size_; }
+  WebSize VisibleRect() const override { return size_; }
   bool Paused() const override { return false; }
   bool Seeking() const override { return false; }
   double Duration() const override { return 0.0; }
@@ -98,6 +98,7 @@
   }
 
   bool is_video_opaque_ = true;
+  WebSize size_ = WebSize(16, 10);
 
   base::WeakPtrFactory<MockWebMediaPlayer> weak_factory_{this};
 };
@@ -128,6 +129,8 @@
     web_media_player_->is_video_opaque_ = opacity;
   }
 
+  void SetVideoPlayerSize(WebSize size) { web_media_player_->size_ = size; }
+
  protected:
   std::unique_ptr<MockWebMediaPlayer> web_media_player_;
   std::unique_ptr<HtmlVideoElementCapturerSource> html_video_capturer_;
@@ -284,4 +287,48 @@
   Mock::VerifyAndClearExpectations(this);
 }
 
+// Verify that changes in the natural size of the source WebMediaPlayer do not
+// crash.
+// TODO(crbug.com/1817203): Verify that size changes are fully supported.
+TEST_F(HTMLVideoElementCapturerSourceTest, SizeChange) {
+  InSequence s;
+  media::VideoCaptureFormats formats =
+      html_video_capturer_->GetPreferredFormats();
+  media::VideoCaptureParams params;
+  params.requested_format = formats[0];
+
+  {
+    SetVideoPlayerSize(WebSize(16, 10));
+
+    base::RunLoop run_loop;
+    base::RepeatingClosure quit_closure = run_loop.QuitClosure();
+    scoped_refptr<media::VideoFrame> frame;
+    EXPECT_CALL(*this, DoOnRunning(true)).Times(1);
+    EXPECT_CALL(*this, DoOnDeliverFrame(_, _))
+        .WillOnce(
+            DoAll(SaveArg<0>(&frame), RunClosure2(std::move(quit_closure))));
+    html_video_capturer_->StartCapture(
+        params,
+        WTF::BindRepeating(&HTMLVideoElementCapturerSourceTest::OnDeliverFrame,
+                           base::Unretained(this)),
+        WTF::BindRepeating(&HTMLVideoElementCapturerSourceTest::OnRunning,
+                           base::Unretained(this)));
+    run_loop.Run();
+  }
+  {
+    SetVideoPlayerSize(WebSize(32, 20));
+
+    base::RunLoop run_loop;
+    base::RepeatingClosure quit_closure = run_loop.QuitClosure();
+    scoped_refptr<media::VideoFrame> frame;
+    EXPECT_CALL(*this, DoOnDeliverFrame(_, _))
+        .WillOnce(
+            DoAll(SaveArg<0>(&frame), RunClosure2(std::move(quit_closure))));
+    run_loop.Run();
+  }
+
+  html_video_capturer_->StopCapture();
+  Mock::VerifyAndClearExpectations(this);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager.cc b/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager.cc
index c06bd7c..e4c2615 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_installed_scripts_manager.cc
@@ -269,10 +269,10 @@
 
   // Don't touch |installed_urls_| after this point. We're on the initiator
   // thread now, but |installed_urls_| will be accessed on the
-  // worker thread later, so they should keep isolated from the current thraed.
+  // worker thread later, so they should keep isolated from the current thread.
   for (const WebURL& url :
        installed_scripts_manager_params->installed_scripts_urls) {
-    installed_urls_.insert(url);
+    installed_urls_.insert(KURL(url).Copy());
   }
 
   PostCrossThreadTask(
diff --git a/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc b/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
index 0ac2717..9f871bf 100644
--- a/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
+++ b/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
@@ -268,25 +268,6 @@
   worker_->WaitForShutdownForTesting();
 }
 
-// TODO(nhiroki): Remove this. This test is the same with
-// TerminateSoonAfterStart() because off-the-main-thread worker script loading
-// got enabled by default.
-TEST_F(WebEmbeddedWorkerImplTest, TerminateWhileLoadingScript) {
-  worker_->StartWorkerContext(
-      CreateStartData(),
-      /*installed_scripts_manager_params=*/nullptr,
-      /*content_settings_proxy=*/mojo::ScopedMessagePipeHandle(),
-      /*cache_storage_remote=*/mojo::ScopedMessagePipeHandle(),
-      /*interface_provider_info=*/mojo::ScopedMessagePipeHandle(),
-      /*browser_interface_broker=*/mojo::ScopedMessagePipeHandle(),
-      Thread::Current()->GetTaskRunner());
-  testing::Mock::VerifyAndClearExpectations(mock_client_.get());
-
-  // Terminate before finishing the script load.
-  worker_->TerminateWorkerContext();
-  worker_->WaitForShutdownForTesting();
-}
-
 TEST_F(WebEmbeddedWorkerImplTest, ScriptNotFound) {
   WebURL script_url = url_test_helpers::ToKURL(kNotFoundScriptURL);
   WebURLResponse response;
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc
index 37d8ba0..13fd3241 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc
@@ -5,7 +5,7 @@
 #include "third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.h"
 
 #include "base/logging.h"
-#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/mojom/wake_lock/wake_lock.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
@@ -37,10 +37,8 @@
   // 4.3. Add lockPromise to record.[[WakeLockStateRecord]].
   // 5. Return active.
   if (!wake_lock_) {
-    auto* interface_provider = execution_context_->GetInterfaceProvider();
-    DCHECK(interface_provider);
     mojo::Remote<mojom::blink::WakeLockService> wake_lock_service;
-    interface_provider->GetInterface(
+    execution_context_->GetBrowserInterfaceBroker().GetInterface(
         wake_lock_service.BindNewPipeAndPassReceiver());
 
     wake_lock_service->GetWakeLock(
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.cc
index ec0e3e5..deddd602 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.cc
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.cc
@@ -10,7 +10,6 @@
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_function.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_exception.h"
@@ -248,9 +247,7 @@
 
 WakeLockTestingContext::WakeLockTestingContext(
     MockWakeLockService* mock_wake_lock_service) {
-  service_manager::InterfaceProvider::TestApi test_api(
-      GetDocument()->GetInterfaceProvider());
-  test_api.SetBinderForName(
+  GetDocument()->GetBrowserInterfaceBroker().SetBinderForTesting(
       mojom::blink::WakeLockService::Name_,
       WTF::BindRepeating(&MockWakeLockService::BindRequest,
                          WTF::Unretained(mock_wake_lock_service)));
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h b/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h
index 03a76b5e..c69f923 100644
--- a/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h
+++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_test_utils.h
@@ -141,7 +141,7 @@
 //   MockWakeLockService mock_service;
 //   WakeLockTestingContext context(&mock_service);
 //   mojo::Remote<mojom::blink::WakeLockService> service;
-//   context.GetDocument()->GetInterfaceProvider()->GetInterface(
+//   context.GetDocument()->GetBrowserInterfaceBroker().GetInterface(
 //       service.BindNewPipeAndPassReceiver());
 //   service->GetWakeLock(...);  // Will call mock_service.GetWakeLock().
 // }
diff --git a/third_party/blink/renderer/platform/bindings/v8_private_property.h b/third_party/blink/renderer/platform/bindings/v8_private_property.h
index fb24ecf..6d41991b 100644
--- a/third_party/blink/renderer/platform/bindings/v8_private_property.h
+++ b/third_party/blink/renderer/platform/bindings/v8_private_property.h
@@ -188,6 +188,14 @@
     return GetSymbol(isolate, "unexpected cached accessor");
   }
 
+  // This is a hack for PopStateEvent to get the same private property of
+  // History, named State.
+  static Symbol GetHistoryStateSymbol(v8::Isolate* isolate) {
+    // This key is used for uniquely identifying v8::Private.
+    static int private_property_key;
+    return GetSymbol(isolate, &private_property_key, "History#State");
+  }
+
   static Symbol GetSymbol(v8::Isolate* isolate, const char* symbol) {
     return Symbol(isolate, CreateCachedV8Private(isolate, symbol));
   }
@@ -199,13 +207,13 @@
     V8PrivateProperty* private_prop =
         V8PerIsolateData::From(isolate)->PrivateProperty();
     auto& symbol_map = private_prop->symbol_map_;
-    auto itr = symbol_map.find(key);
+    auto iter = symbol_map.find(key);
     v8::Local<v8::Private> v8_private;
-    if (UNLIKELY(itr == symbol_map.end())) {
+    if (UNLIKELY(iter == symbol_map.end())) {
       v8_private = CreateV8Private(isolate, desc);
       symbol_map.insert(key, v8::Eternal<v8::Private>(isolate, v8_private));
     } else {
-      v8_private = itr->value.Get(isolate);
+      v8_private = iter->value.Get(isolate);
     }
     return Symbol(isolate, v8_private);
   }
diff --git a/third_party/blink/renderer/platform/graphics/placeholder_image.cc b/third_party/blink/renderer/platform/graphics/placeholder_image.cc
index 75b7b9b..43a015c67 100644
--- a/third_party/blink/renderer/platform/graphics/placeholder_image.cc
+++ b/third_party/blink/renderer/platform/graphics/placeholder_image.cc
@@ -8,6 +8,7 @@
 
 #include "base/stl_util.h"
 #include "third_party/blink/public/resources/grit/blink_image_resources.h"
+#include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/platform/fonts/font.h"
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
 #include "third_party/blink/renderer/platform/fonts/font_family.h"
@@ -115,20 +116,19 @@
 String FormatOriginalResourceSizeBytes(int64_t bytes) {
   DCHECK_LT(0, bytes);
 
-  static constexpr WebLocalizedString::Name kUnitsNames[] = {
-      WebLocalizedString::kUnitsKibibytes, WebLocalizedString::kUnitsMebibytes,
-      WebLocalizedString::kUnitsGibibytes, WebLocalizedString::kUnitsTebibytes,
-      WebLocalizedString::kUnitsPebibytes};
+  static constexpr int kUnitsResourceIds[] = {
+      IDS_UNITS_KIBIBYTES, IDS_UNITS_MEBIBYTES, IDS_UNITS_GIBIBYTES,
+      IDS_UNITS_TEBIBYTES, IDS_UNITS_PEBIBYTES};
 
   // Start with KB. The formatted text will be at least "1 KB", with any smaller
   // amounts being rounded up to "1 KB".
-  const WebLocalizedString::Name* units = kUnitsNames;
+  const int* units = kUnitsResourceIds;
   int64_t denomenator = 1024;
 
   // Find the smallest unit that can represent |bytes| in 3 digits or less.
   // Round up to the next higher unit if possible when it would take 4 digits to
   // display the amount, e.g. 1000 KB will be rounded up to 1 MB.
-  for (; units < kUnitsNames + (base::size(kUnitsNames) - 1) &&
+  for (; units < kUnitsResourceIds + (base::size(kUnitsResourceIds) - 1) &&
          bytes >= denomenator * 1000;
        ++units, denomenator *= 1024) {
   }
@@ -137,7 +137,7 @@
   if (bytes < denomenator) {
     // Round up to 1.
     numeric_string = String::Number(1);
-  } else if (units != kUnitsNames && bytes < denomenator * 10) {
+  } else if (units != kUnitsResourceIds && bytes < denomenator * 10) {
     // For amounts between 1 and 10 units and larger than 1 MB, allow up to one
     // fractional digit.
     numeric_string = String::Number(
diff --git a/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc b/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc
index 0a62b5e..82dbed8 100644
--- a/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc
+++ b/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc
@@ -11,6 +11,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/web_localized_string.h"
 #include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/platform/fonts/font.h"
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
 #include "third_party/blink/renderer/platform/geometry/float_rect.h"
@@ -213,15 +214,15 @@
                                  const WebString& parameter) override {
     String p = parameter;
     switch (resource_id) {
-      case WebLocalizedString::kUnitsKibibytes:
+      case IDS_UNITS_KIBIBYTES:
         return String(p + " KB");
-      case WebLocalizedString::kUnitsMebibytes:
+      case IDS_UNITS_MEBIBYTES:
         return String(p + " MB");
-      case WebLocalizedString::kUnitsGibibytes:
+      case IDS_UNITS_GIBIBYTES:
         return String(p + " GB");
-      case WebLocalizedString::kUnitsTebibytes:
+      case IDS_UNITS_TEBIBYTES:
         return String(p + " TB");
-      case WebLocalizedString::kUnitsPebibytes:
+      case IDS_UNITS_PEBIBYTES:
         return String(p + " PB");
       default:
         return WebString();
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 98d5418..07836b1 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3226,6 +3226,8 @@
 crbug.com/618969 external/wpt/css/css-grid/subgrid/ [ Skip ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Win ] external/wpt/css/css-text/word-break/word-break-keep-all-008.html [ Failure ]
+crbug.com/626703 [ Win ] external/wpt/css/css-text/word-break/word-break-keep-all-007.html [ Failure ]
 crbug.com/626703 [ Mac10.11 ] external/wpt/shape-detection/idlharness.https.any.sharedworker.html [ Failure Timeout ]
 crbug.com/626703 [ Linux ] external/wpt/css/css-text/white-space/textarea-pre-wrap-013.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/css/css-text/white-space/textarea-pre-wrap-013.html [ Failure ]
diff --git a/third_party/blink/web_tests/WPTOverrideExpectations b/third_party/blink/web_tests/WPTOverrideExpectations
index 9d1118a..72717e9 100644
--- a/third_party/blink/web_tests/WPTOverrideExpectations
+++ b/third_party/blink/web_tests/WPTOverrideExpectations
@@ -990,6 +990,7 @@
 crbug.com/lpz external/wpt/html/infrastructure/urls/resolving-urls/query-encoding/location.sub.html?encoding=x-cp1251 [ Failure ]
 crbug.com/lpz external/wpt/html/infrastructure/urls/resolving-urls/query-encoding/utf-16be.html [ Failure ]
 crbug.com/lpz external/wpt/html/infrastructure/urls/resolving-urls/query-encoding/utf-16le.html [ Failure ]
+crbug.com/lpz external/wpt/html/interaction/focus/the-autofocus-attribute/first-when-later-but-before.html [ Failure ]
 crbug.com/lpz external/wpt/html/rendering/dimension-attributes.html [ Failure ]
 crbug.com/lpz external/wpt/html/rendering/non-replaced-elements/form-controls/button-style.html [ Failure ]
 crbug.com/lpz external/wpt/html/rendering/non-replaced-elements/tables/hidden-attr.html [ Failure ]
@@ -1085,7 +1086,6 @@
 crbug.com/lpz external/wpt/html/semantics/embedded-content/the-object-element/object-setcustomvalidity.html [ Failure ]
 crbug.com/lpz external/wpt/html/semantics/embedded-content/the-object-element/usemap-casing.html [ Failure ]
 crbug.com/lpz external/wpt/html/semantics/embedded-content/the-video-element/intrinsic_sizes.htm [ Failure ]
-crbug.com/lpz external/wpt/html/semantics/forms/autofocus/first-when-later-but-before.html [ Failure ]
 crbug.com/lpz external/wpt/html/semantics/forms/constraints/form-validation-validity-valueMissing.html [ Failure ]
 crbug.com/lpz external/wpt/html/semantics/forms/form-submission-0/getactionurl.html [ Failure ]
 crbug.com/lpz external/wpt/html/semantics/forms/form-submission-target/rel-base-target.html [ Failure ]
diff --git a/third_party/blink/web_tests/custom-elements/form-associated-state-restore.html b/third_party/blink/web_tests/custom-elements/form-associated-state-restore.html
index d224cc7..7a55313 100644
--- a/third_party/blink/web_tests/custom-elements/form-associated-state-restore.html
+++ b/third_party/blink/web_tests/custom-elements/form-associated-state-restore.html
@@ -15,6 +15,8 @@
     assert_equals(d.querySelector('my-control-1').lastRestoreMode, 'restore');
     assert_equals(d.querySelector('my-control-2').value, 'edit2');
     assert_equals(d.querySelector('my-control-2').lastRestoreMode, 'restore');
+    assert_equals(d.querySelector('#focus').value, 'initial');
+    assert_equals(d.querySelector('#nested-focus').value, 'initial');
     t.done();
   });
 }
diff --git a/third_party/blink/web_tests/custom-elements/resources/form-associated-state-restore-frame.html b/third_party/blink/web_tests/custom-elements/resources/form-associated-state-restore-frame.html
index 5da49fd..ce4e518 100644
--- a/third_party/blink/web_tests/custom-elements/resources/form-associated-state-restore-frame.html
+++ b/third_party/blink/web_tests/custom-elements/resources/form-associated-state-restore-frame.html
@@ -26,6 +26,7 @@
 
 <form action="../../resources/back.html" id="form1">
 <my-control-1></my-control-1>
+<my-control-1 id="nested-focus"><my-control-1 id="focus" tabindex=0></my-control-1></my-control-1>
 <my-control-2></my-control-2>
 </form>
 
@@ -65,6 +66,8 @@
     upgradeMyControl2();
 
     $('my-control-1').value = 'edit1';
+    $('#focus').value = 'edit3';
+    $('#nested-focus').value = 'edit4';
     $('my-control-2').value = 'edit2';
     setTimeout(() => { $('form').submit(); }, 100);
   } else {
@@ -73,6 +76,10 @@
     parent.doneTest();
   }
 }
-runTest();
+
+$('#focus').focus();
+window.onload = () => {
+  setTimeout(runTest, 1);
+};
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
index 89a9c585..7fa5856 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -6157,18 +6157,6 @@
      {}
     ]
    ],
-   "payment-handler/change-shipping-address-manual.https.html": [
-    [
-     "payment-handler/change-shipping-address-manual.https.html",
-     {}
-    ]
-   ],
-   "payment-handler/change-shipping-option-manual.https.html": [
-    [
-     "payment-handler/change-shipping-option-manual.https.html",
-     {}
-    ]
-   ],
    "payment-handler/payment-request-event-manual.https.html": [
     [
      "payment-handler/payment-request-event-manual.https.html",
@@ -75563,6 +75551,30 @@
      {}
     ]
    ],
+   "css/css-text/word-break/word-break-keep-all-007.html": [
+    [
+     "css/css-text/word-break/word-break-keep-all-007.html",
+     [
+      [
+       "/css/css-text/word-break/reference/word-break-keep-all-005-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/word-break/word-break-keep-all-008.html": [
+    [
+     "css/css-text/word-break/word-break-keep-all-008.html",
+     [
+      [
+       "/css/css-text/word-break/reference/word-break-keep-all-005-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text/word-break/word-break-normal-001.html": [
     [
      "css/css-text/word-break/word-break-normal-001.html",
@@ -124846,6 +124858,12 @@
    "content-security-policy/form-action/support/post-message-to-parent.sub.html": [
     []
    ],
+   "content-security-policy/frame-ancestors/support/content-security-policy.sub.html": [
+    []
+   ],
+   "content-security-policy/frame-ancestors/support/content-security-policy.sub.html.sub.headers": [
+    []
+   ],
    "content-security-policy/frame-ancestors/support/frame-ancestors-and-x-frame-options.sub.html": [
     []
    ],
@@ -145351,6 +145369,9 @@
    "css/css-values/minmax-angle-serialize-expected.txt": [
     []
    ],
+   "css/css-values/minmax-length-serialize-expected.txt": [
+    []
+   ],
    "css/css-values/minmax-time-serialize-expected.txt": [
     []
    ],
@@ -164122,12 +164143,6 @@
    "payment-handler/app-change-payment-method.js": [
     []
    ],
-   "payment-handler/app-change-shipping-address.js": [
-    []
-   ],
-   "payment-handler/app-change-shipping-option.js": [
-    []
-   ],
    "payment-handler/app-supports-shipping-contact-delegation.js": [
     []
    ],
@@ -164146,12 +164161,6 @@
    "payment-handler/change-payment-method-manual.https-expected.txt": [
     []
    ],
-   "payment-handler/change-shipping-address-manual.https-expected.txt": [
-    []
-   ],
-   "payment-handler/change-shipping-option-manual.https-expected.txt": [
-    []
-   ],
    "payment-handler/idlharness.https.any.serviceworker-expected.txt": [
     []
    ],
@@ -166690,6 +166699,9 @@
    "reporting/idlharness.any-expected.txt": [
     []
    ],
+   "reporting/idlharness.any.worker-expected.txt": [
+    []
+   ],
    "reporting/idlharness.window-expected.txt": [
     []
    ],
@@ -197379,6 +197391,12 @@
      }
     ]
    ],
+   "compat/webkit-radial-gradient-radii.html": [
+    [
+     "compat/webkit-radial-gradient-radii.html",
+     {}
+    ]
+   ],
    "compat/webkit-text-fill-color-currentColor.html": [
     [
      "compat/webkit-text-fill-color-currentColor.html",
@@ -198091,6 +198109,12 @@
      {}
     ]
    ],
+   "content-security-policy/frame-ancestors/report-blocked-frame.sub.html": [
+    [
+     "content-security-policy/frame-ancestors/report-blocked-frame.sub.html",
+     {}
+    ]
+   ],
    "content-security-policy/frame-src/frame-src-about-blank-allowed-by-default.sub.html": [
     [
      "content-security-policy/frame-src/frame-src-about-blank-allowed-by-default.sub.html",
@@ -216717,6 +216741,12 @@
      {}
     ]
    ],
+   "css/css-values/minmax-length-serialize.html": [
+    [
+     "css/css-values/minmax-length-serialize.html",
+     {}
+    ]
+   ],
    "css/css-values/minmax-number-computed.html": [
     [
      "css/css-values/minmax-number-computed.html",
@@ -329955,6 +329985,10 @@
    "579d88220610e4f2ea7e884018b655cf1c5c8dca",
    "reftest"
   ],
+  "compat/webkit-radial-gradient-radii.html": [
+   "bff414a44e2aeff1276d99ca015288142de09f4e",
+   "testharness"
+  ],
   "compat/webkit-text-fill-color-currentColor.html": [
    "f4912c93450edf03b43b220d205460cc82ef9ba2",
    "testharness"
@@ -330575,6 +330609,18 @@
    "c320370be515ee039fbc799ad088bf7a74779702",
    "testharness"
   ],
+  "content-security-policy/frame-ancestors/report-blocked-frame.sub.html": [
+   "047d377cf4cf5d05851e1d0c9c3b20c77f098cc6",
+   "testharness"
+  ],
+  "content-security-policy/frame-ancestors/support/content-security-policy.sub.html": [
+   "2182f4a3d2292218bc05243895510c4403f76528",
+   "support"
+  ],
+  "content-security-policy/frame-ancestors/support/content-security-policy.sub.html.sub.headers": [
+   "322c99d518575f1e0bfa468ff640740f819cc190",
+   "support"
+  ],
   "content-security-policy/frame-ancestors/support/frame-ancestors-and-x-frame-options.sub.html": [
    "e22fea3ccd3607d770634b9dfddae36c1b6dd314",
    "support"
@@ -390544,11 +390590,11 @@
    "support"
   ],
   "css/css-text/text-transform/text-transform-fullwidth-006.html": [
-   "b0a509758ed56ca4051e1df079c4d758352764c3",
+   "0bd0aa88400cd9e6883183c504010df611b4b928",
    "reftest"
   ],
   "css/css-text/text-transform/text-transform-fullwidth-007.html": [
-   "6e081544a193949b217530b49f71eadfe2800be4",
+   "f1089f19ab67a4c34bea126a9dec4bc23e7de0fe",
    "reftest"
   ],
   "css/css-text/text-transform/text-transform-lowercase-001.xht": [
@@ -392227,6 +392273,14 @@
    "8845ea21edd09bb9a7165643ff8148a4f516bb01",
    "reftest"
   ],
+  "css/css-text/word-break/word-break-keep-all-007.html": [
+   "e1bc9a5205319322ff103a6fc6880adba8cb3323",
+   "reftest"
+  ],
+  "css/css-text/word-break/word-break-keep-all-008.html": [
+   "16529b0619ee28b905dcc6cf91a5e62ad56fdc8c",
+   "reftest"
+  ],
   "css/css-text/word-break/word-break-normal-001.html": [
    "61ccba2465f772c46a771a8868e9036ff4a93b9d",
    "reftest"
@@ -402383,6 +402437,14 @@
    "ee086ef269d07bf6b0db5d1306a0f24af0ad8fab",
    "testharness"
   ],
+  "css/css-values/minmax-length-serialize-expected.txt": [
+   "7859bd08aa09515ccc30515a574bd9c3c35fe21a",
+   "support"
+  ],
+  "css/css-values/minmax-length-serialize.html": [
+   "4b30864d2344ba297f077b7e9090d25984bdb631",
+   "testharness"
+  ],
   "css/css-values/minmax-number-computed.html": [
    "c72c276625466a193bf8829e3ddbb87d7a565dc0",
    "testharness"
@@ -427992,7 +428054,7 @@
    "reftest"
   ],
   "fetch/corb/img-mime-types-coverage.tentative.sub.html": [
-   "c2aa93236360de7c34ecdbddac161b381de8ac7d",
+   "e2386de2f2a949a201a747e3edc9af0416b638ad",
    "testharness"
   ],
   "fetch/corb/img-png-mislabeled-as-html-nosniff.tentative.sub-ref.html": [
@@ -428000,7 +428062,7 @@
    "support"
   ],
   "fetch/corb/img-png-mislabeled-as-html-nosniff.tentative.sub.html": [
-   "46403b0b18107c7943575c424773348a11e39cb2",
+   "82adc47b0cf31c779637d2a55f9c3b9b532aceca",
    "reftest"
   ],
   "fetch/corb/img-png-mislabeled-as-html.sub-ref.html": [
@@ -428012,7 +428074,7 @@
    "reftest"
   ],
   "fetch/corb/preload-image-png-mislabeled-as-html-nosniff.tentative.sub.html": [
-   "2fc93f8317e6bc0188ebe52e3587d37290fed18e",
+   "cea80f2f89fac47e0ec8b3ac53f8b15d0ccfb76d",
    "testharness"
   ],
   "fetch/corb/resources/css-mislabeled-as-html-nosniff.css": [
@@ -428116,7 +428178,7 @@
    "support"
   ],
   "fetch/corb/script-html-correctly-labeled.tentative.sub.html": [
-   "407cef9158444d4fd19bfc8c09ed73d663d55fba",
+   "8f4d7679e3d749b3cd402997f607b7358fe98c28",
    "testharness"
   ],
   "fetch/corb/script-html-js-polyglot.sub.html": [
@@ -428136,7 +428198,7 @@
    "testharness"
   ],
   "fetch/corb/script-resource-with-json-parser-breaker.tentative.sub.html": [
-   "03924cdad0dbad0e5fb2bc05cdd8ac19b619fa13",
+   "cabc7b09c458ca1f3852c633c9cd47680ab6e89d",
    "testharness"
   ],
   "fetch/corb/script-resource-with-nonsniffable-types.tentative.sub.html": [
@@ -440452,7 +440514,7 @@
    "support"
   ],
   "html/semantics/document-metadata/the-link-element/resources/link-style-error.js": [
-   "7ebc39ba6cc5b25e6743684c1e2975049ed8fcd1",
+   "d1fa5ac2d6fcb4d94561c18b2d2e22a5a2afd6e3",
    "support"
   ],
   "html/semantics/document-metadata/the-link-element/resources/neutral.css": [
@@ -451940,7 +452002,7 @@
    "support"
   ],
   "interfaces/encrypted-media.idl": [
-   "61e869367a113092bd8c2ccf475da4fab9414b54",
+   "26c03f6f24dc0321332c7e1d16443d350d35975a",
    "support"
   ],
   "interfaces/entries-api.idl": [
@@ -452072,7 +452134,7 @@
    "support"
   ],
   "interfaces/mediastream-recording.idl": [
-   "0be0538d1227147930cb03c52391cce7497eb2e4",
+   "2d849ba9affe476a23141c9f3976c65b2d9f7ef4",
    "support"
   ],
   "interfaces/mst-content-hint.idl": [
@@ -452164,7 +452226,7 @@
    "support"
   ],
   "interfaces/reporting.idl": [
-   "ae24f7538f6ba1557f152624416b359b740a60f2",
+   "05d5a42458b5cd38c4a902b5a7066af524a396fc",
    "support"
   ],
   "interfaces/requestidlecallback.idl": [
@@ -465723,14 +465785,6 @@
    "0e5a4768e7626f666077e794e0731c9a1e3e9d35",
    "support"
   ],
-  "payment-handler/app-change-shipping-address.js": [
-   "df39258dc92642b130c6765f6c75d7efb523712f",
-   "support"
-  ],
-  "payment-handler/app-change-shipping-option.js": [
-   "ac3307b619ccfb464a5fa44820cea39befdd8f2e",
-   "support"
-  ],
   "payment-handler/app-supports-shipping-contact-delegation.js": [
    "770e2de64f13eeef8a1ee21783c2997facc8ff0b",
    "support"
@@ -465775,22 +465829,6 @@
    "56690d2b26ed671f773a16853463126e57735f77",
    "manual"
   ],
-  "payment-handler/change-shipping-address-manual.https-expected.txt": [
-   "de7d40690bcabc4d58a4db268d0d969514e05a77",
-   "support"
-  ],
-  "payment-handler/change-shipping-address-manual.https.html": [
-   "3b98d56a2516ed96c20c4c04533a129c2054c718",
-   "manual"
-  ],
-  "payment-handler/change-shipping-option-manual.https-expected.txt": [
-   "4b23059bbc5f364d9276caf84baa7d008300b2ab",
-   "support"
-  ],
-  "payment-handler/change-shipping-option-manual.https.html": [
-   "00d1aee70b991924fde8562dffc5691567d676fb",
-   "manual"
-  ],
   "payment-handler/idlharness.https.any.js": [
    "878114ec7ed878d3e754341cbffa138092fee129",
    "testharness"
@@ -478239,6 +478277,10 @@
    "17cef8183596ae1d0b307fb8ddccc7455b955966",
    "testharness"
   ],
+  "reporting/idlharness.any.worker-expected.txt": [
+   "2d07eecf67cc89acc3cf03366b39e9fb2cb3611a",
+   "support"
+  ],
   "reporting/idlharness.window-expected.txt": [
    "2852fc5895a9b4e8fccf5c53619cdb79fb399587",
    "support"
@@ -479132,7 +479174,7 @@
    "support"
   ],
   "resources/testharness.js": [
-   "55de1ce252bd0349aa5bee2e69120ae44520ed02",
+   "b45f3291a0419156d042a84d829033c56a815210",
    "support"
   ],
   "resources/testharness.js.headers": [
@@ -505076,7 +505118,7 @@
    "testharness"
   ],
   "workers/dedicated-worker-from-blob-url.window.js": [
-   "f54faf54350990a9593e7af022c9eeb769595b15",
+   "8455285571a357a5e6c46a38dcf465f7bd432b55",
    "testharness"
   ],
   "workers/examples/fetch_tests_from_worker.html": [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-fullwidth-006.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-fullwidth-006.html
index b0a5097..0bd0aa8 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-fullwidth-006.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-fullwidth-006.html
@@ -9,9 +9,11 @@
 <style>
 div { font: 50px/1 Ahem; }
 #test {
-  text-transform: fullwidth;
   color: green;
 }
+span {
+  text-transform: full-width;
+}
 #ref {
   color: red;
   position: absolute;
@@ -21,4 +23,4 @@
 
 <p>Test passes if there are two green squares and no red.
 <div id=ref>x&#x3000;x</div>
-<div id=test>x   x</div>
+<div id=test>x<span>   </span>x</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-fullwidth-007.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-fullwidth-007.html
index 6e081544..f1089f1 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-fullwidth-007.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-fullwidth-007.html
@@ -9,10 +9,12 @@
 <style>
 div { font: 50px/1 Ahem; }
 #test {
-  text-transform: fullwidth;
   color: green;
   white-space: pre-wrap;
 }
+span {
+  text-transform: full-width;
+}
 #ref {
   color: red;
   position: absolute;
@@ -22,4 +24,4 @@
 
 <p>Test passes if there are two green squares and no red.
 <div id=ref>x&#x3000;&#x3000;&#x3000;x</div>
-<div id=test>x   x</div>
+<div id=test>x<span>   </span>x</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/word-break/word-break-keep-all-007.html b/third_party/blink/web_tests/external/wpt/css/css-text/word-break/word-break-keep-all-007.html
new file mode 100644
index 0000000..e1bc9a5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/word-break/word-break-keep-all-007.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>CSS-Text test: word-break keep-all + pre-wrap does not affect U+3000</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net">
+<meta name=flags content="">
+<meta name=assert content="U+3000, despite being called Ideographic Space, does not belong to the ID line breaking class, or any other class whose wrapping opportunities are suppressed by word-break:keep-all. A break after it should still be allowed. white-space:pre-wrap doesn't change that.">
+<link rel="match" href="reference/word-break-keep-all-005-ref.html">
+<link rel=help href="https://drafts.csswg.org/css-text-3/#valdef-word-break-keep-all">
+<link rel=help href="https://drafts.csswg.org/css-text-3/#white-space-phase-2">
+<style>
+div {
+  width: 4em;
+  word-break: keep-all;
+  white-space: pre-wrap;
+}
+</style>
+
+<p>This test passes if the four characters below are arranged in a two-by-two square.
+<div lang=ja>字字 字字</div>
+<!--
+If keep-all has no effect at all, breaks are allowed everywhere,
+and the result will be:
+  字字 字
+  å­—
+
+If keep-all correctly suppresses wrapping opportunities between CJK ideographs
+but also incorrectly suppresses the wrapping opportunity after U+3000,
+no wrapping is possible, and the result will be:
+  字字 字字
+-->
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/word-break/word-break-keep-all-008.html b/third_party/blink/web_tests/external/wpt/css/css-text/word-break/word-break-keep-all-008.html
new file mode 100644
index 0000000..16529b0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/word-break/word-break-keep-all-008.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>CSS-Text test: word-break keep-all + break-spaces does not affect U+3000</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net">
+<meta name=flags content="">
+<meta name=assert content="U+3000, despite being called Ideographic Space, does not belong to the ID line breaking class, or any other class whose wrapping opportunities are suppressed by word-break:keep-all. A break after it should still be allowed. white-space:break-spaces doesn't change that.">
+<link rel="match" href="reference/word-break-keep-all-005-ref.html">
+<link rel=help href="https://drafts.csswg.org/css-text-3/#valdef-word-break-keep-all">
+<link rel=help href="https://drafts.csswg.org/css-text-3/#white-space-phase-2">
+<style>
+div {
+  width: 4em;
+  word-break: keep-all;
+  white-space: break-spaces;
+}
+</style>
+
+<p>This test passes if the four characters below are arranged in a two-by-two square.
+<div lang=ja>字字 字字</div>
+<!--
+If keep-all has no effect at all, breaks are allowed everywhere,
+and the result will be:
+  字字 字
+  å­—
+
+If keep-all correctly suppresses wrapping opportunities between CJK ideographs
+but also incorrectly suppresses the wrapping opportunity after U+3000,
+no wrapping is possible, and the result will be:
+  字字 字字
+-->
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/minmax-length-serialize-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-length-serialize-expected.txt
new file mode 100644
index 0000000..7859bd0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-length-serialize-expected.txt
@@ -0,0 +1,47 @@
+This is a testharness.js-based test.
+PASS e.style['letter-spacing'] = "min(1px)" should set the property value
+PASS e.style['letter-spacing'] = "min(1cm)" should set the property value
+PASS e.style['letter-spacing'] = "min(1mm)" should set the property value
+PASS e.style['letter-spacing'] = "min(1Q)" should set the property value
+PASS e.style['letter-spacing'] = "min(1in)" should set the property value
+PASS e.style['letter-spacing'] = "min(1pc)" should set the property value
+PASS e.style['letter-spacing'] = "min(1pt)" should set the property value
+PASS e.style['letter-spacing'] = "min(1em)" should set the property value
+PASS e.style['letter-spacing'] = "min(1ex)" should set the property value
+PASS e.style['letter-spacing'] = "min(1ch)" should set the property value
+PASS e.style['letter-spacing'] = "min(1rem)" should set the property value
+PASS e.style['letter-spacing'] = "min(1vh)" should set the property value
+PASS e.style['letter-spacing'] = "min(1vw)" should set the property value
+PASS e.style['letter-spacing'] = "min(1vmin)" should set the property value
+PASS e.style['letter-spacing'] = "min(1vmax)" should set the property value
+PASS e.style['letter-spacing'] = "max(1px)" should set the property value
+PASS e.style['letter-spacing'] = "max(1cm)" should set the property value
+PASS e.style['letter-spacing'] = "max(1mm)" should set the property value
+PASS e.style['letter-spacing'] = "max(1Q)" should set the property value
+PASS e.style['letter-spacing'] = "max(1in)" should set the property value
+PASS e.style['letter-spacing'] = "max(1pc)" should set the property value
+PASS e.style['letter-spacing'] = "max(1pt)" should set the property value
+PASS e.style['letter-spacing'] = "max(1em)" should set the property value
+PASS e.style['letter-spacing'] = "max(1ex)" should set the property value
+PASS e.style['letter-spacing'] = "max(1ch)" should set the property value
+PASS e.style['letter-spacing'] = "max(1rem)" should set the property value
+PASS e.style['letter-spacing'] = "max(1vh)" should set the property value
+PASS e.style['letter-spacing'] = "max(1vw)" should set the property value
+PASS e.style['letter-spacing'] = "max(1vmin)" should set the property value
+PASS e.style['letter-spacing'] = "max(1vmax)" should set the property value
+PASS e.style['letter-spacing'] = "min(10px, 20px, 30px)" should set the property value
+PASS e.style['letter-spacing'] = "min(30px, 20px, 10px)" should set the property value
+PASS e.style['letter-spacing'] = "min(20px, 1em, 10vw)" should set the property value
+PASS e.style['letter-spacing'] = "min(10vw, 1em, 20px)" should set the property value
+PASS e.style['letter-spacing'] = "max(10px, 20px, 30px)" should set the property value
+PASS e.style['letter-spacing'] = "max(30px, 20px, 10px)" should set the property value
+PASS e.style['letter-spacing'] = "max(20px, 1em, 10vw)" should set the property value
+PASS e.style['letter-spacing'] = "max(10vw, 1em, 20px)" should set the property value
+PASS e.style['letter-spacing'] = "calc(min(10px) + max(1em) + min(10vw))" should set the property value
+PASS e.style['letter-spacing'] = "calc(max(1em) + min(10vw) + max(10px))" should set the property value
+PASS e.style['letter-spacing'] = "calc(10px + min(1em))" should set the property value
+FAIL e.style['letter-spacing'] = "calc(min(1em) + 10px)" should set the property value assert_equals: serialization should be canonical expected "calc(10px + min(1em))" but got "calc(min(1em) + 10px)"
+PASS e.style['letter-spacing'] = "calc(10px + max(1em))" should set the property value
+FAIL e.style['letter-spacing'] = "calc(max(1em) + 10px)" should set the property value assert_equals: serialization should be canonical expected "calc(10px + max(1em))" but got "calc(max(1em) + 10px)"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/minmax-length-serialize.html b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-length-serialize.html
new file mode 100644
index 0000000..4b30864
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-length-serialize.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#comp-func">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#lengths">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#calc-serialize">
+<link rel="author" title="Xiaocheng Hu" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/parsing-testcommon.js"></script>
+<script>
+const property = 'letter-spacing';
+
+function test_valid_length(value, expected) {
+  test_valid_value(property, value, expected);
+}
+
+test_valid_length('min(1px)', 'min(1px)');
+test_valid_length('min(1cm)', 'min(1cm)');
+test_valid_length('min(1mm)', 'min(1mm)');
+// Values are case-insensitive and serialize as lower case, for example 1Q
+// serializes as 1q.
+test_valid_length('min(1Q)', 'min(1q)');
+test_valid_length('min(1in)', 'min(1in)');
+test_valid_length('min(1pc)', 'min(1pc)');
+test_valid_length('min(1pt)', 'min(1pt)');
+test_valid_length('min(1em)', 'min(1em)');
+test_valid_length('min(1ex)', 'min(1ex)');
+test_valid_length('min(1ch)', 'min(1ch)');
+test_valid_length('min(1rem)', 'min(1rem)');
+test_valid_length('min(1vh)', 'min(1vh)');
+test_valid_length('min(1vw)', 'min(1vw)');
+test_valid_length('min(1vmin)', 'min(1vmin)');
+test_valid_length('min(1vmax)', 'min(1vmax)');
+test_valid_length('max(1px)', 'max(1px)');
+test_valid_length('max(1cm)', 'max(1cm)');
+test_valid_length('max(1mm)', 'max(1mm)');
+test_valid_length('max(1Q)', 'max(1q)');
+test_valid_length('max(1in)', 'max(1in)');
+test_valid_length('max(1pc)', 'max(1pc)');
+test_valid_length('max(1pt)', 'max(1pt)');
+test_valid_length('max(1em)', 'max(1em)');
+test_valid_length('max(1ex)', 'max(1ex)');
+test_valid_length('max(1ch)', 'max(1ch)');
+test_valid_length('max(1rem)', 'max(1rem)');
+test_valid_length('max(1vh)', 'max(1vh)');
+test_valid_length('max(1vw)', 'max(1vw)');
+test_valid_length('max(1vmin)', 'max(1vmin)');
+test_valid_length('max(1vmax)', 'max(1vmax)');
+
+test_valid_length('min(10px, 20px, 30px)', 'min(10px, 20px, 30px)');
+test_valid_length('min(30px, 20px, 10px)', 'min(30px, 20px, 10px)');
+test_valid_length('min(20px, 1em, 10vw)', 'min(20px, 1em, 10vw)');
+test_valid_length('min(10vw, 1em, 20px)', 'min(10vw, 1em, 20px)');
+test_valid_length('max(10px, 20px, 30px)', 'max(10px, 20px, 30px)');
+test_valid_length('max(30px, 20px, 10px)', 'max(30px, 20px, 10px)');
+test_valid_length('max(20px, 1em, 10vw)', 'max(20px, 1em, 10vw)');
+test_valid_length('max(10vw, 1em, 20px)', 'max(10vw, 1em, 20px)');
+
+test_valid_length('calc(min(10px) + max(1em) + min(10vw))', 'calc(min(10px) + max(1em) + min(10vw))');
+test_valid_length('calc(max(1em) + min(10vw) + max(10px))', 'calc(max(1em) + min(10vw) + max(10px))');
+test_valid_length('calc(10px + min(1em))', 'calc(10px + min(1em))');
+test_valid_length('calc(min(1em) + 10px)', 'calc(10px + min(1em))');
+test_valid_length('calc(10px + max(1em))', 'calc(10px + max(1em))');
+test_valid_length('calc(max(1em) + 10px)', 'calc(10px + max(1em))');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/autofocus-in-not-fully-active-document.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/autofocus-in-not-fully-active-document.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/autofocus-in-not-fully-active-document.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/autofocus-in-not-fully-active-document.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/autofocus-on-stable-document.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/autofocus-on-stable-document.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/autofocus-on-stable-document.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/autofocus-on-stable-document.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/first-reconnected.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/first-reconnected.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/first-reconnected.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/first-reconnected.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/first-when-later-but-before.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/first-when-later-but-before.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/first-when-later-but-before.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/first-when-later-but-before.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/first-when-later.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/first-when-later.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/first-when-later.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/first-when-later.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/first.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/first.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/first.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/first.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/focusable-area-in-top-document.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/focusable-area-in-top-document.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/focusable-area-in-top-document.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/focusable-area-in-top-document.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/no-cross-origin-autofocus.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/no-cross-origin-autofocus.html
similarity index 91%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/no-cross-origin-autofocus.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/no-cross-origin-autofocus.html
index c3974bd..2cf7428 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/no-cross-origin-autofocus.html
+++ b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/no-cross-origin-autofocus.html
@@ -37,7 +37,7 @@
         }
       }));
       document.getElementById("child").src =
-          get_host_info().HTTP_REMOTE_ORIGIN + "/html/semantics/forms/autofocus/resources/child-autofocus.html";
+          get_host_info().HTTP_REMOTE_ORIGIN + "/html/interaction/focus/the-autofocus-attribute/resources/child-autofocus.html";
     }, "Autofocus shouldn't work in cross-origin iframe");
   </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/no-sandboxed-automatic-features.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/no-sandboxed-automatic-features.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/no-sandboxed-automatic-features.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/no-sandboxed-automatic-features.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/not-on-first-task.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/not-on-first-task.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/not-on-first-task.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/not-on-first-task.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/queue-non-focusable.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/queue-non-focusable.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/queue-non-focusable.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/queue-non-focusable.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/resources/child-autofocus.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/resources/child-autofocus.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/resources/child-autofocus.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/resources/child-autofocus.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/resources/child-iframe.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/resources/child-iframe.html
similarity index 82%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/resources/child-iframe.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/resources/child-iframe.html
index 2645a18..f60acfc 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/resources/child-iframe.html
+++ b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/resources/child-iframe.html
@@ -4,7 +4,7 @@
 
 <script>
   iframe.src =
-    get_host_info().ORIGIN + "/html/semantics/forms/autofocus/resources/grand-child-autofocus.html";
+    get_host_info().ORIGIN + "/html/interaction/focus/the-autofocus-attribute/resources/grand-child-autofocus.html";
   window.addEventListener("message", event => {
     if (event.data == "grand_child_loaded") {
       parent.postMessage("ready", "*");
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/resources/erase-first.css b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/resources/erase-first.css
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/resources/erase-first.css
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/resources/erase-first.css
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/resources/frame-with-autofocus-element.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/resources/frame-with-autofocus-element.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/resources/frame-with-autofocus-element.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/resources/frame-with-autofocus-element.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/resources/grand-child-autofocus.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/resources/grand-child-autofocus.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/resources/grand-child-autofocus.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/resources/grand-child-autofocus.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/resources/moving-autofocus-to-parent.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/resources/moving-autofocus-to-parent.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/resources/moving-autofocus-to-parent.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/resources/moving-autofocus-to-parent.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/resources/utils.js b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/resources/utils.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/resources/utils.js
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/resources/utils.js
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/same-origin-autofocus.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/same-origin-autofocus.html
similarity index 92%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/same-origin-autofocus.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/same-origin-autofocus.html
index 9cfcdb9..6e67aa6 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/same-origin-autofocus.html
+++ b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/same-origin-autofocus.html
@@ -39,7 +39,7 @@
         }
       }));
       document.getElementById("child").src =
-          get_host_info().HTTP_NOTSAMESITE_ORIGIN + "/html/semantics/forms/autofocus/resources/child-iframe.html";
+          get_host_info().HTTP_NOTSAMESITE_ORIGIN + "/html/interaction/focus/the-autofocus-attribute/resources/child-iframe.html";
     }, "Autofocus should work in the same origin grand child iframe");
   </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/skip-another-top-level-browsing-context.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/skip-another-top-level-browsing-context.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/skip-another-top-level-browsing-context.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/skip-another-top-level-browsing-context.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/skip-document-with-fragment.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/skip-document-with-fragment.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/skip-document-with-fragment.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/skip-document-with-fragment.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/skip-non-focusable.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/skip-non-focusable.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/skip-non-focusable.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/skip-non-focusable.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/skip-not-fully-active.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/skip-not-fully-active.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/skip-not-fully-active.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/skip-not-fully-active.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/spin-by-blocking-style-sheet.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/spin-by-blocking-style-sheet.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/spin-by-blocking-style-sheet.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/spin-by-blocking-style-sheet.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/supported-elements.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/supported-elements.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/supported-elements.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/supported-elements.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/update-the-rendering.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/update-the-rendering.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/html/semantics/forms/autofocus/update-the-rendering.html
rename to third_party/blink/web_tests/external/wpt/html/interaction/focus/the-autofocus-attribute/update-the-rendering.html
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/encrypted-media.idl b/third_party/blink/web_tests/external/wpt/interfaces/encrypted-media.idl
index 61e8693..26c03f6 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/encrypted-media.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/encrypted-media.idl
@@ -5,8 +5,7 @@
 
 [Exposed=Window]
 partial interface Navigator {
-    [SecureContext] Promise<MediaKeySystemAccess> requestMediaKeySystemAccess(DOMString keySystem,
-                                                                              sequence<MediaKeySystemConfiguration> supportedConfigurations);
+    [SecureContext] Promise<MediaKeySystemAccess> requestMediaKeySystemAccess (DOMString keySystem, sequence<MediaKeySystemConfiguration> supportedConfigurations);
 };
 
 enum MediaKeysRequirement {
@@ -16,26 +15,24 @@
 };
 
 dictionary MediaKeySystemConfiguration {
-    DOMString                               label = "";
-    sequence<DOMString>                     initDataTypes = [];
-    sequence<MediaKeySystemMediaCapability> audioCapabilities = [];
-    sequence<MediaKeySystemMediaCapability> videoCapabilities = [];
-    MediaKeysRequirement                    distinctiveIdentifier = "optional";
-    MediaKeysRequirement                    persistentState = "optional";
-    sequence<DOMString>                     sessionTypes;
+             DOMString                               label = "";
+             sequence<DOMString>                     initDataTypes = [];
+             sequence<MediaKeySystemMediaCapability> audioCapabilities = [];
+             sequence<MediaKeySystemMediaCapability> videoCapabilities = [];
+             MediaKeysRequirement                    distinctiveIdentifier = "optional";
+             MediaKeysRequirement                    persistentState = "optional";
+             sequence<DOMString>                     sessionTypes;
 };
 
 dictionary MediaKeySystemMediaCapability {
-    DOMString contentType = "";
-    DOMString robustness = "";
+             DOMString contentType = "";
+             DOMString robustness = "";
 };
 
-[Exposed=Window,
- SecureContext]
-interface MediaKeySystemAccess {
-    readonly attribute DOMString keySystem;
-    MediaKeySystemConfiguration getConfiguration();
-    Promise<MediaKeys>          createMediaKeys();
+[Exposed=Window, SecureContext] interface MediaKeySystemAccess {
+    readonly        attribute DOMString keySystem;
+    MediaKeySystemConfiguration getConfiguration ();
+    Promise<MediaKeys>          createMediaKeys ();
 };
 
 enum MediaKeySessionType {
@@ -43,37 +40,30 @@
     "persistent-license"
 };
 
-[Exposed=Window,
- SecureContext]
-interface MediaKeys {
-    MediaKeySession  createSession(optional MediaKeySessionType sessionType = "temporary");
-    Promise<boolean> setServerCertificate(BufferSource serverCertificate);
+[Exposed=Window, SecureContext] interface MediaKeys {
+    MediaKeySession  createSession (optional MediaKeySessionType sessionType = "temporary");
+    Promise<boolean> setServerCertificate (BufferSource serverCertificate);
 };
 
-[Exposed=Window,
- SecureContext]
-interface MediaKeySession : EventTarget {
-    readonly attribute DOMString           sessionId;
-    readonly attribute unrestricted double expiration;
-    readonly attribute Promise<void>       closed;
-    readonly attribute MediaKeyStatusMap   keyStatuses;
-             attribute EventHandler        onkeystatuseschange;
-             attribute EventHandler        onmessage;
-    Promise<void>    generateRequest(DOMString initDataType,
-                                     BufferSource initData);
-    Promise<boolean> load(DOMString sessionId);
-    Promise<void>    update(BufferSource response);
-    Promise<void>    close();
-    Promise<void>    remove();
+[Exposed=Window, SecureContext] interface MediaKeySession : EventTarget {
+    readonly        attribute DOMString           sessionId;
+    readonly        attribute unrestricted double expiration;
+    readonly        attribute Promise<void>       closed;
+    readonly        attribute MediaKeyStatusMap   keyStatuses;
+                    attribute EventHandler        onkeystatuseschange;
+                    attribute EventHandler        onmessage;
+    Promise<void>    generateRequest (DOMString initDataType, BufferSource initData);
+    Promise<boolean> load (DOMString sessionId);
+    Promise<void>    update (BufferSource response);
+    Promise<void>    close ();
+    Promise<void>    remove ();
 };
 
-[Exposed=Window,
- SecureContext]
-interface MediaKeyStatusMap {
-    iterable<BufferSource, MediaKeyStatus>;
-    readonly attribute unsigned long size;
-    boolean has(BufferSource keyId);
-    any     get(BufferSource keyId);
+[Exposed=Window, SecureContext] interface MediaKeyStatusMap {
+    iterable<BufferSource,MediaKeyStatus>;
+    readonly        attribute unsigned long size;
+    boolean has (BufferSource keyId);
+    any     get (BufferSource keyId);
 };
 
 enum MediaKeyStatus {
@@ -93,36 +83,33 @@
     "individualization-request"
 };
 
-[Exposed=Window,
- SecureContext,
- Constructor(DOMString type, MediaKeyMessageEventInit eventInitDict)]
+[Exposed=Window, SecureContext]
 interface MediaKeyMessageEvent : Event {
-    readonly attribute MediaKeyMessageType messageType;
-    readonly attribute ArrayBuffer         message;
+    constructor(DOMString type, MediaKeyMessageEventInit eventInitDict);
+    readonly        attribute MediaKeyMessageType messageType;
+    readonly        attribute ArrayBuffer         message;
 };
 
 dictionary MediaKeyMessageEventInit : EventInit {
-    required MediaKeyMessageType messageType;
-    required ArrayBuffer         message;
+             required MediaKeyMessageType messageType;
+             required ArrayBuffer         message;
+};
+
+[Exposed=Window] partial interface HTMLMediaElement {
+    [SecureContext] readonly        attribute MediaKeys?   mediaKeys;
+                                    attribute EventHandler onencrypted;
+                                    attribute EventHandler onwaitingforkey;
+    [SecureContext] Promise<void> setMediaKeys (MediaKeys? mediaKeys);
 };
 
 [Exposed=Window]
-partial interface HTMLMediaElement {
-    [SecureContext]
-    readonly attribute MediaKeys?   mediaKeys;
-             attribute EventHandler onencrypted;
-             attribute EventHandler onwaitingforkey;
-    [SecureContext] Promise<void> setMediaKeys(MediaKeys? mediaKeys);
-};
-
-[Exposed=Window,
- Constructor(DOMString type, optional MediaEncryptedEventInit eventInitDict)]
 interface MediaEncryptedEvent : Event {
-    readonly attribute DOMString    initDataType;
-    readonly attribute ArrayBuffer? initData;
+    constructor(DOMString type, optional MediaEncryptedEventInit eventInitDict = {});
+    readonly        attribute DOMString    initDataType;
+    readonly        attribute ArrayBuffer? initData;
 };
 
 dictionary MediaEncryptedEventInit : EventInit {
-    DOMString    initDataType = "";
-    ArrayBuffer? initData = null;
+             DOMString    initDataType = "";
+             ArrayBuffer? initData = null;
 };
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/mediastream-recording.idl b/third_party/blink/web_tests/external/wpt/interfaces/mediastream-recording.idl
index 0be0538..2d849ba9 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/mediastream-recording.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/mediastream-recording.idl
@@ -28,7 +28,7 @@
 };
 
 dictionary MediaRecorderOptions {
-  DOMString mimeType;
+  DOMString mimeType = "";
   unsigned long audioBitsPerSecond;
   unsigned long videoBitsPerSecond;
   unsigned long bitsPerSecond;
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/reporting.idl b/third_party/blink/web_tests/external/wpt/interfaces/reporting.idl
index ae24f75..05d5a42 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/reporting.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/reporting.idl
@@ -3,18 +3,18 @@
 // (https://github.com/tidoust/reffy-reports)
 // Source: Reporting API 1 (https://w3c.github.io/reporting/)
 
-[Exposed=Window]
+[Exposed=(Window,Worker)]
 interface ReportBody {
 };
 
-[Exposed=Window]
+[Exposed=(Window,Worker)]
 interface Report {
   readonly attribute DOMString type;
   readonly attribute DOMString url;
   readonly attribute ReportBody? body;
 };
 
-[Exposed=Window]
+[Exposed=(Window,Worker)]
 interface ReportingObserver {
   constructor(ReportingObserverCallback callback, optional ReportingObserverOptions options = {});
   void observe();
@@ -31,7 +31,7 @@
 
 typedef sequence<Report> ReportList;
 
-[Exposed=Window]
+[Exposed=(Window,Worker)]
 interface DeprecationReportBody : ReportBody {
   readonly attribute DOMString id;
   readonly attribute Date? anticipatedRemoval;
@@ -41,7 +41,7 @@
   readonly attribute unsigned long? columnNumber;
 };
 
-[Exposed=Window]
+[Exposed=(Window,Worker)]
 interface InterventionReportBody : ReportBody {
   readonly attribute DOMString id;
   readonly attribute DOMString message;
@@ -50,7 +50,7 @@
   readonly attribute unsigned long? columnNumber;
 };
 
-[Exposed=Window]
+[Exposed=(Window,Worker)]
 interface CrashReportBody : ReportBody {
   readonly attribute DOMString? reason;
 };
diff --git a/third_party/blink/web_tests/external/wpt/reporting/idlharness.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/reporting/idlharness.any.worker-expected.txt
new file mode 100644
index 0000000..2d07eec
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/reporting/idlharness.any.worker-expected.txt
@@ -0,0 +1,60 @@
+This is a testharness.js-based test.
+Found 56 tests; 2 PASS, 54 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS idl_test setup
+PASS idl_test validation
+FAIL ReportBody interface: existence and properties of interface object assert_own_property: self does not have own property "ReportBody" expected property "ReportBody" missing
+FAIL ReportBody interface object length assert_own_property: self does not have own property "ReportBody" expected property "ReportBody" missing
+FAIL ReportBody interface object name assert_own_property: self does not have own property "ReportBody" expected property "ReportBody" missing
+FAIL ReportBody interface: existence and properties of interface prototype object assert_own_property: self does not have own property "ReportBody" expected property "ReportBody" missing
+FAIL ReportBody interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "ReportBody" expected property "ReportBody" missing
+FAIL ReportBody interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "ReportBody" expected property "ReportBody" missing
+FAIL Report interface: existence and properties of interface object assert_own_property: self does not have own property "Report" expected property "Report" missing
+FAIL Report interface object length assert_own_property: self does not have own property "Report" expected property "Report" missing
+FAIL Report interface object name assert_own_property: self does not have own property "Report" expected property "Report" missing
+FAIL Report interface: existence and properties of interface prototype object assert_own_property: self does not have own property "Report" expected property "Report" missing
+FAIL Report interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "Report" expected property "Report" missing
+FAIL Report interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "Report" expected property "Report" missing
+FAIL Report interface: attribute type assert_own_property: self does not have own property "Report" expected property "Report" missing
+FAIL Report interface: attribute url assert_own_property: self does not have own property "Report" expected property "Report" missing
+FAIL Report interface: attribute body assert_own_property: self does not have own property "Report" expected property "Report" missing
+FAIL ReportingObserver interface: existence and properties of interface object assert_own_property: self does not have own property "ReportingObserver" expected property "ReportingObserver" missing
+FAIL ReportingObserver interface object length assert_own_property: self does not have own property "ReportingObserver" expected property "ReportingObserver" missing
+FAIL ReportingObserver interface object name assert_own_property: self does not have own property "ReportingObserver" expected property "ReportingObserver" missing
+FAIL ReportingObserver interface: existence and properties of interface prototype object assert_own_property: self does not have own property "ReportingObserver" expected property "ReportingObserver" missing
+FAIL ReportingObserver interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "ReportingObserver" expected property "ReportingObserver" missing
+FAIL ReportingObserver interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "ReportingObserver" expected property "ReportingObserver" missing
+FAIL ReportingObserver interface: operation observe() assert_own_property: self does not have own property "ReportingObserver" expected property "ReportingObserver" missing
+FAIL ReportingObserver interface: operation disconnect() assert_own_property: self does not have own property "ReportingObserver" expected property "ReportingObserver" missing
+FAIL ReportingObserver interface: operation takeRecords() assert_own_property: self does not have own property "ReportingObserver" expected property "ReportingObserver" missing
+FAIL DeprecationReportBody interface: existence and properties of interface object assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
+FAIL DeprecationReportBody interface object length assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
+FAIL DeprecationReportBody interface object name assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
+FAIL DeprecationReportBody interface: existence and properties of interface prototype object assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
+FAIL DeprecationReportBody interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
+FAIL DeprecationReportBody interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
+FAIL DeprecationReportBody interface: attribute id assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
+FAIL DeprecationReportBody interface: attribute anticipatedRemoval assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
+FAIL DeprecationReportBody interface: attribute message assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
+FAIL DeprecationReportBody interface: attribute sourceFile assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
+FAIL DeprecationReportBody interface: attribute lineNumber assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
+FAIL DeprecationReportBody interface: attribute columnNumber assert_own_property: self does not have own property "DeprecationReportBody" expected property "DeprecationReportBody" missing
+FAIL InterventionReportBody interface: existence and properties of interface object assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
+FAIL InterventionReportBody interface object length assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
+FAIL InterventionReportBody interface object name assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
+FAIL InterventionReportBody interface: existence and properties of interface prototype object assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
+FAIL InterventionReportBody interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
+FAIL InterventionReportBody interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
+FAIL InterventionReportBody interface: attribute id assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
+FAIL InterventionReportBody interface: attribute message assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
+FAIL InterventionReportBody interface: attribute sourceFile assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
+FAIL InterventionReportBody interface: attribute lineNumber assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
+FAIL InterventionReportBody interface: attribute columnNumber assert_own_property: self does not have own property "InterventionReportBody" expected property "InterventionReportBody" missing
+FAIL CrashReportBody interface: existence and properties of interface object assert_own_property: self does not have own property "CrashReportBody" expected property "CrashReportBody" missing
+FAIL CrashReportBody interface object length assert_own_property: self does not have own property "CrashReportBody" expected property "CrashReportBody" missing
+FAIL CrashReportBody interface object name assert_own_property: self does not have own property "CrashReportBody" expected property "CrashReportBody" missing
+FAIL CrashReportBody interface: existence and properties of interface prototype object assert_own_property: self does not have own property "CrashReportBody" expected property "CrashReportBody" missing
+FAIL CrashReportBody interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "CrashReportBody" expected property "CrashReportBody" missing
+FAIL CrashReportBody interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "CrashReportBody" expected property "CrashReportBody" missing
+FAIL CrashReportBody interface: attribute reason assert_own_property: self does not have own property "CrashReportBody" expected property "CrashReportBody" missing
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/resources/testharness.js b/third_party/blink/web_tests/external/wpt/resources/testharness.js
index 55de1ce2..b45f329 100644
--- a/third_party/blink/web_tests/external/wpt/resources/testharness.js
+++ b/third_party/blink/web_tests/external/wpt/resources/testharness.js
@@ -2491,6 +2491,7 @@
                 this.status.status = this.status.ERROR;
                 this.status.message = String(e);
                 this.status.stack = e.stack ? e.stack : null;
+                this.complete();
             }
         }
         this.set_timeout();
diff --git a/third_party/blink/web_tests/external/wpt/svg/struct/scripted/autofocus-attribute.svg b/third_party/blink/web_tests/external/wpt/svg/struct/scripted/autofocus-attribute.svg
index d8f2574..edf200c 100644
--- a/third_party/blink/web_tests/external/wpt/svg/struct/scripted/autofocus-attribute.svg
+++ b/third_party/blink/web_tests/external/wpt/svg/struct/scripted/autofocus-attribute.svg
@@ -7,7 +7,7 @@
     </metadata>
     <h:script src="/resources/testharness.js"/>
     <h:script src="/resources/testharnessreport.js"/>
-    <h:script src="/html/semantics/forms/autofocus/resources/utils.js"/>
+    <h:script src="/html/interaction/focus/the-autofocus-attribute/resources/utils.js"/>
     <script><![CDATA[
 const SVG_NS = 'http://www.w3.org/2000/svg';
 
diff --git a/third_party/blink/web_tests/external/wpt/workers/dedicated-worker-from-blob-url.window.js b/third_party/blink/web_tests/external/wpt/workers/dedicated-worker-from-blob-url.window.js
index f54faf5..8455285 100644
--- a/third_party/blink/web_tests/external/wpt/workers/dedicated-worker-from-blob-url.window.js
+++ b/third_party/blink/web_tests/external/wpt/workers/dedicated-worker-from-blob-url.window.js
@@ -1,6 +1,7 @@
 function message_from_port(port) {
-  return new Promise(resolve => {
+  return new Promise((resolve, reject) => {
     port.onmessage = e => resolve(e.data);
+    port.onerror = e => reject(e);
   });
 }
 
diff --git a/third_party/blink/web_tests/fast/forms/button-state-restore.html b/third_party/blink/web_tests/fast/forms/button-state-restore.html
index 33d25d5..b91a73db 100644
--- a/third_party/blink/web_tests/fast/forms/button-state-restore.html
+++ b/third_party/blink/web_tests/fast/forms/button-state-restore.html
@@ -1,6 +1,11 @@
 <html>
 <head>
     <script>
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+        }
+
         function loaded()
         {
             var beenHere = document.getElementById("beenHere");
@@ -21,11 +26,6 @@
                 return;
             }
 
-            if (window.testRunner) {
-                testRunner.dumpAsText();
-                testRunner.waitUntilDone();
-            }
-
             beenHere.value = "FAIL";
 
             input.value = "FAIL";
@@ -38,7 +38,7 @@
     </script>
 </head>
 <!-- the unload handler keeps this page out of the B/F cache -->
-<body onunload="" onload="loaded()">
+<body onunload="" onload="setTimeout(loaded, 1)">
     <p>
         This is a test for <i><a href="http://bugs.webkit.org/show_bug.cgi?id=14400">http://bugs.webkit.org/show_bug.cgi?id=14400</a>
         Assertion failure (SHOULD NEVER BE REACHED) going back on YouTube</i>.
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui/calendar-picker/calendar-picker-click-disabled-day.html b/third_party/blink/web_tests/fast/forms/controls-new-ui/calendar-picker/calendar-picker-click-disabled-day.html
new file mode 100644
index 0000000..fa812de
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/controls-new-ui/calendar-picker/calendar-picker-click-disabled-day.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../../../resources/testharness.js"></script>
+<script src="../../../../resources/testharnessreport.js"></script>
+<script src="../../../forms/resources/common.js"></script>
+<script src="../../../forms/resources/picker-common.js"></script>
+<script src="../../../forms/calendar-picker/resources/calendar-picker-common.js"></script>
+</head>
+<body>
+
+<input type=date id=date value="2019-09-20" min="2019-09-17" max="2019-09-25">
+
+<script>
+let t = async_test('Test clicking on disabled day');
+
+function test1() {
+  removeCommitDelay();
+  clickDayCellAt(6, 0);
+  assert_not_equals(internals.pagePopupWindow, null);
+}
+
+t.step(() => {
+  openPicker(document.getElementById('date'), t.step_func_done(test1));
+});
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-clearbutton-visibility-after-restore.html b/third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-clearbutton-visibility-after-restore.html
index 349786e5..9b4ceeb 100644
--- a/third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-clearbutton-visibility-after-restore.html
+++ b/third_party/blink/web_tests/fast/forms/date-multiple-fields/date-multiple-fields-clearbutton-visibility-after-restore.html
@@ -27,6 +27,6 @@
     }
 }
 
-window.onload = runTest;
+window.onload = function() { setTimeout(runTest, 1); };
 </script>
 </body>
diff --git a/third_party/blink/web_tests/fast/forms/date-multiple-fields/resources/preserve-value-after-history-back-frame.html b/third_party/blink/web_tests/fast/forms/date-multiple-fields/resources/preserve-value-after-history-back-frame.html
index c3553432..22ddd302 100644
--- a/third_party/blink/web_tests/fast/forms/date-multiple-fields/resources/preserve-value-after-history-back-frame.html
+++ b/third_party/blink/web_tests/fast/forms/date-multiple-fields/resources/preserve-value-after-history-back-frame.html
@@ -25,7 +25,7 @@
 var testInput2 = document.getElementById('test2');
 var pageName = location.search || '?page1';
 
-window.onload = function() {
+window.onload = function() { setTimeout(function() {
     switch (pageName) {
     case '?page1':
         switch (parent.state) {
@@ -95,6 +95,6 @@
         parent.failed('Unexpected page(' + pageName + ')');
         break;
     }
-};
+}, 1); };
 </script>
 </body>
diff --git a/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-clearbutton-visibility-after-restore.html b/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-clearbutton-visibility-after-restore.html
index 8d0d421..c34d057 100644
--- a/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-clearbutton-visibility-after-restore.html
+++ b/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/datetimelocal-multiple-fields-clearbutton-visibility-after-restore.html
@@ -27,6 +27,6 @@
     }
 }
 
-window.onload = runTest;
+window.onload = function() { setTimeout(runTest, 1); };
 </script>
 </body>
diff --git a/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/resources/preserve-value-after-history-back-frame.html b/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/resources/preserve-value-after-history-back-frame.html
index 13662298..965f3ed 100644
--- a/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/resources/preserve-value-after-history-back-frame.html
+++ b/third_party/blink/web_tests/fast/forms/datetimelocal-multiple-fields/resources/preserve-value-after-history-back-frame.html
@@ -31,7 +31,7 @@
 var testInput2 = document.getElementById('test2');
 var pageName = location.search || '?page1';
 
-window.onload = function() {
+window.onload = function() { setTimeout(function() {
     switch (pageName) {
     case '?page1':
         switch (parent.state) {
@@ -111,6 +111,6 @@
         parent.failed('Unexpected page(' + pageName + ')');
         break;
     }
-};
+}, 1); };
 </script>
 </body>
diff --git a/third_party/blink/web_tests/fast/forms/file/recover-file-input-in-unposted-form.html b/third_party/blink/web_tests/fast/forms/file/recover-file-input-in-unposted-form.html
index 5a4250d..2a19645d 100644
--- a/third_party/blink/web_tests/fast/forms/file/recover-file-input-in-unposted-form.html
+++ b/third_party/blink/web_tests/fast/forms/file/recover-file-input-in-unposted-form.html
@@ -3,7 +3,7 @@
 <script src="../../../resources/js-test.js"></script>
 <script src="resources/file-drag-common.js"></script>
 </head>
-<body onload="test()">
+<body onload="setTimeout(test, 1)">
 <form id="form" method="post" enctype="multipart/form-data">
 <input id="text-input" type="text" name="posted-text">
 <input id="file-input" type="file" name="posted-file">
diff --git a/third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-clearbutton-visibility-after-restore.html b/third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-clearbutton-visibility-after-restore.html
index d566c65..69b39e3 100644
--- a/third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-clearbutton-visibility-after-restore.html
+++ b/third_party/blink/web_tests/fast/forms/month-multiple-fields/month-multiple-fields-clearbutton-visibility-after-restore.html
@@ -27,6 +27,6 @@
     }
 }
 
-window.onload = runTest;
+window.onload = function() { setTimeout(runTest, 1); };
 </script>
 </body>
diff --git a/third_party/blink/web_tests/fast/forms/month-multiple-fields/resources/preserve-value-after-history-back-frame.html b/third_party/blink/web_tests/fast/forms/month-multiple-fields/resources/preserve-value-after-history-back-frame.html
index 7e4cc69..5af5ecf 100644
--- a/third_party/blink/web_tests/fast/forms/month-multiple-fields/resources/preserve-value-after-history-back-frame.html
+++ b/third_party/blink/web_tests/fast/forms/month-multiple-fields/resources/preserve-value-after-history-back-frame.html
@@ -25,7 +25,7 @@
 var testInput2 = document.getElementById('test2');
 var pageName = location.search || '?page1';
 
-window.onload = function() {
+window.onload = function() { setTimeout(function() {
     switch (pageName) {
     case '?page1':
         switch (parent.state) {
@@ -92,6 +92,6 @@
         parent.failed('Unexpected page(' + pageName + ')');
         break;
     }
-};
+}, 1); };
 </script>
 </body>
diff --git a/third_party/blink/web_tests/fast/forms/radio/state-restore-radio-group-expected.txt b/third_party/blink/web_tests/fast/forms/radio/state-restore-radio-group-expected.txt
index 14015a26..c871125 100644
--- a/third_party/blink/web_tests/fast/forms/radio/state-restore-radio-group-expected.txt
+++ b/third_party/blink/web_tests/fast/forms/radio/state-restore-radio-group-expected.txt
@@ -1,9 +1,9 @@
 Test to restore form value states for a radio group.
 
+PASS document.getElementById("input1").checked is true
+PASS document.getElementById("input2").checked is false
 PASS successfullyParsed is true
 
 TEST COMPLETE
 
-PASS document.getElementById("input1").checked is true
-PASS document.getElementById("input2").checked is false
  
diff --git a/third_party/blink/web_tests/fast/forms/radio/state-restore-radio-group.html b/third_party/blink/web_tests/fast/forms/radio/state-restore-radio-group.html
index 7a20b4e..2ae37b3c 100644
--- a/third_party/blink/web_tests/fast/forms/radio/state-restore-radio-group.html
+++ b/third_party/blink/web_tests/fast/forms/radio/state-restore-radio-group.html
@@ -3,7 +3,7 @@
 <head>
 <script src="../../../resources/js-test.js"></script>
 </head>
-<body onload="runTest()">
+<body onload="setTimeout(runTest, 1)">
 <p>Test to restore form value states for a radio group.</p>
 <div id="console"></div>
 
@@ -16,14 +16,13 @@
 </div>
 
 <script>
+window.jsTestIsAsync = true;
 
 function runTest() {
     var parent = document.getElementById('parent');
     var state = document.getElementById('emptyOnFirstVisit');
     if (!state.value) {
         // First visit.
-        if (window.testRunner)
-            testRunner.waitUntilDone();
         state.value = 'visited';
 
         document.getElementById('input1').checked = true;
@@ -33,8 +32,7 @@
         // Second visit.
         shouldBeTrue('document.getElementById("input1").checked');
         shouldBeFalse('document.getElementById("input2").checked');
-        if (window.testRunner)
-            testRunner.notifyDone();
+        finishJSTest();
     }
 }
 </script>
diff --git a/third_party/blink/web_tests/fast/forms/resources/state-restore-dynamic-controls-frame.html b/third_party/blink/web_tests/fast/forms/resources/state-restore-dynamic-controls-frame.html
new file mode 100644
index 0000000..a87c14e6
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/resources/state-restore-dynamic-controls-frame.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<div></div>
+<input type=checkbox disabled>
+<input type=text disabled>
+<script>
+document.querySelector('div').innerHTML = '<input type=checkbox><input type=text>'
+</script>
diff --git a/third_party/blink/web_tests/fast/forms/resources/state-restore-to-non-edited-controls-frame.html b/third_party/blink/web_tests/fast/forms/resources/state-restore-to-non-edited-controls-frame.html
new file mode 100644
index 0000000..9be7cbb
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/resources/state-restore-to-non-edited-controls-frame.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<body>
+<script>
+function makeForm(buttonValue, hiddenValue, imageValue, resetValue, submitValue,
+    textValue0, textValue1, textValue2, textValue3, textAreaValue) {
+  document.write('<form action="about:blank" id=form1>'
+      + '<input name=button id=button type=button value="' + buttonValue + '">'
+      + '<input name=hidden id=hidden type=hidden value="' + hiddenValue + '">'
+      + '<input name=image id=image type=image value="' + imageValue + '">'
+      + '<input name=reset id=reset type=reset value="' + resetValue + '">'
+      + '<input name=submit1 id=submit1 type=submit value="' + submitValue + '">'
+      + '<input name=text0 id=text0 type=text value="' + textValue0 + '">'
+      + '<input name=text1 id=text1 type=text value="' + textValue1 + '">'
+      + '<input name=text2 id=text2 type=text value="' + textValue2 + '">'
+      + '<input name=text3 id=text3 type=text value="' + textValue3 + '">'
+      + '<textarea name=textarea id=textarea>' + textAreaValue + '</textarea>'
+      + '</form>');
+}
+
+if (!parent.isSecondVisit) {
+  makeForm('1', '1', '1', '1', '1', '1', '1', '1', '1', '1');
+} else {
+  makeForm('2', '2', '2', '2', '2', '2', '2', '2', '2', '2');
+  // A user edits #text3.
+  document.querySelector('#text3').focus();
+  document.querySelector('#text3').value = '';
+  eventSender.keyDown('z');
+}
+</script>
+</body>
diff --git a/third_party/blink/web_tests/fast/forms/select/select-state-restore.html b/third_party/blink/web_tests/fast/forms/select/select-state-restore.html
index b9a78bd0..38c929a 100644
--- a/third_party/blink/web_tests/fast/forms/select/select-state-restore.html
+++ b/third_party/blink/web_tests/fast/forms/select/select-state-restore.html
@@ -63,6 +63,6 @@
     }
 }
 
-window.onload = runTest;
+window.onload = function() { setTimeout(runTest, 1); };
 </script>
 </body>
diff --git a/third_party/blink/web_tests/fast/forms/state-restore-dynamic-controls.html b/third_party/blink/web_tests/fast/forms/state-restore-dynamic-controls.html
new file mode 100644
index 0000000..67ad178
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/state-restore-dynamic-controls.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../external/wpt/html/semantics/forms/autofocus/resources/utils.js"></script>
+<body>
+<iframe src="resources/state-restore-dynamic-controls-frame.html"></iframe>
+<script>
+promise_test(async t => {
+  await waitForEvent(window, 'load', {once:true});
+  const iframe = document.querySelector('iframe');
+  const container = iframe.contentDocument.querySelector('div');
+  // Change states of controls
+  container.firstChild.click();
+  container.lastChild.focus();
+  eventSender.keyDown('z');
+  assert_true(container.firstChild.checked, 'sanity check for a checkbox');
+  assert_equals(container.lastChild.value, 'z', 'sanity check for a text field');
+
+  // Navigate
+  iframe.src = 'data:text/html,<h1></h1>';
+  await waitForEvent(iframe, 'load', {once:true});
+
+  // Navigate back
+  history.back();
+  await waitForEvent(iframe, 'load', {once:true});
+  // Wait until finishing the restore task.
+  await timeOut(t, 0);
+  const inputs = iframe.contentDocument.querySelectorAll('input');
+  assert_true(inputs[0].checked, 'Checkbox state should be restored.');
+  assert_equals(inputs[1].value, 'z', 'Text field state should be restored.');
+  assert_false(inputs[2].checked, 'Checkbox should have initial value.');
+  assert_equals(inputs[3].value, '', 'Text field should have initial value.');
+}, 'Control states should be restored correctly even if controls were inserted before existing controls.');
+</script>
diff --git a/third_party/blink/web_tests/fast/forms/state-restore-hidden.html b/third_party/blink/web_tests/fast/forms/state-restore-hidden.html
index 9501fcf..5cbfcda 100644
--- a/third_party/blink/web_tests/fast/forms/state-restore-hidden.html
+++ b/third_party/blink/web_tests/fast/forms/state-restore-hidden.html
@@ -3,7 +3,7 @@
 <head>
 <script src="../../resources/js-test.js"></script>
 </head>
-<body onload="runTest()">
+<body onload="setTimeout(runTest, 1)">
 
 <div id="console"></div>
 
diff --git a/third_party/blink/web_tests/fast/forms/state-restore-to-non-autocomplete-form-expected.txt b/third_party/blink/web_tests/fast/forms/state-restore-to-non-autocomplete-form-expected.txt
index 5bc8561..85d8ca1 100644
--- a/third_party/blink/web_tests/fast/forms/state-restore-to-non-autocomplete-form-expected.txt
+++ b/third_party/blink/web_tests/fast/forms/state-restore-to-non-autocomplete-form-expected.txt
@@ -1,9 +1,5 @@
 Test to NOT restore form state to a form with autocomplete=off.
 
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
 Controls in the first form should have their default values:
 PASS document.getElementById("input1").value is ""
 PASS document.getElementById("textarea1").value is ""
@@ -12,6 +8,10 @@
 PASS document.getElementById("input2").value is "value2"
 PASS document.getElementById("textarea2").value is "good"
 PASS document.getElementById("select2").value is "BSD"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
   
 Mac
 Windows
diff --git a/third_party/blink/web_tests/fast/forms/state-restore-to-non-autocomplete-form.html b/third_party/blink/web_tests/fast/forms/state-restore-to-non-autocomplete-form.html
index dad54c0..9ef9c07 100644
--- a/third_party/blink/web_tests/fast/forms/state-restore-to-non-autocomplete-form.html
+++ b/third_party/blink/web_tests/fast/forms/state-restore-to-non-autocomplete-form.html
@@ -3,7 +3,7 @@
 <head>
 <script src="../../resources/js-test.js"></script>
 </head>
-<body onload="runTest()">
+<body onload="setTimeout(runTest, 1)">
 <p>Test to NOT restore form state to a form with autocomplete=off.</p>
 <div id="console"></div>
 
@@ -22,6 +22,7 @@
 </div>
 
 <script>
+window.jsTestIsAsync = true;
 
 function runTest()
 {
@@ -29,8 +30,6 @@
     var state = document.getElementById('emptyOnFirstVisit');
     if (!state.value) {
         // First visit.
-        if (window.testRunner)
-            testRunner.waitUntilDone();
         state.value = 'visited';
 
         document.getElementById('input1').value = 'value1';
@@ -51,8 +50,7 @@
         shouldBe('document.getElementById("input2").value', '"value2"');
         shouldBe('document.getElementById("textarea2").value', '"good"');
         shouldBe('document.getElementById("select2").value', '"BSD"');
-        if (window.testRunner)
-            testRunner.notifyDone();
+        finishJSTest();
     }
  }
 </script>
diff --git a/third_party/blink/web_tests/fast/forms/state-restore-to-non-edited-controls-expected.txt b/third_party/blink/web_tests/fast/forms/state-restore-to-non-edited-controls-expected.txt
deleted file mode 100644
index 4ee381a44..0000000
--- a/third_party/blink/web_tests/fast/forms/state-restore-to-non-edited-controls-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-Test to NOT save state for non-edited controls
-
-PASS document.getElementById("button").value is "2"
-PASS document.getElementById("hidden").value is "2"
-PASS document.getElementById("image").value is "2"
-PASS document.getElementById("reset").value is "2"
-PASS document.getElementById("submit1").value is "2"
-PASS document.getElementById("text0").value is "2"
-PASS document.getElementById("text1").value is "edit"
-PASS document.getElementById("text2").value is "2"
-PASS document.getElementById("textarea").value is "2"
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/fast/forms/state-restore-to-non-edited-controls.html b/third_party/blink/web_tests/fast/forms/state-restore-to-non-edited-controls.html
index 38ec7ae..3fb7518 100644
--- a/third_party/blink/web_tests/fast/forms/state-restore-to-non-edited-controls.html
+++ b/third_party/blink/web_tests/fast/forms/state-restore-to-non-edited-controls.html
@@ -1,66 +1,46 @@
 <!DOCTYPE html>
 <html>
 <head>
-<script src="../../resources/js-test.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 </head>
 <body>
-<p>Test to NOT save state for non-edited controls</p>
-<div id="console"></div>
 
-<input id=emptyOnFirstVisit>
-<div id=parent>
-</div>
+<iframe></iframe>
 
 <script>
-
-function makeForm(parent, buttonValue, hiddenValue, imageValue, resetValue, submitValue, textValue0, textValue1, textValue2, textAreaValue) {
-    document.write('<form action="../../resources/back.html" id=form1>'
-        + '<input name=button id=button type=button value="' + buttonValue + '">'
-        + '<input name=hidden id=hidden type=hidden value="' + hiddenValue + '">'
-        + '<input name=image id=image type=image value="' + imageValue + '">'
-        + '<input name=reset id=reset type=reset value="' + resetValue + '">'
-        + '<input name=submit1 id=submit1 type=submit value="' + submitValue + '">'
-        + '<input name=text1 id=text0 type=text value="' + textValue0 + '">'
-        + '<input name=text1 id=text1 type=text value="' + textValue1 + '">'
-        + '<input name=text2 id=text2 type=text value="' + textValue2 + '">'
-        + '<textarea name=textarea id=textarea>' + textAreaValue + '</textarea>'
-        + '</form>');
-}
-
-function runTest()
-{
-    var parent = document.getElementById('parent');
-    var state = document.getElementById('emptyOnFirstVisit');
-    if (!state.value) {
-        // First visit.
-        if (window.testRunner)
-            testRunner.waitUntilDone();
-        state.value = 'visited';
-        makeForm(parent, '1', '1', '1', '1', '1', '1', '1', '1', '1');
-
-        document.getElementById('text1').value = 'edit';
-        // Submit form in a timeout to make sure that we create a new back/forward list item.
-        setTimeout(function() {document.getElementById('form1').submit();}, 100);
+window.isSecondVisit = false;
+const iframe = document.querySelector('iframe');
+let t = async_test('Test to NOT save sate for non-edited controls');
+t.step(() => {
+  iframe.src = 'resources/state-restore-to-non-edited-controls-frame.html';
+  iframe.addEventListener('load', t.step_func(() => {
+    if (!isSecondVisit) {
+      isSecondVisit = true;
+      let doc = iframe.contentDocument;
+      doc.getElementById('text1').value = 'edit';
+      doc.getElementById('text3').value = 'edit';
+      // Submit form in a timeout to make sure that we create a new back/forward list item.
+      t.step_timeout(function() {doc.getElementById('form1').submit();}, 100);
+    } else if (iframe.contentDocument.URL.indexOf('blank') != -1) {
+      history.back();
     } else {
-        // Second visit.
-        makeForm(parent, '2', '2', '2', '2', '2', '2', '2', '2', '2');
-
-        shouldBe('document.getElementById("button").value', '"2"');
-        shouldBe('document.getElementById("hidden").value', '"2"');
-        shouldBe('document.getElementById("image").value', '"2"');
-        shouldBe('document.getElementById("reset").value', '"2"');
-        shouldBe('document.getElementById("submit1").value', '"2"');
-        shouldBe('document.getElementById("text0").value', '"2"');
-        shouldBe('document.getElementById("text1").value', '"edit"');
-        shouldBe('document.getElementById("text2").value', '"2"');
-        shouldBe('document.getElementById("textarea").value', '"2"');
-
-        parent.innerHTML = '';
-        if (window.testRunner)
-            testRunner.notifyDone();
+      t.step_timeout(() => {
+        let doc = iframe.contentDocument
+        assert_equals(doc.getElementById('button').value, '2');
+        assert_equals(doc.getElementById('hidden').value, '2');
+        assert_equals(doc.getElementById('image').value, '2');
+        assert_equals(doc.getElementById('reset').value, '2');
+        assert_equals(doc.getElementById('submit1').value, '2');
+        assert_equals(doc.getElementById('text0').value, '2');
+        assert_equals(doc.getElementById('text1').value, 'edit', 'Non-initial value should be restored.');
+        assert_equals(doc.getElementById('text2').value, '2');
+        assert_equals(doc.getElementById('text3').value, 'z', 'User-edit value should be preserved.');
+        assert_equals(doc.getElementById('textarea').value, '2');
+        t.done();
+      }, 1);
     }
-}
-
-runTest();
+  }));
+});
 </script>
 </body>
diff --git a/third_party/blink/web_tests/fast/forms/state-save-of-detached-control-expected.txt b/third_party/blink/web_tests/fast/forms/state-save-of-detached-control-expected.txt
index 2c8423f..76ac635 100644
--- a/third_party/blink/web_tests/fast/forms/state-save-of-detached-control-expected.txt
+++ b/third_party/blink/web_tests/fast/forms/state-save-of-detached-control-expected.txt
@@ -1,7 +1,7 @@
 Test to NOT save state of a detached form control.
 
+PASS document.getElementById("input2").value is ""
 PASS successfullyParsed is true
 
 TEST COMPLETE
 
-PASS document.getElementById("input2").value is ""
diff --git a/third_party/blink/web_tests/fast/forms/state-save-of-detached-control.html b/third_party/blink/web_tests/fast/forms/state-save-of-detached-control.html
index f1150cb..018c3b5b 100644
--- a/third_party/blink/web_tests/fast/forms/state-save-of-detached-control.html
+++ b/third_party/blink/web_tests/fast/forms/state-save-of-detached-control.html
@@ -3,7 +3,7 @@
 <head>
 <script src="../../resources/js-test.js"></script>
 </head>
-<body onload="runTest()">
+<body onload="setTimeout(runTest, 1)">
 <p>Test to NOT save state of a detached form control.</p>
 <div id="console"></div>
 
@@ -13,6 +13,7 @@
 </form>
 
 <script>
+window.jsTestIsAsync = true;
 var i1;
 
 function runTest()
@@ -21,8 +22,6 @@
     var state = document.getElementById('emptyOnFirstVisit');
     if (!state.value) {
         // First visit.
-        if (window.testRunner)
-            testRunner.waitUntilDone();
         state.value = 'visited';
 
         form1.innerHTML = '<input name=user id=input1>';
@@ -34,9 +33,7 @@
     } else {
         // Second visit.
         shouldBe('document.getElementById("input2").value', '""'); // Not 'value1'.
-
-        if (window.testRunner)
-            testRunner.notifyDone();
+        finishJSTest();
     }
  }
 </script>
diff --git a/third_party/blink/web_tests/fast/forms/time-multiple-fields/resources/preserve-value-after-history-back-frame.html b/third_party/blink/web_tests/fast/forms/time-multiple-fields/resources/preserve-value-after-history-back-frame.html
index 87e20fa..37343303 100644
--- a/third_party/blink/web_tests/fast/forms/time-multiple-fields/resources/preserve-value-after-history-back-frame.html
+++ b/third_party/blink/web_tests/fast/forms/time-multiple-fields/resources/preserve-value-after-history-back-frame.html
@@ -25,7 +25,7 @@
 var testInput2 = document.getElementById('test2');
 var pageName = location.search || '?page1';
 
-window.onload = function() {
+window.onload = function() { setTimeout(function() {
     switch (pageName) {
     case '?page1':
         switch (parent.state) {
@@ -94,7 +94,7 @@
         parent.failed('Unsupported page(' + pageName + ')');
         break;
     }
-};
+}, 1); };
 </script>
 <body>
 
diff --git a/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-clearbutton-visibility-after-restore.html b/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-clearbutton-visibility-after-restore.html
index c224b59..b9009139 100644
--- a/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-clearbutton-visibility-after-restore.html
+++ b/third_party/blink/web_tests/fast/forms/time-multiple-fields/time-multiple-fields-clearbutton-visibility-after-restore.html
@@ -27,6 +27,6 @@
     }
 }
 
-window.onload = runTest;
+window.onload = function() { setTimeout(runTest, 1); };
 </script>
 </body>
diff --git a/third_party/blink/web_tests/fast/forms/week-multiple-fields/resources/preserve-value-after-history-back-frame.html b/third_party/blink/web_tests/fast/forms/week-multiple-fields/resources/preserve-value-after-history-back-frame.html
index 9d7305b..09a3ba6 100644
--- a/third_party/blink/web_tests/fast/forms/week-multiple-fields/resources/preserve-value-after-history-back-frame.html
+++ b/third_party/blink/web_tests/fast/forms/week-multiple-fields/resources/preserve-value-after-history-back-frame.html
@@ -25,7 +25,7 @@
 var testInput2 = document.getElementById('test2');
 var pageName = location.search || '?page1';
 
-window.onload = function() {
+window.onload = function() { setTimeout(function() {
     switch (pageName) {
     case '?page1':
         switch (parent.state) {
@@ -92,6 +92,6 @@
         parent.failed('Unexpected page(' + pageName + ')');
         break;
     }
-};
+}, 1); };
 </script>
 </body>
diff --git a/third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-clearbutton-visibility-after-restore.html b/third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-clearbutton-visibility-after-restore.html
index 6df4ff5f..b92e3b5 100644
--- a/third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-clearbutton-visibility-after-restore.html
+++ b/third_party/blink/web_tests/fast/forms/week-multiple-fields/week-multiple-fields-clearbutton-visibility-after-restore.html
@@ -27,6 +27,6 @@
     }
 }
 
-window.onload = runTest;
+window.onload = function() { setTimeout(runTest, 1); };
 </script>
 </body>
diff --git a/third_party/blink/web_tests/fast/history/saves-state-after-fragment-nav.html b/third_party/blink/web_tests/fast/history/saves-state-after-fragment-nav.html
index 17b55e4..0b16d3e 100644
--- a/third_party/blink/web_tests/fast/history/saves-state-after-fragment-nav.html
+++ b/third_party/blink/web_tests/fast/history/saves-state-after-fragment-nav.html
@@ -26,10 +26,6 @@
   var field = document.getElementById('field');
 
   if (hash == "") {
-    if (window.testRunner) {
-      testRunner.dumpAsText();
-      testRunner.waitUntilDone();
-    }
     // Location changes need to happen outside the onload handler to generate history entries.
     setTimeout(function() {
       navigateToHash(field);
@@ -42,8 +38,12 @@
   }
 }
 
+if (window.testRunner) {
+  testRunner.dumpAsText();
+  testRunner.waitUntilDone();
+}
 </script>
-<body onload='runTestStep()' onunload='/*suppress page cache*/'>
+<body onload='setTimeout(runTestStep, 1)' onunload='/*suppress page cache*/'>
   <input id='field'></input>
   <a id='anchor' href='resources/back-on-load.html'>go away and come back</a>
 </body>
diff --git a/third_party/blink/web_tests/fast/history/saves-state-after-frame-nav.html b/third_party/blink/web_tests/fast/history/saves-state-after-frame-nav.html
index a10e131..bc9fd32 100644
--- a/third_party/blink/web_tests/fast/history/saves-state-after-frame-nav.html
+++ b/third_party/blink/web_tests/fast/history/saves-state-after-frame-nav.html
@@ -38,13 +38,15 @@
     stage = "after forward";
     history.forward();
   } else if (stage == "after forward") {
-    // Check that the value is still in the form.
-    if (getTextInput().value == "test")
-      document.getElementById("result").innerHTML = "PASS";
+    setTimeout(function() {
+      // Check that the value is still in the form.
+      if (getTextInput().value == "test")
+        document.getElementById("result").innerHTML = "PASS";
 
-    if (window.testRunner) {
-      testRunner.notifyDone();
-    }
+      if (window.testRunner) {
+        testRunner.notifyDone();
+      }
+    }, 1);
   }
 }
 
diff --git a/third_party/blink/web_tests/fast/loader/form-state-restore-with-frames.html b/third_party/blink/web_tests/fast/loader/form-state-restore-with-frames.html
index a1f1a723..b6b3a3b 100644
--- a/third_party/blink/web_tests/fast/loader/form-state-restore-with-frames.html
+++ b/third_party/blink/web_tests/fast/loader/form-state-restore-with-frames.html
@@ -4,7 +4,7 @@
 <meta http-equiv="pragma" content="no-cache">
 <meta http-equiv="cache-control" content="no-cache">
 </head>
-<body onload="startTest()">
+<body onload="setTimeout(startTest, 1)">
 <input name="name1" id="input1">
 <iframe id="frame1" src="resources/form-state-restore-with-frames-1.html">
 </iframe>
@@ -26,12 +26,10 @@
         shouldBeEqualToString('frame$(frame$($("frame1"), "frame2"), "input3").value', 'value3');
         finishJSTest();
     } else {
-        setTimeout(function() {
-            $('input1').value = 'visited';
-            frame$($('frame1'), 'input2').value = 'value2';
-            frame$(frame$($('frame1'), 'frame2'), 'input3').value = 'value3';
-            $('form1').submit();
-        }, 0);
+        $('input1').value = 'visited';
+        frame$($('frame1'), 'input2').value = 'value2';
+        frame$(frame$($('frame1'), 'frame2'), 'input3').value = 'value3';
+        $('form1').submit();
     }
 }
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/audits/audits-emulate-run-expected.txt b/third_party/blink/web_tests/http/tests/devtools/audits/audits-emulate-run-expected.txt
index 6503b21..44d2af5 100644
--- a/third_party/blink/web_tests/http/tests/devtools/audits/audits-emulate-run-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/audits/audits-emulate-run-expected.txt
@@ -12,7 +12,7 @@
 
 =============== Lighthouse Results ===============
 URL: http://127.0.0.1:8000/devtools/audits/resources/audits-emulate-pass.html
-Version: 5.2.0
+Version: 5.4.0
 TestedAsMobileDevice: true
 ViewportDimensions: {
   "innerWidth": 412,
diff --git a/third_party/blink/web_tests/http/tests/devtools/audits/audits-successful-run-expected.txt b/third_party/blink/web_tests/http/tests/devtools/audits/audits-successful-run-expected.txt
index a85d484..2ec356c 100644
--- a/third_party/blink/web_tests/http/tests/devtools/audits/audits-successful-run-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/audits/audits-successful-run-expected.txt
@@ -131,7 +131,7 @@
 Auditing: Uses HTTPS
 Computing artifact: NetworkRecords
 Auditing: Redirects HTTP traffic to HTTPS
-Auditing: Registers a service worker that controls page and start_url
+Auditing: Registers a service worker that controls page and `start_url`
 Auditing: Current page responds with a 200 when offline
 Auditing: Has a `<meta name="viewport">` tag with `width` or `initial-scale`
 Computing artifact: ViewportMeta
@@ -169,7 +169,7 @@
 Computing artifact: ManifestValues
 Auditing: Provides a valid `apple-touch-icon`
 Auditing: Configured for a custom splash screen
-Auditing: Sets an address-bar theme color
+Auditing: Sets a theme color for the address bar.
 Auditing: Content is sized correctly for the viewport
 Auditing: Displays images with correct aspect ratio
 Auditing: Avoids deprecated APIs
@@ -189,18 +189,18 @@
 Auditing: Server Backend Latencies
 Auditing: Tasks
 Auditing: Metrics
-Auditing: start_url responds with a 200 when offline
+Auditing: `start_url` responds with a 200 when offline
 Auditing: Performance budget
 Computing artifact: ResourceSummary
 Auditing: Keep request counts low and transfer sizes small
-Auditing: Third-Party Usage
+Auditing: Third-Party usage
 Auditing: Site works cross-browser
 Auditing: Page transitions don't feel like they block on the network
 Auditing: Each page has a URL
 Auditing: `[accesskey]` values are unique
 Auditing: `[aria-*]` attributes match their roles
 Auditing: `[role]`s have all required `[aria-*]` attributes
-Auditing: Elements with `[role]` that require specific children `[role]`s, are present
+Auditing: Elements with an ARIA `[role]` that require children to contain a specific `[role]` have all required children.
 Auditing: `[role]`s are contained by their required parent element
 Auditing: `[role]` values are valid
 Auditing: `[aria-*]` attributes have valid values
@@ -227,7 +227,7 @@
 Auditing: `[user-scalable="no"]` is not used in the `<meta name="viewport">` element and the `[maximum-scale]` attribute is not less than 5.
 Auditing: `<object>` elements have `[alt]` text
 Auditing: No element has a `[tabindex]` value greater than 0
-Auditing: Cells in a `<table>` element that use the `[headers]` attribute only refer to other cells of that same table.
+Auditing: Cells in a `<table>` element that use the `[headers]` attribute refer to table cells within the same table.
 Auditing: `<th>` elements and elements with `[role="columnheader"/"rowheader"]` have data cells they describe.
 Auditing: `[lang]` attributes have a valid value
 Auditing: `<video>` elements contain a `<track>` element with `[kind="captions"]`
@@ -282,7 +282,7 @@
 
 =============== Lighthouse Results ===============
 URL: http://127.0.0.1:8000/devtools/resources/inspected-page.html
-Version: 5.2.0
+Version: 5.4.0
 TestedAsMobileDevice: true
 ViewportDimensions: {
   "innerWidth": 981,
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/target/target-setAutoAttach-windowOpen-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/target/target-setAutoAttach-windowOpen-expected.txt
new file mode 100644
index 0000000..d55a2e78
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/target/target-setAutoAttach-windowOpen-expected.txt
@@ -0,0 +1,8 @@
+Tests that Target.setAutoAttach(windowOpen=true) attaches to window.open targets.
+Opened the window
+Attached to window
+Navigated the window
+Target info changed
+Closed the window
+Detached from window
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/target/target-setAutoAttach-windowOpen.js b/third_party/blink/web_tests/http/tests/inspector-protocol/target/target-setAutoAttach-windowOpen.js
new file mode 100644
index 0000000..ee752ecdb
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/target/target-setAutoAttach-windowOpen.js
@@ -0,0 +1,31 @@
+(async function(testRunner) {
+  var {page, session, dp} = await testRunner.startBlank(
+      `Tests that Target.setAutoAttach(windowOpen=true) attaches to window.open targets.`);
+
+  await dp.Target.setDiscoverTargets({discover: true});
+
+  await dp.Target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: true, flatten: true, windowOpen: true});
+
+  session.evaluate(`
+    window.myWindow = window.open('../resources/inspector-protocol-page.html'); undefined;
+  `);
+  testRunner.log('Opened the window');
+  await dp.Target.onceAttachedToTarget();
+  testRunner.log('Attached to window');
+
+  session.evaluate(`
+    window.myWindow.location.assign('../resources/inspector-protocol-page.html?foo'); undefined;
+  `);
+  testRunner.log('Navigated the window');
+  await dp.Target.onceTargetInfoChanged();
+  testRunner.log('Target info changed');
+
+  session.evaluate(`
+    window.myWindow.close(); undefined;
+  `);
+  testRunner.log('Closed the window');
+  await dp.Target.onceDetachedFromTarget();
+  testRunner.log('Detached from window');
+
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/http/tests/navigation/resources/restore-form-state-https-1.html b/third_party/blink/web_tests/http/tests/navigation/resources/restore-form-state-https-1.html
index b06b753..1601b2f 100644
--- a/third_party/blink/web_tests/http/tests/navigation/resources/restore-form-state-https-1.html
+++ b/third_party/blink/web_tests/http/tests/navigation/resources/restore-form-state-https-1.html
@@ -28,8 +28,16 @@
     <textarea name="textarea1" rows="10" cols="30" value="Initial text before user input">More initial text before user input.</textarea>
 </form>
 <script>
-    if (document.getElementById('tf').value == "Test Failed")
-        document.getElementById('console').innerHTML = "Test Failed";
+function checkControlState() {
+  testRunner.waitUntilDone();
+  setTimeout(() => {
+    if (document.getElementById('tf').value == "New form text from user")
+      document.getElementById('console').innerHTML = "Test Passed";
+    else
+      document.getElementById('console').innerHTML = "Test Failed";
+    testRunner.notifyDone();
+  }, 1);
+};
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/http/tests/navigation/restore-form-state-https.html b/third_party/blink/web_tests/http/tests/navigation/restore-form-state-https.html
index 9d9979a..046314b 100644
--- a/third_party/blink/web_tests/http/tests/navigation/restore-form-state-https.html
+++ b/third_party/blink/web_tests/http/tests/navigation/restore-form-state-https.html
@@ -3,6 +3,7 @@
     if (window.testRunner)
         testRunner.dumpAsText();
     runBackTest("resources/restore-form-state-https-1.html", 1, "");
+    testRunner.queueNonLoadingScript("checkControlState()");
 </script>
 This page just kicks off a test, and should not appear in the expected test output.
 The files in the resources dir have comments about the tests.
diff --git a/third_party/blink/web_tests/wpt_internal/html/interaction/focus/the-autofocus-attribute/object-fallback.html b/third_party/blink/web_tests/wpt_internal/html/interaction/focus/the-autofocus-attribute/object-fallback.html
index b6c90260d..c3efaa0 100644
--- a/third_party/blink/web_tests/wpt_internal/html/interaction/focus/the-autofocus-attribute/object-fallback.html
+++ b/third_party/blink/web_tests/wpt_internal/html/interaction/focus/the-autofocus-attribute/object-fallback.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="/html/semantics/forms/autofocus/resources/utils.js"></script>
+<script src="/html/interaction/focus/the-autofocus-attribute/resources/utils.js"></script>
 <body>
 <script>
 
diff --git a/third_party/closure_compiler/externs/file_manager_private.js b/third_party/closure_compiler/externs/file_manager_private.js
index 233d902e..5a8a0c8 100644
--- a/third_party/closure_compiler/externs/file_manager_private.js
+++ b/third_party/closure_compiler/externs/file_manager_private.js
@@ -869,17 +869,6 @@
 chrome.fileManagerPrivate.computeChecksum = function(entry, callback) {};
 
 /**
- * Sets a tag on a file or a directory. Only Drive files are supported.
- * @param {!Entry} entry
- * @param {string} visibility 'private' or 'public'
- * @param {string} key
- * @param {string} value
- * @param {function()} callback
- */
-chrome.fileManagerPrivate.setEntryTag = function(entry, visibility, key,
-    value, callback) {};
-
-/**
  * Gets a flag indicating whether PiexLoader is enabled.
  * @param {function((boolean|undefined))} callback
  */
diff --git a/third_party/grpc/OWNERS b/third_party/grpc/OWNERS
index eb57faa..f49c132 100644
--- a/third_party/grpc/OWNERS
+++ b/third_party/grpc/OWNERS
@@ -1,3 +1,6 @@
 jamiewalch@chromium.org
 joedow@chromium.org
-yuweih@chromium.org
\ No newline at end of file
+yuweih@chromium.org
+
+# COMPONENT: Services>Chromoting
+# TEAM: chromoting-team@google.com
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 797119d..00aa5730 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -20685,6 +20685,7 @@
   <int value="1384" label="AUTOFILLPRIVATE_SETCREDITCARDFIDOAUTHENABLEDSTATE"/>
   <int value="1385" label="USERSPRIVATE_ISWHITELISTEDUSER"/>
   <int value="1386" label="PRINTINGMETRICS_GETPRINTJOBS"/>
+  <int value="1387" label="AUTOTESTPRIVATE_WAITFORASSISTANTQUERYSTATUS"/>
 </enum>
 
 <enum name="ExtensionIconState">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 788c6373..d28e464 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -94854,6 +94854,17 @@
   </summary>
 </histogram>
 
+<histogram name="OptimizationGuide.OptimizationHintsComponent.MajorVersion"
+    units="major version number" expires_after="M85">
+  <owner>sophiechang@chromium.org</owner>
+  <owner>mcrouse@chromium.org</owner>
+  <summary>
+    Records the major version of the Optimization Hints component that was
+    pushed to the client. This will be recorded at startup and when a new
+    component is received.
+  </summary>
+</histogram>
+
 <histogram
     name="OptimizationGuide.PredictionModelFetcher.GetModelsRequest.HostCount"
     units="total host count" expires_after="M84">
@@ -173020,6 +173031,9 @@
   <suffix name="Clients.SignedExchange.Cached"
       label="PageLoadMetrics for page loads for which the document was served
              through a cached signed exchange."/>
+  <suffix name="Clients.SignedExchange.NotCached"
+      label="PageLoadMetrics for page loads for which the document was served
+             through a signed exchange directly from network."/>
   <affected-histogram
       name="PageLoad.DocumentTiming.NavigationToDOMContentLoadedEventFired"/>
   <affected-histogram
diff --git a/tools/translation/helper/translation_helper.py b/tools/translation/helper/translation_helper.py
index 1f9434d..e0ef7c50 100644
--- a/tools/translation/helper/translation_helper.py
+++ b/tools/translation/helper/translation_helper.py
@@ -4,6 +4,8 @@
 
 """Helpers for dealing with translation files."""
 
+from __future__ import print_function
+
 import ast
 import os
 import re
@@ -236,5 +238,5 @@
     return grd_to_langs, untranslated_grds, internal_grds
 
   except Exception:
-    print 'Error: failed to parse', path
+    print('Error: failed to parse', path)
     raise
diff --git a/tools/translation/upload_screenshots.py b/tools/translation/upload_screenshots.py
index a22825f4..c52c896 100755
--- a/tools/translation/upload_screenshots.py
+++ b/tools/translation/upload_screenshots.py
@@ -16,6 +16,8 @@
 It will attempt to upload the image anyways.
 """
 
+from __future__ import print_function
+
 import argparse
 import sys
 import os
@@ -71,14 +73,14 @@
 
   valid = {'yes': True, 'y': True, 'ye': True, 'no': False, 'n': False}
   while True:
-    print question, prompt
+    print(question, prompt)
     choice = raw_input().lower()
     if default is not None and choice == '':
       return valid[default]
     elif choice in valid:
       return valid[choice]
     else:
-      print "Please respond with 'yes' or 'no' (or 'y' or 'n')."
+      print("Please respond with 'yes' or 'no' (or 'y' or 'n').")
 
 
 def list_grds_in_repository(repo_path):
@@ -134,7 +136,7 @@
       if f in ('OWNERS', 'README.md') or f.endswith('.sha1'):
         continue
       if not f.endswith('.png'):
-        print 'File with unexpected extension: %s in %s' % (f, screenshots_dir)
+        print('File with unexpected extension: %s in %s' % (f, screenshots_dir))
         continue
       screenshots.append(os.path.join(screenshots_dir, f))
   return screenshots
@@ -162,10 +164,10 @@
            "this script can pick up its screenshot directory.")
     exit(0)
 
-  print 'Found %d updated screenshot(s): ' % len(screenshots)
+  print('Found %d updated screenshot(s): ' % len(screenshots))
   for s in screenshots:
-    print '  %s' % s
-  print
+    print('  %s' % s)
+  print()
   if not query_yes_no(
       'Do you want to upload these to Google Cloud Storage?\n\n'
       'FILES WILL BE PUBLIC, DO NOT UPLOAD ANYTHING CONFIDENTIAL.'):
@@ -190,13 +192,13 @@
              '`download_from_google_storage --config`.')
       exit(1)
 
-  print
-  print 'Images are uploaded and their signatures are calculated:'
+  print()
+  print('Images are uploaded and their signatures are calculated:')
 
   signatures = ['%s.sha1' % s for s in screenshots]
   for s in signatures:
-    print '  %s' % s
-  print
+    print('  %s' % s)
+  print()
 
   # Always ask if the .sha1 files should be added to the CL, even if they are
   # already part of the CL. If the files are not modified, adding again is a
@@ -208,7 +210,7 @@
   if not args.dry_run:
     git_add(signatures, src_path)
 
-  print 'DONE.'
+  print('DONE.')
 
 
 if __name__ == '__main__':
diff --git a/tools/unused-symbols-report.py b/tools/unused-symbols-report.py
index 993e436b..4376a5b 100755
--- a/tools/unused-symbols-report.py
+++ b/tools/unused-symbols-report.py
@@ -17,6 +17,8 @@
   ./tools/unused-symbols-report.py buildlog > report.html
 """
 
+from __future__ import print_function
+
 import cgi
 import optparse
 import os
@@ -31,7 +33,7 @@
   if cppfilt_proc is None:
     cppfilt_proc = subprocess.Popen(['c++filt'], stdin=subprocess.PIPE,
                                     stdout=subprocess.PIPE)
-  print >>cppfilt_proc.stdin, sym
+  print(sym, file=cppfilt_proc.stdin)
   return cppfilt_proc.stdout.readline().strip()
 
 
@@ -73,7 +75,7 @@
       continue
     match = path_re.match(path)
     if not match:
-      print >>sys.stderr, "Skipping weird path", path
+      print("Skipping weird path", path, file=sys.stderr)
       continue
     target, path = match.groups()
     yield target, path, symbol
@@ -130,23 +132,23 @@
     entries = targets.setdefault(target, [])
     entries.append((symbol, path))
 
-  print TEMPLATE_HEADER
-  print "<p>jump to target:"
-  print "<select onchange='document.location.hash = this.value'>"
+  print(TEMPLATE_HEADER)
+  print("<p>jump to target:")
+  print("<select onchange='document.location.hash = this.value'>")
   for target in sorted(targets.keys()):
-    print "<option>%s</option>" % target
-  print "</select></p>"
+    print("<option>%s</option>" % target)
+  print("</select></p>")
 
   for target in sorted(targets.keys()):
-    print "<h2>%s" % target
-    print "<a class=permalink href='#%s' name='%s'>#</a>" % (target, target)
-    print "</h2>"
-    print "<table width=100% cellspacing=0>"
+    print("<h2>%s" % target)
+    print("<a class=permalink href='#%s' name='%s'>#</a>" % (target, target))
+    print("</h2>")
+    print("<table width=100% cellspacing=0>")
     for symbol, path in sorted(targets[target]):
       htmlsymbol = cgi.escape(symbol).replace('::', '::<wbr>')
-      print "<tr><td><div class=symbol>%s</div></td>" % htmlsymbol
-      print "<td valign=top><div class=file>%s</div></td></tr>" % path
-    print "</table>"
+      print("<tr><td><div class=symbol>%s</div></td>" % htmlsymbol)
+      print("<td valign=top><div class=file>%s</div></td></tr>" % path)
+    print("</table>")
 
 
 def main():
diff --git a/tools/usb_gadget/__main__.py b/tools/usb_gadget/__main__.py
index 03341ea8..1218edcf 100644
--- a/tools/usb_gadget/__main__.py
+++ b/tools/usb_gadget/__main__.py
@@ -4,6 +4,8 @@
 
 """Package entry-point."""
 
+from __future__ import print_function
+
 import argparse
 
 import netifaces
@@ -60,7 +62,7 @@
   server.http_server.listen(server.port)
 
   ioloop.IOLoop.instance().start()
-  print 'Exiting...'
+  print('Exiting...')
 
 
 if __name__ == '__main__':
diff --git a/tools/usb_gadget/gadget.py b/tools/usb_gadget/gadget.py
index c290750..c9bee94 100644
--- a/tools/usb_gadget/gadget.py
+++ b/tools/usb_gadget/gadget.py
@@ -5,6 +5,8 @@
 """Generic USB gadget functionality.
 """
 
+from __future__ import print_function
+
 import struct
 
 import msos20_descriptors
@@ -266,8 +268,8 @@
         desc_index = value & 0xff
         desc_lang = index
 
-        print 'GetDescriptor(recipient={}, type={}, index={}, lang={})'.format(
-            recipient, desc_type, desc_index, desc_lang)
+        print('GetDescriptor(recipient={}, type={}, index={}, lang={})'.format(
+            recipient, desc_type, desc_index, desc_lang))
 
         return self.GetDescriptor(recipient, desc_type, desc_index, desc_lang,
                                   length)
@@ -519,7 +521,7 @@
     Returns:
       True on success, None on error to stall the pipe.
     """
-    print 'SetConfiguration({})'.format(index)
+    print('SetConfiguration({})'.format(index))
 
     for endpoint_addrs in self._active_endpoints.values():
       for endpoint_addr in endpoint_addrs:
@@ -559,7 +561,7 @@
     Returns:
       True on success, None on error to stall the pipe.
     """
-    print 'SetInterface({}, {})'.format(interface, alt_setting)
+    print('SetInterface({}, {})'.format(interface, alt_setting))
 
     config_desc = self.GetConfigurationDescriptor()
     interface_desc = None
diff --git a/tools/usb_gadget/hid_gadget.py b/tools/usb_gadget/hid_gadget.py
index 5d2662b5..5a47a26 100644
--- a/tools/usb_gadget/hid_gadget.py
+++ b/tools/usb_gadget/hid_gadget.py
@@ -10,6 +10,8 @@
 report requests as necessary.
 """
 
+from __future__ import print_function
+
 import math
 import struct
 import uuid
@@ -157,8 +159,8 @@
 
     if request == hid_constants.Request.GET_REPORT:
       report_type, report_id = value >> 8, value & 0xFF
-      print ('GetReport(type={}, id={}, length={})'
-             .format(report_type, report_id, length))
+      print('GetReport(type={}, id={}, length={})'.format(
+          report_type, report_id, length))
       return self.GetReport(report_type, report_id, length)
 
   def ClassControlWrite(self, recipient, request, value, index, data):
diff --git a/tools/usb_gadget/linux_gadgetfs.py b/tools/usb_gadget/linux_gadgetfs.py
index b67bba8..11a25a7 100644
--- a/tools/usb_gadget/linux_gadgetfs.py
+++ b/tools/usb_gadget/linux_gadgetfs.py
@@ -10,6 +10,8 @@
 https://github.com/torvalds/linux/blob/master/drivers/usb/gadget/inode.c
 """
 
+from __future__ import print_function
+
 import errno
 import multiprocessing
 import os
@@ -124,7 +126,7 @@
     event_type, = struct.unpack_from('=I', buf, 8)
 
     if event_type == GADGETFS_NOP:
-      print 'NOP'
+      print('NOP')
     elif event_type == GADGETFS_CONNECT:
       speed, = struct.unpack('=Ixxxxxxxx', buf)
       self.Connected(speed)
@@ -135,31 +137,31 @@
           '<BBHHHxxxx', buf)
       self.HandleSetup(request_type, request, value, index, length)
     elif event_type == GADGETFS_SUSPEND:
-      print 'SUSPEND'
+      print('SUSPEND')
     else:
-      print 'Unknown gadgetfs event type:', event_type
+      print('Unknown gadgetfs event type:', event_type)
 
   def Connected(self, speed):
-    print 'CONNECT speed={}'.format(speed)
+    print('CONNECT speed={}'.format(speed))
     self._gadget.Connected(self, speed)
 
   def Disconnected(self):
-    print 'DISCONNECT'
+    print('DISCONNECT')
     for endpoint_addr in self._ep_fds.keys():
       self.StopEndpoint(endpoint_addr)
     self._ep_fds.clear()
     self._gadget.Disconnected()
 
   def HandleSetup(self, request_type, request, value, index, length):
-    print ('SETUP bmRequestType=0x{:02X} bRequest=0x{:02X} wValue=0x{:04X} '
-           'wIndex=0x{:04X} wLength={}'
-           .format(request_type, request, value, index, length))
+    print('SETUP bmRequestType=0x{:02X} bRequest=0x{:02X} wValue=0x{:04X} '
+          'wIndex=0x{:04X} wLength={}'.format(request_type, request, value,
+                                              index, length))
 
     if request_type & usb_constants.Dir.IN:
       data = self._gadget.ControlRead(
           request_type, request, value, index, length)
       if data is None:
-        print 'SETUP STALL'
+        print('SETUP STALL')
         try:
           os.read(self._fd, 0)  # Backwards I/O stalls the pipe.
         except OSError, e:
@@ -175,7 +177,7 @@
       result = self._gadget.ControlWrite(
           request_type, request, value, index, data)
       if result is None:
-        print 'SETUP STALL'
+        print('SETUP STALL')
         try:
           os.write(self._fd, '')  # Backwards I/O stalls the pipe.
         except OSError, e:
@@ -269,7 +271,7 @@
       self._ep_fds[endpoint_addr] = fd, child, pipe_r
 
     child.start()
-    print 'Started endpoint 0x{:02X}.'.format(endpoint_addr)
+    print('Started endpoint 0x{:02X}.'.format(endpoint_addr))
 
   def StopEndpoint(self, endpoint_addr):
     """Deactivate the given endpoint."""
@@ -280,7 +282,7 @@
     if not endpoint_addr & usb_constants.Dir.IN:
       self._io_loop.remove_handler(pipe_fd)
     os.close(fd)
-    print 'Stopped endpoint 0x{:02X}.'.format(endpoint_addr)
+    print('Stopped endpoint 0x{:02X}.'.format(endpoint_addr))
 
   def SendPacket(self, endpoint_addr, data):
     """Send a packet on the given endpoint."""
diff --git a/tools/usb_gadget/server.py b/tools/usb_gadget/server.py
index e5aa8b8..91601722 100644
--- a/tools/usb_gadget/server.py
+++ b/tools/usb_gadget/server.py
@@ -5,6 +5,8 @@
 """WSGI application to manage a USB gadget.
 """
 
+from __future__ import print_function
+
 import datetime
 import hashlib
 import re
@@ -83,7 +85,7 @@
     if claimed_by is not None:
       args.extend(['--start-claimed', claimed_by])
 
-    print 'Reloading with version {}...'.format(md5sum)
+    print('Reloading with version {}...'.format(md5sum))
 
     global http_server
     if chip.IsConfigured():
diff --git a/tools/variations/bisect_variations.py b/tools/variations/bisect_variations.py
index f9bab74..ea46aab1 100644
--- a/tools/variations/bisect_variations.py
+++ b/tools/variations/bisect_variations.py
@@ -24,6 +24,8 @@
 Run with --help to get a complete list of options this script runs with.
 """
 
+from __future__ import print_function
+
 import logging
 import optparse
 import os
@@ -248,7 +250,7 @@
   runs = [variations_file]
   while runs:
     run = runs[0]
-    print 'Run Chrome with variations file', run
+    print('Run Chrome with variations file', run)
     variations_args = _LoadVariations(run)
     exit_status, stdout, stderr = _RunVariations(
         browser_path=browser_path, url=url,
@@ -260,7 +262,7 @@
       runs = split_variations_cmd.SplitVariationsCmdFromFile(run, output_dir)
       if len(runs) == 1:
         # Can divide no further.
-        print 'Bisecting succeeded:', ' '.join(variations_args)
+        print('Bisecting succeeded:', ' '.join(variations_args))
         return
     elif answer == 'n':
       if len(runs) == 1:
diff --git a/tools/variations/fieldtrial_util.py b/tools/variations/fieldtrial_util.py
index 689cdff..e61e09c2 100644
--- a/tools/variations/fieldtrial_util.py
+++ b/tools/variations/fieldtrial_util.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from __future__ import print_function
+
 import json
 import sys
 
@@ -78,11 +80,11 @@
         param_list.append(param['key'])
         param_list.append(param['value'])
     if len(param_list):
-       # Escape the variables for the command-line.
-       selected_study = [_escape(x) for x in selected_study]
-       param_list = [_escape(x) for x in param_list]
-       param = '%s:%s' % ('.'.join(selected_study), '/'.join(param_list))
-       params.append(param)
+      # Escape the variables for the command-line.
+      selected_study = [_escape(x) for x in selected_study]
+      param_list = [_escape(x) for x in param_list]
+      param = '%s:%s' % ('.'.join(selected_study), '/'.join(param_list))
+      params.append(param)
     for feature in experiment.get('enable_features', []):
       enable_features.append(feature + '<' + study_name)
     for feature in experiment.get('disable_features', []):
@@ -102,24 +104,25 @@
 
 def main():
   if len(sys.argv) < 3:
-    print 'Usage: fieldtrial_util.py [config_path] [platform]'
-    print 'Optionally pass \'shell_cmd\' as an extra argument to print'
-    print 'quoted command line arguments.'
+    print('Usage: fieldtrial_util.py [config_path] [platform]')
+    print('Optionally pass \'shell_cmd\' as an extra argument to print')
+    print('quoted command line arguments.')
     exit(-1)
   print_shell_cmd = len(sys.argv) >= 4 and sys.argv[3] == 'shell_cmd'
 
   supported_platforms = ['android', 'android_webview', 'chromeos', 'ios',
                          'linux', 'mac', 'windows']
   if sys.argv[2] not in supported_platforms:
-    print ('\'%s\' is an unknown platform. Supported platforms: %s' %
-        (sys.argv[2], supported_platforms))
+    print('\'%s\' is an unknown platform. Supported platforms: %s' %
+          (sys.argv[2], supported_platforms))
     exit(-1)
 
   generated_args = GenerateArgs(sys.argv[1], [sys.argv[2]])
   if print_shell_cmd:
-    print " ".join(map((lambda arg: '"{0}"'.format(arg)), generated_args))
+    print(" ".join(map((lambda arg: '"{0}"'.format(arg)), generated_args)))
   else:
-    print generated_args
+    print(generated_args)
+
 
 if __name__ == '__main__':
   main()
diff --git a/tools/vim/ninja_output.py b/tools/vim/ninja_output.py
index 6695fcfa..650f5d0 100644
--- a/tools/vim/ninja_output.py
+++ b/tools/vim/ninja_output.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from __future__ import print_function
+
 import sys
 import os
 import itertools
diff --git a/tools/web_bluetooth/compact_blocklist.py b/tools/web_bluetooth/compact_blocklist.py
index 307741b..05b31a9 100755
--- a/tools/web_bluetooth/compact_blocklist.py
+++ b/tools/web_bluetooth/compact_blocklist.py
@@ -14,6 +14,8 @@
   compact_blocklist.py <gatt_blocklist.txt>
 """
 
+from __future__ import print_function
+
 import collections
 import string
 import sys
diff --git a/tools/web_dev_style/js_checker_test.py b/tools/web_dev_style/js_checker_test.py
index 8a3acd68c..00aef42 100755
--- a/tools/web_dev_style/js_checker_test.py
+++ b/tools/web_dev_style/js_checker_test.py
@@ -38,7 +38,7 @@
   def testBindThisFails(self):
     lines = [
         'let bound = this.method_.bind(this);',
-        "cr.doc.addEventListener('click', this.onClick_.bind(this));",
+        "document.addEventListener('click', this.onClick_.bind(this));",
         'this.api_.onEvent = this.onClick_.bind(this);',
         'this.api_.getThinger(this.gotThinger_.bind(this));',
         'this.api_.getThinger(this.gotThinger_.bind(this, param1, param2));',
diff --git a/tools/win/link_limiter/build_link_limiter.py b/tools/win/link_limiter/build_link_limiter.py
index 464d30c5..0872017 100755
--- a/tools/win/link_limiter/build_link_limiter.py
+++ b/tools/win/link_limiter/build_link_limiter.py
@@ -3,6 +3,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from __future__ import print_function
+
 import glob
 import os
 import shutil
@@ -16,11 +18,11 @@
 def run_with_vsvars(cmd, tmpdir=None):
   fd, filename = tempfile.mkstemp('.bat', text=True)
   with os.fdopen(fd, 'w') as f:
-    print >> f, '@echo off'
-    print >> f, r'call "%VS100COMNTOOLS%\vsvars32.bat"'
+    print('@echo off', file=f)
+    print(r'call "%VS100COMNTOOLS%\vsvars32.bat"', file=f)
     if tmpdir:
-      print >> f, r'cd %s' % tmpdir
-    print >> f, cmd
+      print(r'cd %s' % tmpdir, file=f)
+    print(cmd, file=f)
   try:
     p = subprocess.Popen([filename], shell=True, stdout=subprocess.PIPE,
         universal_newlines=True)
@@ -45,17 +47,17 @@
   outpath = os.path.join(BUILD_DIR, outfile)
   cpptime = os.path.getmtime(infile)
   if not os.path.exists(outpath) or cpptime > os.path.getmtime(outpath):
-    print 'Building %s...' % outfile
+    print('Building %s...' % outfile)
     rc, out = run_with_vsvars(
         'cl /nologo /Ox /Zi /W4 /WX /D_UNICODE /DUNICODE'
         ' /D_CRT_SECURE_NO_WARNINGS /EHsc %s /link /out:%s'
         % (os.path.join('..', infile), outfile), BUILD_DIR)
     if rc:
-      print out
-      print 'Failed to build %s' % outfile
+      print(out)
+      print('Failed to build %s' % outfile)
       sys.exit(1)
   else:
-    print '%s already built' % outfile
+    print('%s already built' % outfile)
   return outpath
 
 
@@ -74,7 +76,7 @@
   if not vcdir:
     vcdir = get_vc_dir()
     if not vcdir:
-      print 'Could not get VCINSTALLDIR. Run vsvars32.bat?'
+      print('Could not get VCINSTALLDIR. Run vsvars32.bat?')
       return 1
     os.environ['PATH'] += (';' + os.path.join(vcdir, 'bin') +
                            ';' + os.path.join(vcdir, r'..\Common7\IDE'))
@@ -82,15 +84,15 @@
   # Verify that we can find link.exe.
   link = os.path.join(vcdir, 'bin', 'link.exe')
   if not os.path.exists(link):
-    print 'link.exe not found at %s' % link
+    print('link.exe not found at %s' % link)
     return 1
 
   exe_name = build('limiter.cc')
   for shim_exe in ('lib.exe', 'link.exe'):
     newpath = '%s__LIMITER.exe' % shim_exe
     shutil.copyfile(exe_name, newpath)
-    print '%s shim built. Use with msbuild like: "/p:LinkToolExe=%s"' \
-        % (shim_exe, os.path.abspath(newpath))
+    print('%s shim built. Use with msbuild like: "/p:LinkToolExe=%s"' \
+        % (shim_exe, os.path.abspath(newpath)))
 
   return 0
 
diff --git a/tools/win/linker_verbose_tracking.py b/tools/win/linker_verbose_tracking.py
index b92cc34..e726d37e 100644
--- a/tools/win/linker_verbose_tracking.py
+++ b/tools/win/linker_verbose_tracking.py
@@ -66,6 +66,8 @@
         Command-line obj file: url_loader.mojom.obj
 """
 
+from __future__ import print_function
+
 import io
 import pdb
 import re
@@ -165,9 +167,9 @@
     targets.append(obj_name)
   # Print what we are searching for.
   if len(targets) == 1:
-    print 'Searching for %s' % targets[0]
+    print('Searching for %s' % targets[0])
   else:
-    print 'Searching for %s' % targets
+    print('Searching for %s' % targets)
   printed = False
   # Follow the chain of references up to an arbitrary maximum level, which has
   # so far never been approached.
@@ -179,28 +181,28 @@
         if target in cross_refs.keys():
           symbol = cross_refed_symbols[target]
           printed = True
-          print '%s.obj pulled in for symbol "%s" by' % (target, symbol)
+          print('%s.obj pulled in for symbol "%s" by' % (target, symbol))
           for ref in cross_refs[target]:
-            print '\t%s' % ref
+            print('\t%s' % ref)
             new_targets[ref] = True
     if len(new_targets) == 0:
       break
-    print
+    print()
     targets = new_targets.keys()
   if not printed:
-    print ('No references to %s found. Directly specified in sources or a '
+    print('No references to %s found. Directly specified in sources or a '
           'source_set?' % obj_name)
 
 
 def main():
   if len(sys.argv) < 3:
-    print r'Usage: %s <verbose_output_file> <objfile>' % sys.argv[0]
-    print r'Sample: %s chrome_dll_verbose.txt SkTLS' % sys.argv[0]
+    print(r'Usage: %s <verbose_output_file> <objfile>' % sys.argv[0])
+    print(r'Sample: %s chrome_dll_verbose.txt SkTLS' % sys.argv[0])
     return 0
   cross_refs, cross_refed_symbols = ParseVerbose(sys.argv[1])
-  print 'Database loaded - %d xrefs found' % len(cross_refs)
+  print('Database loaded - %d xrefs found' % len(cross_refs))
   if not len(cross_refs):
-    print 'No data found to analyze. Exiting'
+    print('No data found to analyze. Exiting')
     return 0
   TrackObj(cross_refs, cross_refed_symbols, sys.argv[2])
 
diff --git a/tools/win/pdb_compare_globals.py b/tools/win/pdb_compare_globals.py
index 0455a5d2..222837aa 100644
--- a/tools/win/pdb_compare_globals.py
+++ b/tools/win/pdb_compare_globals.py
@@ -18,6 +18,8 @@
   1       1824    0       0       LcidToLocaleNameTable   chrome.dll.pdb
 """
 
+from __future__ import print_function
+
 import os
 import subprocess
 import sys
@@ -32,7 +34,7 @@
   elif extension in ['.txt']:
     lines = open(pdb_name).readlines()
   else:
-    print 'Unrecognized extension in %s' % pdb_name
+    print('Unrecognized extension in %s' % pdb_name)
     return result
   for line in lines:
     parts = line.split('\t')
@@ -45,17 +47,17 @@
 
 
 def ShowExtras(symbols_A, symbols_B, name_A, name_B):
-  print 'Symbols that are in %s but not in %s' % (name_A, name_B)
+  print('Symbols that are in %s but not in %s' % (name_A, name_B))
   for key in symbols_A:
     if not key in symbols_B:
       # Print all the numerical data, followed by the symbol name, separated by
       # tabs.
-      print '\t'.join(symbols_A[key] + [key])
-  print
+      print('\t'.join(symbols_A[key] + [key]))
+  print()
 
 
 def ShowDifferences(symbols_A, symbols_B, name_A, name_B):
-  print 'Symbols that are changed from %s to %s' % (name_A, name_B)
+  print('Symbols that are changed from %s to %s' % (name_A, name_B))
   for key in symbols_A:
     if key in symbols_B:
       value_a = symbols_A[key]
@@ -63,10 +65,10 @@
       if value_a != value_b:
         # Print the symbol name and then the two versions of the numerical data,
         # indented.
-        print '%s changed from/to:' % key
-        print '\t' + '\t'.join(value_a)
-        print '\t' + '\t'.join(value_b)
-  print
+        print('%s changed from/to:' % key)
+        print('\t' + '\t'.join(value_a))
+        print('\t' + '\t'.join(value_b))
+  print()
 
 
 def main():
@@ -74,14 +76,14 @@
   symbols_2 = LoadSymbols(sys.argv[2])
 
   if len(symbols_1) == 0:
-    print 'No data found in %s - fastlink?' % sys.argv[1]
+    print('No data found in %s - fastlink?' % sys.argv[1])
     return
   if len(symbols_2) == 0:
-    print 'No data found in %s - fastlink?' % sys.argv[2]
+    print('No data found in %s - fastlink?' % sys.argv[2])
     return
 
-  print ('%d interesting globals in %s, %d interesting globals in %s' %
-         (len(symbols_1), sys.argv[1], len(symbols_2), sys.argv[2]))
+  print('%d interesting globals in %s, %d interesting globals in %s' %
+        (len(symbols_1), sys.argv[1], len(symbols_2), sys.argv[2]))
 
   ShowExtras(symbols_1, symbols_2, sys.argv[1], sys.argv[2])
   ShowExtras(symbols_2, symbols_1, sys.argv[2], sys.argv[1])
diff --git a/tools/win/pe_summarize.py b/tools/win/pe_summarize.py
index fa74d9e9..666ac6d 100644
--- a/tools/win/pe_summarize.py
+++ b/tools/win/pe_summarize.py
@@ -52,6 +52,8 @@
 therefore objectively 'worse' than the others.
 """
 
+from __future__ import print_function
+
 import os
 import subprocess
 import sys
@@ -66,9 +68,9 @@
 
 def main():
   if len(sys.argv) < 2:
-    print r'Usage: %s PEFileName [OtherPeFileNames...]' % sys.argv[0]
-    print r'Sample: %s chrome.dll' % sys.argv[0]
-    print r'Sample: %s chrome.dll original\chrome.dll' % sys.argv[0]
+    print(r'Usage: %s PEFileName [OtherPeFileNames...]' % sys.argv[0])
+    print(r'Sample: %s chrome.dll' % sys.argv[0])
+    print(r'Sample: %s chrome.dll original\chrome.dll' % sys.argv[0])
     return 0
 
   # Track the name of the last PE (Portable Executable) file to be processed -
@@ -78,11 +80,11 @@
   for pe_path in sys.argv[1:]:
     results = []
     if not os.path.exists(pe_path):
-      print '%s does not exist!' % pe_path
+      print('%s does not exist!' % pe_path)
       continue
 
-    print 'Size of %s is %1.6f MB' % (pe_path, os.path.getsize(pe_path) / 1e6)
-    print '%10s:  %9s  ,  %9s' % ('name', 'mem size', 'disk size')
+    print('Size of %s is %1.6f MB' % (pe_path, os.path.getsize(pe_path) / 1e6))
+    print('%10s:  %9s  ,  %9s' % ('name', 'mem size', 'disk size'))
 
     sections = None
     # Pass the undocumented /nopdb header to avoid hitting symbol servers for
@@ -108,10 +110,10 @@
             # understand - 33.199959 is easier to read than 33199959. Decimal MB
             # is used to allow simple conversions to a precise number of bytes.
             if abs(memory_size - disk_size) < 512:
-              print '%10s: %9.6f MB' % (name, memory_size / 1e6)
+              print('%10s: %9.6f MB' % (name, memory_size / 1e6))
             else:
-              print '%10s: %9.6f MB, %9.6f MB' % (name, memory_size / 1e6,
-                                                  disk_size / 1e6)
+              print('%10s: %9.6f MB, %9.6f MB' % (name, memory_size / 1e6,
+                                                  disk_size / 1e6))
             results.append((name, memory_size))
             sections = None
     except WindowsError as error:
@@ -120,14 +122,14 @@
                r'Visual Studio\2017\Professional\VC\Auxiliary\Build'
                r'\vcvarsall.bat amd64" or similar to add dumpbin to the path.')
       else:
-        print error
+        print(error)
       break
 
-    print
+    print()
     pe_filepart = os.path.split(pe_path)[1]
     if pe_filepart.lower() == last_pe_filepart.lower():
       # Print out the section-by-section size changes, for memory sizes only.
-      print 'Memory size change from %s to %s' % (last_pe_path, pe_path)
+      print('Memory size change from %s to %s' % (last_pe_path, pe_path))
       total_delta = 0
       for i in range(len(results)):
         section_name = results[i][0]
@@ -140,7 +142,7 @@
           delta -= last_results[last_i][1]
         total_delta += delta
         if delta:
-          print '%12s: %7d bytes change' % (section_name, delta)
+          print('%12s: %7d bytes change' % (section_name, delta))
       for last_i in range(len(last_results)):
         section_name = last_results[last_i][0]
         # Find sections that exist only in last_results.
@@ -148,8 +150,8 @@
         if i < 0:
           delta = -last_results[last_i][1]
           total_delta += delta
-          print '%12s: %7d bytes change' % (section_name, delta)
-      print 'Total change: %7d bytes' % total_delta
+          print('%12s: %7d bytes change' % (section_name, delta))
+      print('Total change: %7d bytes' % total_delta)
     last_pe_filepart = pe_filepart
     last_pe_path = pe_path
     last_results = results
diff --git a/tools/win/setenv.py b/tools/win/setenv.py
index 9e90399..4ff82d2 100644
--- a/tools/win/setenv.py
+++ b/tools/win/setenv.py
@@ -6,6 +6,8 @@
 Helper script to do the heavy lifting for setenv.bat.
 """
 
+from __future__ import print_function
+
 import os
 import sys
 
@@ -17,13 +19,13 @@
 
 if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))):
   win_sdk_dir = vs_toolchain.SetEnvironmentAndGetSDKDir()
-  print os.path.normpath(os.path.join(win_sdk_dir, 'bin/SetEnv.cmd'))
+  print(os.path.normpath(os.path.join(win_sdk_dir, 'bin/SetEnv.cmd')))
 else:
   vs_version = vs_toolchain.GetVisualStudioVersion()
   vs_path = vs_toolchain.DetectVisualStudioPath()
   if vs_version == '2017':
-    print os.path.join(vs_path, r'VC\Auxiliary\Build\vcvarsall.bat')
+    print(os.path.join(vs_path, r'VC\Auxiliary\Build\vcvarsall.bat'))
   elif vs_version == '2015':
-    print os.path.join(vs_path, r'VC\vcvarsall.bat')
+    print(os.path.join(vs_path, r'VC\vcvarsall.bat'))
   else:
     raise Exception('Unknown VS version %s' % vs_version)
diff --git a/tools/win/sizeviewer/sizeviewer.py b/tools/win/sizeviewer/sizeviewer.py
index 9fc4c8b6..d72a210 100644
--- a/tools/win/sizeviewer/sizeviewer.py
+++ b/tools/win/sizeviewer/sizeviewer.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from __future__ import print_function
+
 import base64
 import codecs
 import json
@@ -84,7 +86,7 @@
             for dll in ('chrome.dll', 'chrome_child.dll')]
   for dll_path in dlls:
     if os.path.exists(dll_path):
-      print 'Tallying %s...' % dll_path
+      print('Tallying %s...' % dll_path)
       json_path = dll_path + '.json'
       Run(os.path.join(BASE_DIR, '..', '..', '..', 'third_party', 'syzygy',
                        'binaries', 'exe', 'experimental', 'code_tally.exe'),
@@ -93,9 +95,10 @@
           '--output-file=' + json_path)
       jsons.append(json_path)
   if not jsons:
-    print 'Couldn\'t find dlls.'
-    print 'Pass fully qualified dll name(s) if you want to use something other '
-    print 'than out\\Release\\chrome.dll and chrome_child.dll.'
+    print('Couldn\'t find dlls.')
+    print(
+        'Pass fully qualified dll name(s) if you want to use something other ')
+    print('than out\\Release\\chrome.dll and chrome_child.dll.')
     return 1
 
   # Munge the code_tally json format into an easier-to-view format.
@@ -103,7 +106,7 @@
     with open(json_name, 'r') as jsonf:
       all_data = json.load(jsonf)
     html_path = os.path.splitext(json_name)[0] + '.html'
-    print 'Generating %s... (standlone)' % html_path
+    print('Generating %s... (standlone)' % html_path)
     by_source = {}
     symbols_index = {}
     symbols = []
diff --git a/tools/win/subtract_time.py b/tools/win/subtract_time.py
index e11c6bfc..563d6653 100644
--- a/tools/win/subtract_time.py
+++ b/tools/win/subtract_time.py
@@ -7,6 +7,8 @@
 subtracts them, and prints the difference. That's it. It's used by timeit.bat.
 """
 
+from __future__ import print_function
+
 import re
 import sys
 
@@ -17,5 +19,6 @@
   hours, minutes, seconds, fraction = map(int, match.groups())
   return hours * 3600 + minutes * 60 + seconds + fraction * .01
 
-print "%1.2f seconds elapsed time" % (ParseTime(sys.argv[1]) -
-                                      ParseTime(sys.argv[2]))
+
+print("%1.2f seconds elapsed time" %
+      (ParseTime(sys.argv[1]) - ParseTime(sys.argv[2])))
diff --git a/tools/yes_no.py b/tools/yes_no.py
index 8682ec8..aed3863 100644
--- a/tools/yes_no.py
+++ b/tools/yes_no.py
@@ -2,12 +2,14 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from __future__ import print_function
+
 import sys
 
 
 def YesNo(prompt):
   """Prompts with a yes/no question, returns True if yes."""
-  print prompt,
+  print(prompt, end=' ')
   sys.stdout.flush()
   # http://code.activestate.com/recipes/134892/
   if sys.platform == 'win32':
@@ -24,5 +26,5 @@
       ch = sys.stdin.read(1)
     finally:
       termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
-  print ch
+  print(ch)
   return ch in ('Y', 'y')
diff --git a/ui/aura/test/aura_test_helper.cc b/ui/aura/test/aura_test_helper.cc
index d17f4de..364504bc 100644
--- a/ui/aura/test/aura_test_helper.cc
+++ b/ui/aura/test/aura_test_helper.cc
@@ -57,10 +57,8 @@
 }
 
 AuraTestHelper::~AuraTestHelper() {
-  CHECK(setup_called_)
-      << "AuraTestHelper::SetUp() never called.";
-  CHECK(teardown_called_)
-      << "AuraTestHelper::TearDown() never called.";
+  CHECK(setup_called_) << "AuraTestHelper::SetUp() never called.";
+  CHECK(teardown_called_) << "AuraTestHelper::TearDown() never called.";
 }
 
 // static
@@ -107,6 +105,7 @@
   display::Screen* screen = display::Screen::GetScreen();
   gfx::Size host_size(screen ? screen->GetPrimaryDisplay().GetSizeInPixel()
                              : gfx::Size(800, 600));
+
   // This must be reset before creating TestScreen, which sets up the display
   // scale factor for this test iteration.
   display::Display::ResetForceDeviceScaleFactorForTesting();
diff --git a/ui/aura/window_targeter_unittest.cc b/ui/aura/window_targeter_unittest.cc
index d01378a..9ab8a23 100644
--- a/ui/aura/window_targeter_unittest.cc
+++ b/ui/aura/window_targeter_unittest.cc
@@ -21,8 +21,7 @@
 // Always returns the same window.
 class StaticWindowTargeter : public WindowTargeter {
  public:
-  explicit StaticWindowTargeter(aura::Window* window)
-      : window_(window) {}
+  explicit StaticWindowTargeter(aura::Window* window) : window_(window) {}
   ~StaticWindowTargeter() override {}
 
  private:
@@ -86,7 +85,6 @@
   display::Display display =
       display::Screen::GetScreen()->GetDisplayNearestWindow(root_window());
   EXPECT_EQ(display.bounds(), root_window()->GetBoundsInScreen());
-  EXPECT_EQ(display.bounds(), gfx::Rect(0, 0, 800, 600));
 
   // Mouse and touch presses inside the display yield null targets.
   gfx::Point inside = display.bounds().CenterPoint();
diff --git a/ui/aura/window_tree_host_unittest.cc b/ui/aura/window_tree_host_unittest.cc
index 2016411..b5652f4 100644
--- a/ui/aura/window_tree_host_unittest.cc
+++ b/ui/aura/window_tree_host_unittest.cc
@@ -20,26 +20,50 @@
 
 namespace aura {
 
+namespace {
+
+gfx::Rect ScaleRect(const gfx::Rect& rect_in_pixels, float scale) {
+  gfx::RectF rect_in_dip(rect_in_pixels);
+  gfx::Transform transform;
+  transform.Scale(scale, scale);
+  transform.TransformRectReverse(&rect_in_dip);
+  return gfx::ToEnclosingRect(rect_in_dip);
+}
+
+gfx::Rect TransformRect(const gfx::Rect& rect_in_pixels,
+                        const gfx::Transform& transform,
+                        float device_scale_factor) {
+  gfx::RectF new_bounds =
+      gfx::ScaleRect(gfx::RectF(rect_in_pixels), 1.0f / device_scale_factor);
+  transform.TransformRect(&new_bounds);
+  return gfx::ToEnclosingRect(new_bounds);
+}
+
+}  // namespace
+
 using WindowTreeHostTest = test::AuraTestBase;
 
 TEST_F(WindowTreeHostTest, DPIWindowSize) {
-  gfx::Rect starting_bounds(0, 0, 800, 600);
+  gfx::Rect starting_bounds = host()->GetBoundsInPixels();
   EXPECT_EQ(starting_bounds.size(), host()->compositor()->size());
-  EXPECT_EQ(starting_bounds, host()->GetBoundsInPixels());
   EXPECT_EQ(starting_bounds, root_window()->bounds());
 
-  test_screen()->SetDeviceScaleFactor(1.5f);
+  float device_scale_factor = 1.5;
+  test_screen()->SetDeviceScaleFactor(device_scale_factor);
   EXPECT_EQ(starting_bounds, host()->GetBoundsInPixels());
   // Size should be rounded up after scaling.
-  EXPECT_EQ(gfx::Rect(0, 0, 534, 400), root_window()->bounds());
+  gfx::Rect rect_in_dip = ScaleRect(starting_bounds, device_scale_factor);
+  EXPECT_EQ(rect_in_dip, root_window()->bounds());
 
   gfx::Transform transform;
   transform.Translate(0, 1.1f);
   host()->SetRootTransform(transform);
-  EXPECT_EQ(gfx::Rect(0, 1, 534, 401), root_window()->bounds());
+  gfx::Rect transformed_rect =
+      TransformRect(starting_bounds, transform, device_scale_factor);
+  EXPECT_EQ(transformed_rect, root_window()->bounds());
 
   EXPECT_EQ(starting_bounds, host()->GetBoundsInPixels());
-  EXPECT_EQ(gfx::Rect(0, 1, 534, 401), root_window()->bounds());
+  EXPECT_EQ(transformed_rect, root_window()->bounds());
 }
 
 #if defined(OS_CHROMEOS)
diff --git a/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.cc b/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.cc
index 8a464d5..ec93a4b 100644
--- a/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.cc
+++ b/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.cc
@@ -261,7 +261,7 @@
 OnScreenKeyboardDisplayManagerTabTip::~OnScreenKeyboardDisplayManagerTabTip() {}
 
 bool OnScreenKeyboardDisplayManagerTabTip::DisplayVirtualKeyboard() {
-  if (base::win::IsKeyboardPresentOnSlate(nullptr, ui::GetHiddenWindow()))
+  if (base::win::IsKeyboardPresentOnSlate(ui::GetHiddenWindow(), nullptr))
     return false;
 
   if (osk_path_.empty() && !GetOSKPath(&osk_path_)) {
diff --git a/ui/file_manager/file_manager/background/js/media_import_handler.js b/ui/file_manager/file_manager/background/js/media_import_handler.js
index 3194f1a..0c75302b 100644
--- a/ui/file_manager/file_manager/background/js/media_import_handler.js
+++ b/ui/file_manager/file_manager/background/js/media_import_handler.js
@@ -9,15 +9,6 @@
 importer.MediaImportHandler.ImportTask =
     importer.MediaImportHandler.ImportTask || {};
 
-// The name of the Drive property used to tag imported files.  Used to look up
-// the property later.
-importer.MediaImportHandler.IMPORTS_TAG_KEY = 'cloud-import';
-
-// The value of the Drive property used to tag imported files.  Cloud import
-// only imports 'media' right now - change this to an enum if other types of
-// files start being imported.
-importer.MediaImportHandler.IMPORTS_TAG_VALUE = 'media';
-
 /**
  * Handler for importing media from removable devices into the user's Drive.
  *
@@ -71,7 +62,6 @@
         directoryPromise, destination, this.getDisposition_);
 
     task.addObserver(this.onTaskProgress_.bind(this, task));
-    task.addObserver(this.onFileImported_.bind(this, task));
 
     // Schedule the task when it is initialized.
     scanResult.whenFinal()
@@ -184,37 +174,6 @@
     this.driveSyncHandler_.addEventListener(
         this.driveSyncHandler_.getCompletedEventName(), task.driveListener_);
   }
-
-  /**
-   * Tags newly-imported files with a Drive property.
-   * @param {!importer.TaskQueue.Task} task
-   * @param {string} updateType
-   * @param {Object=} updateInfo
-   */
-  onFileImported_(task, updateType, updateInfo) {
-    if (updateType !==
-        importer.MediaImportHandler.ImportTask.UpdateType.ENTRY_CHANGED) {
-      return;
-    }
-    // Update info must exist for ENTRY_CHANGED notifications.
-    console.assert(updateInfo && updateInfo.destination);
-    const info =
-        /** @type {!importer.MediaImportHandler.ImportTask.EntryChangedInfo} */
-        (updateInfo);
-
-    // Tag the import with a private drive property.
-    chrome.fileManagerPrivate.setEntryTag(
-        info.destination,
-        'private',  // Scoped to just this app.
-        importer.MediaImportHandler.IMPORTS_TAG_KEY,
-        importer.MediaImportHandler.IMPORTS_TAG_VALUE, () => {
-          if (chrome.runtime.lastError) {
-            console.error(
-                'Unable to tag imported media: ' +
-                chrome.runtime.lastError.message);
-          }
-        });
-  }
 };
 
 /**
diff --git a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.js b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.js
index e60d38e..09e174b 100644
--- a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.js
+++ b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.js
@@ -63,9 +63,6 @@
         mockChrome.power.requestKeepAwakeStatus = false;
       }
     },
-    fileManagerPrivate: {
-      setEntryTag: function() {},
-    },
   };
 
   installMockChrome(mockChrome);
@@ -423,52 +420,6 @@
 }
 
 /**
- * Tests that media imports tag entries after import.
- */
-function testTagsEntriesAfterImport(callback) {
-  const entries = setupFileSystem([
-    '/DCIM/photos0/IMG00001.jpg',
-    '/DCIM/photos1/IMG00003.jpg',
-  ]);
-
-  const scanResult = new TestScanResult(entries);
-  const importTask = mediaImporter.importFromScanResult(
-      scanResult, importer.Destination.GOOGLE_DRIVE, destinationFactory);
-
-  const whenImportDone = new Promise((resolve, reject) => {
-    importTask.addObserver(
-        /**
-         * @param {!importer.TaskQueue.UpdateType} updateType
-         * @param {Object=} opt_task
-         */
-        (updateType, opt_task) => {
-          switch (updateType) {
-            case importer.TaskQueue.UpdateType.COMPLETE:
-              resolve();
-              break;
-            case importer.TaskQueue.UpdateType.ERROR:
-              reject(new Error(importer.TaskQueue.UpdateType.ERROR));
-              break;
-          }
-        });
-  });
-
-  const taggedEntries = [];
-  // Replace chrome.fileManagerPrivate.setEntryTag with a listener.
-  mockChrome.fileManagerPrivate.setEntryTag = entry => {
-    taggedEntries.push(entry);
-  };
-
-  reportPromise(
-      whenImportDone.then(() => {
-        assertEquals(entries.length, taggedEntries.length);
-      }),
-      callback);
-
-  scanResult.finalize();
-}
-
-/**
  * Tests cancelling a media import.
  */
 function testImportCancellation(callback) {
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
index ccf265c..102bcaab 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
@@ -639,14 +639,14 @@
    * @private
    */
   setupEjectButton_(rowElement) {
-    const ejectButton = cr.doc.createElement('button');
+    const ejectButton = document.createElement('button');
 
     ejectButton.className = 'root-eject align-right-icon';
     ejectButton.setAttribute('aria-label', str('UNMOUNT_DEVICE_BUTTON_LABEL'));
     ejectButton.setAttribute('tabindex', '0');
 
     // Add paper-ripple effect on the eject button.
-    const ripple = cr.doc.createElement('paper-ripple');
+    const ripple = document.createElement('paper-ripple');
     ripple.setAttribute('fit', '');
     ripple.className = 'circle recenteringTouch';
     ejectButton.appendChild(ripple);
@@ -666,7 +666,8 @@
     });
     ejectButton.addEventListener('click', (event) => {
       event.stopPropagation();
-      const command = cr.doc.querySelector('command#unmount');
+      const command = /** @type {!cr.ui.Command} */ (
+          document.querySelector('command#unmount'));
       // Ensure 'canExecute' state of the command is properly setup for the
       // root before executing it.
       command.canExecuteChange(this);
@@ -1063,7 +1064,7 @@
    * @private
    */
   setupRenamePlaceholder_(rowElement) {
-    const placeholder = cr.doc.createElement('span');
+    const placeholder = document.createElement('span');
     placeholder.className = 'rename-placeholder';
     rowElement.querySelector('.label').insertAdjacentElement(
         'afterend', placeholder);
@@ -1614,7 +1615,7 @@
 
     // Create an external link icon. TODO(crbug.com/986169) does this icon
     // element need aria-label, role, tabindex, etc?
-    const externalLinkIcon = cr.doc.createElement('span');
+    const externalLinkIcon = document.createElement('span');
     externalLinkIcon.className = 'external-link-icon align-right-icon';
 
     // Add the external link as the last element of the tree row content.
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index d70a3bc9..9b34d4c 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -134,6 +134,8 @@
     "ios/NSString+CrStringDrawing.mm",
     "ios/uikit_util.h",
     "ios/uikit_util.mm",
+    "linux/fontconfig_util.cc",
+    "linux/fontconfig_util.h",
     "mac/coordinate_conversion.h",
     "mac/coordinate_conversion.mm",
     "mac/nswindow_frame_controls.h",
@@ -849,7 +851,10 @@
   }
 
   if (is_linux) {
-    sources += [ "linux/native_pixmap_dmabuf_unittest.cc" ]
+    sources += [
+      "linux/fontconfig_util_unittest.cc",
+      "linux/native_pixmap_dmabuf_unittest.cc",
+    ]
     deps += [ "//third_party/fontconfig" ]
   }
 
diff --git a/ui/gfx/font_fallback_linux.cc b/ui/gfx/font_fallback_linux.cc
index db36d85..7086af8 100644
--- a/ui/gfx/font_fallback_linux.cc
+++ b/ui/gfx/font_fallback_linux.cc
@@ -17,6 +17,7 @@
 #include "base/no_destructor.h"
 #include "base/trace_event/trace_event.h"
 #include "ui/gfx/font.h"
+#include "ui/gfx/linux/fontconfig_util.h"
 
 namespace gfx {
 
@@ -85,11 +86,6 @@
                      const std::string& locale,
                      base::StringPiece16 text,
                      Font* result) {
-  struct FcPatternDeleter {
-    void operator()(FcPattern* ptr) const { FcPatternDestroy(ptr); }
-  };
-  using ScopedFcPattern = std::unique_ptr<FcPattern, FcPatternDeleter>;
-
   TRACE_EVENT0("fonts", "gfx::GetFallbackFont");
 
   // The text passed must be at least length 1.
diff --git a/ui/gfx/font_render_params_linux.cc b/ui/gfx/font_render_params_linux.cc
index 18d44fab..fed4e0f 100644
--- a/ui/gfx/font_render_params_linux.cc
+++ b/ui/gfx/font_render_params_linux.cc
@@ -22,6 +22,7 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "ui/gfx/font.h"
+#include "ui/gfx/linux/fontconfig_util.h"
 #include "ui/gfx/skia_font_delegate.h"
 #include "ui/gfx/switches.h"
 
@@ -103,27 +104,6 @@
 base::LazyInstance<SynchronizedCache>::Leaky g_synchronized_cache =
     LAZY_INSTANCE_INITIALIZER;
 
-// Converts Fontconfig FC_HINT_STYLE to FontRenderParams::Hinting.
-FontRenderParams::Hinting ConvertFontconfigHintStyle(int hint_style) {
-  switch (hint_style) {
-    case FC_HINT_SLIGHT: return FontRenderParams::HINTING_SLIGHT;
-    case FC_HINT_MEDIUM: return FontRenderParams::HINTING_MEDIUM;
-    case FC_HINT_FULL:   return FontRenderParams::HINTING_FULL;
-    default:             return FontRenderParams::HINTING_NONE;
-  }
-}
-
-// Converts Fontconfig FC_RGBA to FontRenderParams::SubpixelRendering.
-FontRenderParams::SubpixelRendering ConvertFontconfigRgba(int rgba) {
-  switch (rgba) {
-    case FC_RGBA_RGB:  return FontRenderParams::SUBPIXEL_RENDERING_RGB;
-    case FC_RGBA_BGR:  return FontRenderParams::SUBPIXEL_RENDERING_BGR;
-    case FC_RGBA_VRGB: return FontRenderParams::SUBPIXEL_RENDERING_VRGB;
-    case FC_RGBA_VBGR: return FontRenderParams::SUBPIXEL_RENDERING_VBGR;
-    default:           return FontRenderParams::SUBPIXEL_RENDERING_NONE;
-  }
-}
-
 // Queries Fontconfig for rendering settings and updates |params_out| and
 // |family_out| (if non-NULL). Returns false on failure.
 bool QueryFontconfig(const FontRenderParamsQuery& query,
@@ -131,11 +111,6 @@
                      std::string* family_out) {
   TRACE_EVENT0("fonts", "gfx::QueryFontconfig");
 
-  struct FcPatternDeleter {
-    void operator()(FcPattern* ptr) const { FcPatternDestroy(ptr); }
-  };
-  typedef std::unique_ptr<FcPattern, FcPatternDeleter> ScopedFcPattern;
-
   ScopedFcPattern query_pattern(FcPatternCreate());
   CHECK(query_pattern);
 
@@ -190,42 +165,8 @@
       family_out->assign(reinterpret_cast<const char*>(family));
   }
 
-  if (params_out) {
-    FcBool fc_antialias = 0;
-    if (FcPatternGetBool(result_pattern.get(), FC_ANTIALIAS, 0,
-                         &fc_antialias) == FcResultMatch) {
-      params_out->antialiasing = fc_antialias;
-    }
-
-    FcBool fc_autohint = 0;
-    if (FcPatternGetBool(result_pattern.get(), FC_AUTOHINT, 0, &fc_autohint) ==
-        FcResultMatch) {
-      params_out->autohinter = fc_autohint;
-    }
-
-    FcBool fc_bitmap = 0;
-    if (FcPatternGetBool(result_pattern.get(), FC_EMBEDDED_BITMAP, 0,
-                         &fc_bitmap) ==
-        FcResultMatch) {
-      params_out->use_bitmaps = fc_bitmap;
-    }
-
-    FcBool fc_hinting = 0;
-    if (FcPatternGetBool(result_pattern.get(), FC_HINTING, 0, &fc_hinting) ==
-        FcResultMatch) {
-      int fc_hint_style = FC_HINT_NONE;
-      if (fc_hinting) {
-        FcPatternGetInteger(
-            result_pattern.get(), FC_HINT_STYLE, 0, &fc_hint_style);
-      }
-      params_out->hinting = ConvertFontconfigHintStyle(fc_hint_style);
-    }
-
-    int fc_rgba = FC_RGBA_NONE;
-    if (FcPatternGetInteger(result_pattern.get(), FC_RGBA, 0, &fc_rgba) ==
-        FcResultMatch)
-      params_out->subpixel_rendering = ConvertFontconfigRgba(fc_rgba);
-  }
+  if (params_out)
+    GetFontRenderParamsFromFcPattern(result_pattern.get(), params_out);
 
   return true;
 }
diff --git a/ui/gfx/linux/fontconfig_util.cc b/ui/gfx/linux/fontconfig_util.cc
new file mode 100644
index 0000000..fa42dc2
--- /dev/null
+++ b/ui/gfx/linux/fontconfig_util.cc
@@ -0,0 +1,81 @@
+// Copyright 2019 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 "ui/gfx/linux/fontconfig_util.h"
+
+#include <fontconfig/fontconfig.h>
+
+#include "ui/gfx/font_render_params.h"
+
+namespace gfx {
+
+namespace {
+
+// Converts Fontconfig FC_HINT_STYLE to FontRenderParams::Hinting.
+FontRenderParams::Hinting ConvertFontconfigHintStyle(int hint_style) {
+  switch (hint_style) {
+    case FC_HINT_SLIGHT:
+      return FontRenderParams::HINTING_SLIGHT;
+    case FC_HINT_MEDIUM:
+      return FontRenderParams::HINTING_MEDIUM;
+    case FC_HINT_FULL:
+      return FontRenderParams::HINTING_FULL;
+    default:
+      return FontRenderParams::HINTING_NONE;
+  }
+}
+
+// Converts Fontconfig FC_RGBA to FontRenderParams::SubpixelRendering.
+FontRenderParams::SubpixelRendering ConvertFontconfigRgba(int rgba) {
+  switch (rgba) {
+    case FC_RGBA_RGB:
+      return FontRenderParams::SUBPIXEL_RENDERING_RGB;
+    case FC_RGBA_BGR:
+      return FontRenderParams::SUBPIXEL_RENDERING_BGR;
+    case FC_RGBA_VRGB:
+      return FontRenderParams::SUBPIXEL_RENDERING_VRGB;
+    case FC_RGBA_VBGR:
+      return FontRenderParams::SUBPIXEL_RENDERING_VBGR;
+    default:
+      return FontRenderParams::SUBPIXEL_RENDERING_NONE;
+  }
+}
+
+}  // namespace
+
+void GetFontRenderParamsFromFcPattern(FcPattern* pattern,
+                                      FontRenderParams* param_out) {
+  FcBool fc_antialias = 0;
+  if (FcPatternGetBool(pattern, FC_ANTIALIAS, 0, &fc_antialias) ==
+      FcResultMatch) {
+    param_out->antialiasing = fc_antialias;
+  }
+
+  FcBool fc_autohint = 0;
+  if (FcPatternGetBool(pattern, FC_AUTOHINT, 0, &fc_autohint) ==
+      FcResultMatch) {
+    param_out->autohinter = fc_autohint;
+  }
+
+  FcBool fc_bitmap = 0;
+  if (FcPatternGetBool(pattern, FC_EMBEDDED_BITMAP, 0, &fc_bitmap) ==
+      FcResultMatch) {
+    param_out->use_bitmaps = fc_bitmap;
+  }
+
+  FcBool fc_hinting = 0;
+  if (FcPatternGetBool(pattern, FC_HINTING, 0, &fc_hinting) == FcResultMatch) {
+    int fc_hint_style = FC_HINT_NONE;
+    if (fc_hinting) {
+      FcPatternGetInteger(pattern, FC_HINT_STYLE, 0, &fc_hint_style);
+    }
+    param_out->hinting = ConvertFontconfigHintStyle(fc_hint_style);
+  }
+
+  int fc_rgba = FC_RGBA_NONE;
+  if (FcPatternGetInteger(pattern, FC_RGBA, 0, &fc_rgba) == FcResultMatch)
+    param_out->subpixel_rendering = ConvertFontconfigRgba(fc_rgba);
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/linux/fontconfig_util.h b/ui/gfx/linux/fontconfig_util.h
new file mode 100644
index 0000000..b55080a
--- /dev/null
+++ b/ui/gfx/linux/fontconfig_util.h
@@ -0,0 +1,27 @@
+// Copyright 2019 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 UI_GFX_LINUX_FONTCONFIG_UTIL_H_
+#define UI_GFX_LINUX_FONTCONFIG_UTIL_H_
+
+#include <fontconfig/fontconfig.h>
+
+#include "ui/gfx/font_render_params.h"
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+
+struct FcPatternDeleter {
+  void operator()(FcPattern* ptr) const { FcPatternDestroy(ptr); }
+};
+using ScopedFcPattern = std::unique_ptr<FcPattern, FcPatternDeleter>;
+
+// Returns the appropriate parameters for rendering the font represented by the
+// font config pattern.
+GFX_EXPORT void GetFontRenderParamsFromFcPattern(FcPattern* pattern,
+                                                 FontRenderParams* param_out);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_LINUX_FONTCONFIG_UTIL_H_
\ No newline at end of file
diff --git a/ui/gfx/linux/fontconfig_util_unittest.cc b/ui/gfx/linux/fontconfig_util_unittest.cc
new file mode 100644
index 0000000..ef2ca85
--- /dev/null
+++ b/ui/gfx/linux/fontconfig_util_unittest.cc
@@ -0,0 +1,60 @@
+// Copyright 2019 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 "ui/gfx/linux/fontconfig_util.h"
+
+#include <fontconfig/fontconfig.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gfx {
+
+TEST(FontConfigUtilTest, GetFontRenderParamsFromFcPatternWithEmptyPattern) {
+  ScopedFcPattern pattern(FcPatternCreate());
+
+  FontRenderParams params;
+  GetFontRenderParamsFromFcPattern(pattern.get(), &params);
+
+  FontRenderParams empty_params;
+  EXPECT_EQ(params, empty_params);
+}
+
+TEST(FontConfigUtilTest, GetFontRenderParamsFromFcPatternWithFalseValues) {
+  ScopedFcPattern pattern(FcPatternCreate());
+  FcPatternAddBool(pattern.get(), FC_ANTIALIAS, FcFalse);
+  FcPatternAddBool(pattern.get(), FC_AUTOHINT, FcFalse);
+  FcPatternAddBool(pattern.get(), FC_EMBEDDED_BITMAP, FcFalse);
+
+  FontRenderParams params;
+  GetFontRenderParamsFromFcPattern(pattern.get(), &params);
+
+  FontRenderParams expected_params;
+  expected_params.antialiasing = false;
+  expected_params.autohinter = false;
+  expected_params.use_bitmaps = false;
+  EXPECT_EQ(params, expected_params);
+}
+
+TEST(FontConfigUtilTest, GetFontRenderParamsFromFcPatternWithValues) {
+  ScopedFcPattern pattern(FcPatternCreate());
+  FcPatternAddBool(pattern.get(), FC_ANTIALIAS, FcTrue);
+  FcPatternAddBool(pattern.get(), FC_AUTOHINT, FcTrue);
+  FcPatternAddInteger(pattern.get(), FC_HINT_STYLE, FC_HINT_MEDIUM);
+  FcPatternAddBool(pattern.get(), FC_EMBEDDED_BITMAP, FcTrue);
+  FcPatternAddInteger(pattern.get(), FC_RGBA, FC_RGBA_RGB);
+
+  FontRenderParams params;
+  GetFontRenderParamsFromFcPattern(pattern.get(), &params);
+
+  FontRenderParams expected_params;
+  expected_params.antialiasing = true;
+  expected_params.autohinter = true;
+  expected_params.hinting = FontRenderParams::HINTING_MEDIUM;
+  expected_params.use_bitmaps = true;
+  expected_params.subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_RGB;
+
+  EXPECT_EQ(params, expected_params);
+}
+
+}  // namespace gfx
diff --git a/ui/ozone/platform/drm/host/drm_window_host.cc b/ui/ozone/platform/drm/host/drm_window_host.cc
index f7edb74..7a0089a 100644
--- a/ui/ozone/platform/drm/host/drm_window_host.cc
+++ b/ui/ozone/platform/drm/host/drm_window_host.cc
@@ -65,14 +65,11 @@
                                            : cursor_confined_bounds_;
 }
 
-void DrmWindowHost::Show() {
-}
+void DrmWindowHost::Show(bool inactive) {}
 
-void DrmWindowHost::Hide() {
-}
+void DrmWindowHost::Hide() {}
 
-void DrmWindowHost::Close() {
-}
+void DrmWindowHost::Close() {}
 
 bool DrmWindowHost::IsVisible() const {
   NOTREACHED();
@@ -91,8 +88,7 @@
   return bounds_;
 }
 
-void DrmWindowHost::SetTitle(const base::string16& title) {
-}
+void DrmWindowHost::SetTitle(const base::string16& title) {}
 
 void DrmWindowHost::SetCapture() {
   window_manager_->GrabEvents(widget_);
@@ -106,17 +102,13 @@
   return widget_ == window_manager_->event_grabber();
 }
 
-void DrmWindowHost::ToggleFullscreen() {
-}
+void DrmWindowHost::ToggleFullscreen() {}
 
-void DrmWindowHost::Maximize() {
-}
+void DrmWindowHost::Maximize() {}
 
-void DrmWindowHost::Minimize() {
-}
+void DrmWindowHost::Minimize() {}
 
-void DrmWindowHost::Restore() {
-}
+void DrmWindowHost::Restore() {}
 
 PlatformWindowState DrmWindowHost::GetPlatformWindowState() const {
   return PlatformWindowState::kUnknown;
diff --git a/ui/ozone/platform/drm/host/drm_window_host.h b/ui/ozone/platform/drm/host/drm_window_host.h
index d6ae9e0..59ce95713 100644
--- a/ui/ozone/platform/drm/host/drm_window_host.h
+++ b/ui/ozone/platform/drm/host/drm_window_host.h
@@ -58,7 +58,7 @@
   gfx::Rect GetCursorConfinedBounds() const;
 
   // PlatformWindow:
-  void Show() override;
+  void Show(bool inactive) override;
   void Hide() override;
   void Close() override;
   bool IsVisible() const override;
@@ -103,7 +103,7 @@
   DrmWindowHostManager* const window_manager_;    // Not owned.
   DrmDisplayHostManager* const display_manager_;  // Not owned.
   // TODO(crbug.com/936425): Remove after VizDisplayCompositor feature launches.
-  DrmOverlayManager* const overlay_manager_;      // Not owned.
+  DrmOverlayManager* const overlay_manager_;  // Not owned.
 
   gfx::Rect bounds_;
   const gfx::AcceleratedWidget widget_;
diff --git a/ui/ozone/platform/scenic/scenic_window.cc b/ui/ozone/platform/scenic/scenic_window.cc
index 99affc52..ecd987f7 100644
--- a/ui/ozone/platform/scenic/scenic_window.cc
+++ b/ui/ozone/platform/scenic/scenic_window.cc
@@ -84,7 +84,7 @@
   NOTIMPLEMENTED();
 }
 
-void ScenicWindow::Show() {
+void ScenicWindow::Show(bool inactive) {
   view_.AddChild(node_);
 
   // Call Present() to ensure that the scenic session commands are processed,
@@ -201,7 +201,6 @@
   delegate_->OnBoundsChanged(size_rect);
 }
 
-
 void ScenicWindow::OnScenicError(zx_status_t status) {
   LOG(ERROR) << "scenic::Session failed with code " << status << ".";
   delegate_->OnClosed();
diff --git a/ui/ozone/platform/scenic/scenic_window.h b/ui/ozone/platform/scenic/scenic_window.h
index 97f66da2..e359261 100644
--- a/ui/ozone/platform/scenic/scenic_window.h
+++ b/ui/ozone/platform/scenic/scenic_window.h
@@ -50,7 +50,7 @@
   gfx::Rect GetBounds() override;
   void SetBounds(const gfx::Rect& bounds) override;
   void SetTitle(const base::string16& title) override;
-  void Show() override;
+  void Show(bool inactive) override;
   void Hide() override;
   void Close() override;
   bool IsVisible() const override;
diff --git a/ui/ozone/platform/wayland/host/wayland_window.cc b/ui/ozone/platform/wayland/host/wayland_window.cc
index 01e056a..65668d8 100644
--- a/ui/ozone/platform/wayland/host/wayland_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_window.cc
@@ -326,7 +326,7 @@
   connection_->StartDrag(data, operation);
 }
 
-void WaylandWindow::Show() {
+void WaylandWindow::Show(bool inactive) {
   if (!is_tooltip_)  // Tooltip windows should not get keyboard focus
     set_keyboard_focus(true);
 
diff --git a/ui/ozone/platform/wayland/host/wayland_window.h b/ui/ozone/platform/wayland/host/wayland_window.h
index 55c50c2..649b158 100644
--- a/ui/ozone/platform/wayland/host/wayland_window.h
+++ b/ui/ozone/platform/wayland/host/wayland_window.h
@@ -113,7 +113,7 @@
                  base::OnceCallback<void(int)> callback) override;
 
   // PlatformWindow
-  void Show() override;
+  void Show(bool inactive) override;
   void Hide() override;
   void Close() override;
   bool IsVisible() const override;
diff --git a/ui/platform_window/platform_window.h b/ui/platform_window/platform_window.h
index d4ca974..113ddef 100644
--- a/ui/platform_window/platform_window.h
+++ b/ui/platform_window/platform_window.h
@@ -30,7 +30,10 @@
   PlatformWindow();
   ~PlatformWindow() override;
 
-  virtual void Show() = 0;
+  // PlatformWindow maybe called with the |inactive| set to true in some cases.
+  // That means that the Window Manager must not activate the window when it is
+  // shown. Most of PlatformWindow may ignore this value if not supported.
+  virtual void Show(bool inactive = false) = 0;
   virtual void Hide() = 0;
   virtual void Close() = 0;
 
diff --git a/ui/platform_window/stub/stub_window.cc b/ui/platform_window/stub/stub_window.cc
index e4fe9059..79ac9d94 100644
--- a/ui/platform_window/stub/stub_window.cc
+++ b/ui/platform_window/stub/stub_window.cc
@@ -20,7 +20,7 @@
 
 StubWindow::~StubWindow() {}
 
-void StubWindow::Show() {}
+void StubWindow::Show(bool inactive) {}
 
 void StubWindow::Hide() {}
 
diff --git a/ui/platform_window/stub/stub_window.h b/ui/platform_window/stub/stub_window.h
index 09d8138..1504ffd5 100644
--- a/ui/platform_window/stub/stub_window.h
+++ b/ui/platform_window/stub/stub_window.h
@@ -28,7 +28,7 @@
 
  private:
   // PlatformWindow:
-  void Show() override;
+  void Show(bool inactive) override;
   void Hide() override;
   void Close() override;
   bool IsVisible() const override;
diff --git a/ui/platform_window/win/win_window.cc b/ui/platform_window/win/win_window.cc
index 730a1ee8..65c277c 100644
--- a/ui/platform_window/win/win_window.cc
+++ b/ui/platform_window/win/win_window.cc
@@ -63,8 +63,8 @@
     DestroyWindow(hwnd());
 }
 
-void WinWindow::Show() {
-  ShowWindow(hwnd(), SW_SHOWNORMAL);
+void WinWindow::Show(bool inactive) {
+  ShowWindow(hwnd(), inactive ? SW_SHOWNOACTIVATE : SW_SHOWNORMAL);
 }
 
 void WinWindow::Hide() {
diff --git a/ui/platform_window/win/win_window.h b/ui/platform_window/win/win_window.h
index 4152ecb..9d5eeb5 100644
--- a/ui/platform_window/win/win_window.h
+++ b/ui/platform_window/win/win_window.h
@@ -27,7 +27,7 @@
   void Destroy();
 
   // PlatformWindow:
-  void Show() override;
+  void Show(bool inactive) override;
   void Hide() override;
   void Close() override;
   bool IsVisible() const override;
diff --git a/ui/platform_window/x11/DEPS b/ui/platform_window/x11/DEPS
index dace8ae..f9f62a1 100644
--- a/ui/platform_window/x11/DEPS
+++ b/ui/platform_window/x11/DEPS
@@ -3,4 +3,5 @@
   "+ui/base",
   "+ui/events",
   "+ui/gfx",
+  "+ui/display",
 ]
diff --git a/ui/platform_window/x11/x11_window.cc b/ui/platform_window/x11/x11_window.cc
index a86ae7fa..e8d926b 100644
--- a/ui/platform_window/x11/x11_window.cc
+++ b/ui/platform_window/x11/x11_window.cc
@@ -5,6 +5,8 @@
 #include "ui/platform_window/x11/x11_window.h"
 
 #include "base/trace_event/trace_event.h"
+#include "ui/base/x/x11_util.h"
+#include "ui/display/screen.h"
 #include "ui/events/devices/x11/touch_factory_x11.h"
 #include "ui/events/event.h"
 #include "ui/events/event_utils.h"
@@ -87,6 +89,11 @@
 void X11Window::Initialize(PlatformWindowInitProperties properties) {
   XWindow::Configuration config =
       ConvertInitPropertiesToXWindowConfig(properties);
+
+  gfx::Size adjusted_size_in_pixels =
+      AdjustSizeForDisplay(config.bounds.size());
+  config.bounds.set_size(adjusted_size_in_pixels);
+
   Init(config);
 }
 
@@ -95,9 +102,11 @@
   x_event_delegate_ = delegate;
 }
 
-void X11Window::Show() {
-  // TODO(msisov): pass inactivity to PlatformWindow::Show.
-  XWindow::Map(false /* inactive */);
+void X11Window::Show(bool inactive) {
+  if (mapped_in_client())
+    return;
+
+  XWindow::Map(inactive);
 }
 
 void X11Window::Hide() {
@@ -122,17 +131,30 @@
 }
 
 void X11Window::SetBounds(const gfx::Rect& bounds) {
+  gfx::Rect current_bounds_in_pixels = GetBounds();
+  gfx::Rect bounds_in_pixels(bounds.origin(),
+                             AdjustSizeForDisplay(bounds.size()));
+
+  bool size_changed =
+      current_bounds_in_pixels.size() != bounds_in_pixels.size();
+
+  if (size_changed) {
+    // Only cancel the delayed resize task if we're already about to call
+    // OnHostResized in this function.
+    XWindow::CancelResize();
+  }
+
   // Assume that the resize will go through as requested, which should be the
   // case if we're running without a window manager.  If there's a window
   // manager, it can modify or ignore the request, but (per ICCCM) we'll get a
   // (possibly synthetic) ConfigureNotify about the actual size and correct
   // |bounds_| later.
-  XWindow::SetBounds(bounds);
+  XWindow::SetBounds(bounds_in_pixels);
 
   // Even if the pixel bounds didn't change this call to the delegate should
   // still happen. The device scale factor may have changed which effectively
   // changes the bounds.
-  platform_window_delegate_->OnBoundsChanged(bounds);
+  platform_window_delegate_->OnBoundsChanged(bounds_in_pixels);
 }
 
 gfx::Rect X11Window::GetBounds() {
@@ -157,13 +179,79 @@
 }
 
 void X11Window::ToggleFullscreen() {
-  bool is_fullscreen = IsFullscreen();
-  SetFullscreen(!is_fullscreen);
+  // Check if we need to fullscreen the window or not.
+  bool fullscreen = state_ != PlatformWindowState::kFullScreen;
+  if (fullscreen)
+    CancelResize();
+
+  // Work around a bug where if we try to unfullscreen, metacity immediately
+  // fullscreens us again. This is a little flickery and not necessary if
+  // there's a gnome-panel, but it's not easy to detect whether there's a
+  // panel or not.
+  bool unmaximize_and_remaximize = !fullscreen && IsMaximized() &&
+                                   ui::GuessWindowManager() == ui::WM_METACITY;
+
+  if (unmaximize_and_remaximize)
+    Restore();
+
+  // Fullscreen state changes have to be handled manually and then checked
+  // against configuration events, which come from a compositor. The reason
+  // of manually changing the |state_| is that the compositor answers
+  // about state changes asynchronously, which leads to a wrong return value in
+  // DesktopWindowTreeHostPlatform::IsFullscreen, for example, and media
+  // files can never be set to fullscreen. Wayland does the same.
+  if (fullscreen)
+    state_ = PlatformWindowState::kFullScreen;
+  else
+    state_ = PlatformWindowState::kUnknown;
+  SetFullscreen(fullscreen);
+
+  if (unmaximize_and_remaximize)
+    Maximize();
+
+  // Try to guess the size we will have after the switch to/from fullscreen:
+  // - (may) avoid transient states
+  // - works around Flash content which expects to have the size updated
+  //   synchronously.
+  // See https://crbug.com/361408
+  gfx::Rect bounds_in_pixels = GetBounds();
+  if (fullscreen) {
+    display::Screen* screen = display::Screen::GetScreen();
+    const display::Display display =
+        screen->GetDisplayMatching(bounds_in_pixels);
+    SetRestoredBoundsInPixels(bounds_in_pixels);
+    bounds_in_pixels = display.bounds();
+  } else {
+    bounds_in_pixels = GetRestoredBoundsInPixels();
+  }
+  // Do not go through SetBounds as long as it adjusts bounds and sets them to X
+  // Server. Instead, we just store the bounds and notify the client that the
+  // window occupies the entire screen.
+  XWindow::set_bounds(bounds_in_pixels);
+  platform_window_delegate_->OnBoundsChanged(bounds_in_pixels);
 }
 
 void X11Window::Maximize() {
-  if (IsFullscreen())
-    SetFullscreen(false);
+  if (IsFullscreen()) {
+    // Unfullscreen the window if it is fullscreen.
+    ToggleFullscreen();
+
+    // Resize the window so that it does not have the same size as a monitor.
+    // (Otherwise, some window managers immediately put the window back in
+    // fullscreen mode).
+    gfx::Rect bounds_in_pixels = GetBounds();
+    gfx::Rect adjusted_bounds_in_pixels(
+        bounds_in_pixels.origin(),
+        AdjustSizeForDisplay(bounds_in_pixels.size()));
+    if (adjusted_bounds_in_pixels != bounds_in_pixels)
+      SetBounds(adjusted_bounds_in_pixels);
+  }
+
+  // When we are in the process of requesting to maximize a window, we can
+  // accurately keep track of our restored bounds instead of relying on the
+  // heuristics that are in the PropertyNotify and ConfigureNotify handlers.
+  SetRestoredBoundsInPixels(GetBounds());
+
   XWindow::Maximize();
 }
 
@@ -172,10 +260,11 @@
 }
 
 void X11Window::Restore() {
-  if (IsFullscreen())
+  if (XWindow::IsFullscreen())
     ToggleFullscreen();
-  if (IsMaximized())
-    Unmaximize();
+  if (XWindow::IsMaximized())
+    XWindow::Unmaximize();
+  XWindow::Unhide();
 }
 
 PlatformWindowState X11Window::GetPlatformWindowState() const {
@@ -211,14 +300,11 @@
 }
 
 void X11Window::SetRestoredBoundsInPixels(const gfx::Rect& bounds) {
-  // TODO(crbug.com/848131): Restore bounds on restart
-  NOTIMPLEMENTED_LOG_ONCE();
+  restored_bounds_in_pixels_ = bounds;
 }
 
 gfx::Rect X11Window::GetRestoredBoundsInPixels() const {
-  // TODO(crbug.com/848131): Restore bounds on restart
-  NOTIMPLEMENTED_LOG_ONCE();
-  return gfx::Rect();
+  return restored_bounds_in_pixels_;
 }
 
 bool X11Window::ShouldWindowContentsBeTransparent() const {
@@ -378,4 +464,30 @@
   PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
 }
 
+gfx::Size X11Window::AdjustSizeForDisplay(
+    const gfx::Size& requested_size_in_pixels) {
+#if defined(OS_CHROMEOS)
+  // We do not need to apply the workaround for the ChromeOS.
+  return requested_size_in_pixels;
+#else
+  auto* screen = display::Screen::GetScreen();
+  if (screen) {
+    std::vector<display::Display> displays = screen->GetAllDisplays();
+    // Compare against all monitor sizes. The window manager can move the window
+    // to whichever monitor it wants.
+    for (const auto& display : displays) {
+      if (requested_size_in_pixels == display.GetSizeInPixel()) {
+        return gfx::Size(requested_size_in_pixels.width() - 1,
+                         requested_size_in_pixels.height() - 1);
+      }
+    }
+  }
+
+  // Do not request a 0x0 window size. It causes an XError.
+  gfx::Size size_in_pixels = requested_size_in_pixels;
+  size_in_pixels.SetToMax(gfx::Size(1, 1));
+  return size_in_pixels;
+#endif
+}
+
 }  // namespace ui
diff --git a/ui/platform_window/x11/x11_window.h b/ui/platform_window/x11/x11_window.h
index 1f52f50..8effaaf 100644
--- a/ui/platform_window/x11/x11_window.h
+++ b/ui/platform_window/x11/x11_window.h
@@ -46,7 +46,7 @@
   void SetXEventDelegate(XEventDelegate* delegate);
 
   // PlatformWindow:
-  void Show() override;
+  void Show(bool inactive) override;
   void Hide() override;
   void Close() override;
   bool IsVisible() const override;
@@ -117,6 +117,11 @@
   // X11WindowOzone sets own event dispatcher now.
   virtual void SetPlatformEventDispatcher();
 
+  // Adjusts |requested_size_in_pixels| to avoid the WM "feature" where setting
+  // the window size to the monitor size causes the WM to set the EWMH for
+  // fullscreen.
+  gfx::Size AdjustSizeForDisplay(const gfx::Size& requested_size_in_pixels);
+
   // Stores current state of this window.
   PlatformWindowState state_ = PlatformWindowState::kUnknown;
 
@@ -131,6 +136,9 @@
   // behavior if > 0.
   ui::ZOrderLevel z_order_ = ui::ZOrderLevel::kNormal;
 
+  // The bounds of our window before the window was maximized.
+  gfx::Rect restored_bounds_in_pixels_;
+
   DISALLOW_COPY_AND_ASSIGN(X11Window);
 };
 
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 2c53b92..ff4d8f6 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -1222,12 +1222,16 @@
           "widget/desktop_aura/desktop_screen_x11_unittest.cc",
           "widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc",
         ]
-      } else if (is_linux) {
+      }
+      if (is_linux || is_fuchsia) {
         sources += [
-          "widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc",
           "widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc",
         ]
       }
+      if (use_ozone) {
+        sources +=
+            [ "widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc" ]
+      }
     }
   }
 
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc
index cc0060610..7313b8bd 100644
--- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc
+++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc
@@ -30,7 +30,7 @@
   ~FakePlatformWindow() override = default;
 
   // ui::PlatformWindow
-  void Show() override {}
+  void Show(bool inactive) override {}
   void Hide() override {}
   void Close() override {}
   bool IsVisible() const override { return true; }
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
index 9d0b74e..519a6e1 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -26,7 +26,6 @@
 #include "ui/base/class_property.h"
 #include "ui/base/hit_test.h"
 #include "ui/base/ime/input_method.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/compositor/layer.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/point_conversions.h"
@@ -100,13 +99,6 @@
     init_params.type = full_screen ? Widget::InitParams::TYPE_WINDOW
                                    : is_menu ? Widget::InitParams::TYPE_MENU
                                              : Widget::InitParams::TYPE_POPUP;
-#if defined(OS_WIN)
-    // For menus, on Windows versions that support drop shadow remove
-    // the standard frame in order to keep just the shadow.
-    if (features::IsFormControlsRefreshEnabled() &&
-        init_params.type == Widget::InitParams::TYPE_MENU)
-      init_params.remove_standard_frame = true;
-#endif
     init_params.bounds = bounds;
     init_params.ownership = Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET;
     init_params.layer_type = ui::LAYER_NOT_DRAWN;
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
index 5b065a52..cea50c2 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
@@ -33,25 +33,6 @@
   DesktopWindowTreeHostPlatform::OnNativeWidgetCreated(params);
 }
 
-gfx::Size DesktopWindowTreeHostLinux::AdjustSizeForDisplay(
-    const gfx::Size& requested_size_in_pixels) {
-  std::vector<display::Display> displays =
-      display::Screen::GetScreen()->GetAllDisplays();
-  // Compare against all monitor sizes. The window manager can move the window
-  // to whichever monitor it wants.
-  for (const auto& display : displays) {
-    if (requested_size_in_pixels == display.GetSizeInPixel()) {
-      return gfx::Size(requested_size_in_pixels.width() - 1,
-                       requested_size_in_pixels.height() - 1);
-    }
-  }
-
-  // Do not request a 0x0 window size. It causes an XError.
-  gfx::Size size_in_pixels = requested_size_in_pixels;
-  size_in_pixels.SetToMax(gfx::Size(1, 1));
-  return size_in_pixels;
-}
-
 void DesktopWindowTreeHostLinux::OnDisplayMetricsChanged(
     const display::Display& display,
     uint32_t changed_metrics) {
@@ -77,12 +58,6 @@
 void DesktopWindowTreeHostLinux::AddAdditionalInitProperties(
     const Widget::InitParams& params,
     ui::PlatformWindowInitProperties* properties) {
-  // Calculate initial bounds
-  gfx::Rect bounds_in_pixels = ToPixelRect(properties->bounds);
-  gfx::Size adjusted_size = AdjustSizeForDisplay(bounds_in_pixels.size());
-  bounds_in_pixels.set_size(adjusted_size);
-  properties->bounds = bounds_in_pixels;
-
   // Set the background color on startup to make the initial flickering
   // happening between the XWindow is mapped and the first expose event
   // is completely handled less annoying. If possible, we use the content
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h
index 2dd740a..daa7aa5 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h
@@ -33,15 +33,6 @@
   // PlatformWindowDelegateBase:
   void OnClosed() override;
 
-  // Adjusts |requested_size| to avoid the WM "feature" where setting the
-  // window size to the monitor size causes the WM to set the EWMH for
-  // fullscreen.
-  //
-  // TODO(https://crbug.com/990756)): this method is mainly for X11
-  // impl (Wayland does not need this workaround). Move this to X11Window
-  // instead. We can't do it now as there are some methods that depend on this.
-  gfx::Size AdjustSizeForDisplay(const gfx::Size& requested_size_in_pixels);
-
  private:
   FRIEND_TEST_ALL_PREFIXES(DesktopWindowTreeHostLinuxTest, HitTest);
 
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
index 7335817c..e03cf09 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
@@ -28,6 +28,19 @@
 
 namespace {
 
+bool DetermineInactivity(ui::WindowShowState show_state) {
+  if (show_state != ui::SHOW_STATE_DEFAULT &&
+      show_state != ui::SHOW_STATE_NORMAL &&
+      show_state != ui::SHOW_STATE_INACTIVE &&
+      show_state != ui::SHOW_STATE_MAXIMIZED) {
+    // It will behave like SHOW_STATE_NORMAL.
+    NOTIMPLEMENTED_LOG_ONCE();
+  }
+
+  // See comment in PlatformWindow::Show().
+  return show_state == ui::SHOW_STATE_INACTIVE;
+}
+
 ui::PlatformWindowInitProperties ConvertWidgetInitParamsToInitProperties(
     const Widget::InitParams& params) {
   ui::PlatformWindowInitProperties properties;
@@ -58,7 +71,6 @@
       break;
   }
 
-  properties.bounds = params.bounds;
   properties.activatable =
       params.activatable == Widget::InitParams::ACTIVATABLE_YES;
   properties.force_show_in_taskbar = params.force_show_in_taskbar;
@@ -121,6 +133,9 @@
       ConvertWidgetInitParamsToInitProperties(params);
   AddAdditionalInitProperties(params, &properties);
 
+  // Calculate initial bounds.
+  properties.bounds = ToPixelRect(params.bounds);
+
   CreateAndSetPlatformWindow(std::move(properties));
   // Disable compositing on tooltips as a workaround for
   // https://crbug.com/442111.
@@ -234,25 +249,26 @@
 
 void DesktopWindowTreeHostPlatform::Show(ui::WindowShowState show_state,
                                          const gfx::Rect& restore_bounds) {
-  if (show_state == ui::SHOW_STATE_MAXIMIZED && !restore_bounds.IsEmpty())
-    platform_window()->SetRestoredBoundsInPixels(ToPixelRect(restore_bounds));
+  if (compositor())
+    SetVisible(true);
 
-  if (compositor()) {
-    platform_window()->Show();
-    compositor()->SetVisible(true);
-  }
+  platform_window()->Show(DetermineInactivity(show_state));
 
   switch (show_state) {
     case ui::SHOW_STATE_MAXIMIZED:
       platform_window()->Maximize();
+      if (!restore_bounds.IsEmpty()) {
+        // Enforce |restored_bounds_in_pixels_| since calling Maximize() could
+        // have reset it.
+        platform_window()->SetRestoredBoundsInPixels(
+            ToPixelRect(restore_bounds));
+      }
       break;
     case ui::SHOW_STATE_MINIMIZED:
       platform_window()->Minimize();
       break;
     case ui::SHOW_STATE_FULLSCREEN:
-      // TODO(sky): this isn't necessarily the same as explicitly setting
-      // fullscreen.
-      platform_window()->ToggleFullscreen();
+      SetFullscreen(true);
       break;
     default:
       break;
@@ -281,10 +297,10 @@
 }
 
 void DesktopWindowTreeHostPlatform::SetSize(const gfx::Size& size) {
-  gfx::Rect screen_bounds =
-      gfx::ConvertRectToDIP(device_scale_factor(), GetBoundsInPixels());
-  screen_bounds.set_size(size);
-  SetBoundsInDIP(screen_bounds);
+  gfx::Size size_in_pixels = ToPixelRect(gfx::Rect(size)).size();
+  auto bounds_in_pixels = GetBoundsInPixels();
+  bounds_in_pixels.set_size(size_in_pixels);
+  WindowTreeHostPlatform::SetBoundsInPixels(bounds_in_pixels);
 }
 
 void DesktopWindowTreeHostPlatform::StackAbove(aura::Window* window) {
@@ -329,9 +345,18 @@
 void DesktopWindowTreeHostPlatform::GetWindowPlacement(
     gfx::Rect* bounds,
     ui::WindowShowState* show_state) const {
-  NOTIMPLEMENTED_LOG_ONCE();
-  *bounds = gfx::Rect(0, 0, 640, 840);
-  *show_state = ui::SHOW_STATE_NORMAL;
+  *bounds = GetRestoredBounds();
+
+  if (IsFullscreen())
+    *show_state = ui::SHOW_STATE_FULLSCREEN;
+  else if (IsMinimized())
+    *show_state = ui::SHOW_STATE_MINIMIZED;
+  else if (IsMaximized())
+    *show_state = ui::SHOW_STATE_MAXIMIZED;
+  else if (!IsActive())
+    *show_state = ui::SHOW_STATE_INACTIVE;
+  else
+    *show_state = ui::SHOW_STATE_NORMAL;
 }
 
 gfx::Rect DesktopWindowTreeHostPlatform::GetWindowBoundsInScreen() const {
@@ -346,12 +371,18 @@
 }
 
 gfx::Rect DesktopWindowTreeHostPlatform::GetRestoredBounds() const {
+  // We can't reliably track the restored bounds of a window, but we can get
+  // the 90% case down. When *chrome* is the process that requests maximizing
+  // or restoring bounds, we can record the current bounds before we request
+  // maximization, and clear it when we detect a state change.
   gfx::Rect restored_bounds = platform_window()->GetRestoredBoundsInPixels();
+
   // When window is resized, |restored bounds| is not set and empty.
   // If |restored bounds| is empty, it returns the current window size.
-  gfx::Rect bounds =
-      !restored_bounds.IsEmpty() ? restored_bounds : GetBoundsInPixels();
-  return ToDIPRect(bounds);
+  if (!restored_bounds.IsEmpty())
+    return ToDIPRect(restored_bounds);
+
+  return GetWindowBoundsInScreen();
 }
 
 std::string DesktopWindowTreeHostPlatform::GetWorkspace() const {
@@ -385,14 +416,18 @@
 
 void DesktopWindowTreeHostPlatform::Maximize() {
   platform_window()->Maximize();
+  if (IsMinimized())
+    Show(ui::SHOW_STATE_NORMAL, gfx::Rect());
 }
 
 void DesktopWindowTreeHostPlatform::Minimize() {
+  ReleaseCapture();
   platform_window()->Minimize();
 }
 
 void DesktopWindowTreeHostPlatform::Restore() {
   platform_window()->Restore();
+  Show(ui::SHOW_STATE_NORMAL, gfx::Rect());
 }
 
 bool DesktopWindowTreeHostPlatform::IsMaximized() const {
@@ -499,8 +534,19 @@
 }
 
 void DesktopWindowTreeHostPlatform::SetFullscreen(bool fullscreen) {
-  if (IsFullscreen() != fullscreen)
-    platform_window()->ToggleFullscreen();
+  if (IsFullscreen() == fullscreen)
+    return;
+
+  platform_window()->ToggleFullscreen();
+
+  // The state must change synchronously to let media react on fullscreen
+  // changes.
+  DCHECK_EQ(fullscreen, IsFullscreen());
+
+  if (IsFullscreen() == fullscreen)
+    Relayout();
+  // Else: the widget will be relaid out either when the window bounds change
+  // or when |platform_window|'s fullscreen state changes.
 }
 
 bool DesktopWindowTreeHostPlatform::IsFullscreen() const {
@@ -570,6 +616,9 @@
 
 gfx::Transform DesktopWindowTreeHostPlatform::GetRootTransform() const {
   display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
+  // This might be called before the |platform_window| is created. Thus,
+  // explicitly check if that exists before trying to access its visibility and
+  // the display where it is shown.
   if (platform_window() && IsVisible()) {
     display = display::Screen::GetScreen()->GetDisplayNearestWindow(
         GetWidget()->GetNativeWindow());
@@ -581,6 +630,15 @@
   return transform;
 }
 
+void DesktopWindowTreeHostPlatform::ShowImpl() {
+  Show(ui::SHOW_STATE_NORMAL, gfx::Rect());
+}
+
+void DesktopWindowTreeHostPlatform::HideImpl() {
+  WindowTreeHostPlatform::HideImpl();
+  native_widget_delegate_->OnNativeWidgetVisibilityChanged(false);
+}
+
 void DesktopWindowTreeHostPlatform::DispatchEvent(ui::Event* event) {
 #if defined(USE_OZONE)
   // Make sure the |event| is marked as a non-client if it's a non-client
@@ -614,18 +672,28 @@
 
 void DesktopWindowTreeHostPlatform::OnWindowStateChanged(
     ui::PlatformWindowState new_state) {
+  bool was_minimized = old_state_ == ui::PlatformWindowState::kMinimized;
+  bool is_minimized = new_state == ui::PlatformWindowState::kMinimized;
+
   // Propagate minimization/restore to compositor to avoid drawing 'blank'
   // frames that could be treated as previews, which show content even if a
   // window is minimized.
-  bool visible = new_state != ui::PlatformWindowState::kMinimized;
-  if (visible != compositor()->IsVisible()) {
-    compositor()->SetVisible(visible);
-    native_widget_delegate_->OnNativeWidgetVisibilityChanged(visible);
+  if (is_minimized != was_minimized) {
+    if (is_minimized) {
+      SetVisible(false);
+      content_window()->Hide();
+    } else {
+      content_window()->Show();
+      SetVisible(true);
+    }
   }
 
-  // It might require relayouting when state property has been changed.
-  if (visible)
-    Relayout();
+  old_state_ = new_state;
+
+  // Now that we have different window properties, we may need to relayout the
+  // window. (The windows code doesn't need this because their window change is
+  // synchronous.)
+  Relayout();
 }
 
 void DesktopWindowTreeHostPlatform::OnCloseRequest() {
@@ -686,6 +754,13 @@
   return native_widget_delegate_->AsWidget();
 }
 
+void DesktopWindowTreeHostPlatform::SetVisible(bool visible) {
+  if (compositor())
+    compositor()->SetVisible(visible);
+
+  native_widget_delegate()->OnNativeWidgetVisibilityChanged(visible);
+}
+
 void DesktopWindowTreeHostPlatform::AddAdditionalInitProperties(
     const Widget::InitParams& params,
     ui::PlatformWindowInitProperties* properties) {}
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
index 7f095df0..65f4ed9e 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
@@ -5,6 +5,8 @@
 #ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_HOST_PLATFORM_H_
 #define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_HOST_PLATFORM_H_
 
+#include <vector>
+
 #include "base/memory/weak_ptr.h"
 #include "build/build_config.h"
 #include "ui/aura/window_tree_host_platform.h"
@@ -90,6 +92,8 @@
 
   // WindowTreeHost:
   gfx::Transform GetRootTransform() const override;
+  void ShowImpl() override;
+  void HideImpl() override;
 
   // PlatformWindowDelegateBase:
   void DispatchEvent(ui::Event* event) override;
@@ -123,6 +127,9 @@
   Widget* GetWidget();
   const Widget* GetWidget() const;
 
+  // Set visibility and fire OnNativeWidgetVisibilityChanged() if it changed.
+  void SetVisible(bool visible);
+
   // There are platform specific properties that Linux may want to add.
   virtual void AddAdditionalInitProperties(
       const Widget::InitParams& params,
@@ -140,6 +147,11 @@
   DesktopWindowTreeHostPlatform* window_parent_ = nullptr;
   std::set<DesktopWindowTreeHostPlatform*> window_children_;
 
+  // Keep track of PlatformWindow state so that we would react correctly and set
+  // visibility only if the window was minimized or was unminimized from the
+  // normal state.
+  ui::PlatformWindowState old_state_ = ui::PlatformWindowState::kUnknown;
+
   base::WeakPtrFactory<DesktopWindowTreeHostPlatform> close_widget_factory_{
       this};
   base::WeakPtrFactory<DesktopWindowTreeHostPlatform> weak_factory_{this};
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
index 71ffafc..d14c81e 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc
@@ -12,41 +12,80 @@
 
 namespace {
 
-class WidgetDestroyingObserver : public WidgetObserver {
+class TestWidgetObserver : public WidgetObserver {
  public:
-  explicit WidgetDestroyingObserver(Widget* widget) : widget_(widget) {
+  enum class Change {
+    kVisibility,
+    kDestroying,
+  };
+
+  explicit TestWidgetObserver(Widget* widget) : widget_(widget) {
+    DCHECK(widget_);
     widget_->AddObserver(this);
   }
-  ~WidgetDestroyingObserver() override {
+  ~TestWidgetObserver() override {
+    // This might have been destroyed by the widget destroying delegate call.
     if (widget_)
       widget_->RemoveObserver(this);
   }
 
-  // Returns immediately when |widget_| becomes NULL, otherwise a RunLoop is
-  // used until widget closing event is received.
-  void Wait() {
-    if (!on_widget_destroying_)
-      run_loop_.Run();
+  // Waits for notification changes for the |change|. |old_value| must be
+  // provided to be sure that this is not called after the change has already
+  // happened - e.g. synchronous change.
+  void WaitForChange(Change change, bool old_value) {
+    switch (change) {
+      case Change::kVisibility:
+        if (old_value == visible_)
+          Wait();
+        break;
+      case Change::kDestroying:
+        if (old_value == on_widget_destroying_)
+          Wait();
+        break;
+      default:
+        NOTREACHED() << "unknown value";
+        break;
+    }
   }
 
   bool widget_destroying() const { return on_widget_destroying_; }
+  bool visible() const { return visible_; }
 
  private:
-  // views::WidgetObserver override:
+  // views::WidgetObserver overrides:
   void OnWidgetDestroying(Widget* widget) override {
     DCHECK_EQ(widget_, widget);
     widget_->RemoveObserver(this);
     widget_ = nullptr;
     on_widget_destroying_ = true;
-    if (run_loop_.running())
-      run_loop_.Quit();
+    StopWaiting();
+  }
+  void OnWidgetVisibilityChanged(Widget* widget, bool visible) override {
+    DCHECK_EQ(widget_, widget);
+    visible_ = visible;
+    StopWaiting();
+  }
+
+  void Wait() {
+    ASSERT_FALSE(run_loop_);
+    run_loop_ = std::make_unique<base::RunLoop>();
+    run_loop_->Run();
+    run_loop_.reset();
+  }
+
+  void StopWaiting() {
+    if (!run_loop_)
+      return;
+    ASSERT_TRUE(run_loop_->running());
+    run_loop_->Quit();
   }
 
   Widget* widget_;
-  base::RunLoop run_loop_;
+  std::unique_ptr<base::RunLoop> run_loop_;
   bool on_widget_destroying_ = false;
+  bool visible_ = false;
 
-  DISALLOW_COPY_AND_ASSIGN(WidgetDestroyingObserver);
+  DISALLOW_COPY_AND_ASSIGN(TestWidgetObserver);
 };
 
 std::unique_ptr<Widget> CreateWidgetWithNativeWidget() {
@@ -75,12 +114,29 @@
 TEST_F(DesktopWindowTreeHostPlatformTest, CallOnNativeWidgetDestroying) {
   std::unique_ptr<Widget> widget = CreateWidgetWithNativeWidget();
 
-  WidgetDestroyingObserver observer(
-      widget->native_widget_private()->GetWidget());
+  TestWidgetObserver observer(widget->native_widget_private()->GetWidget());
   widget->CloseNow();
 
-  observer.Wait();
+  observer.WaitForChange(TestWidgetObserver::Change::kDestroying,
+                         false /* old_value */);
   EXPECT_TRUE(observer.widget_destroying());
 }
 
+// Calling show/hide/show triggers changing visibility of the native widget.
+TEST_F(DesktopWindowTreeHostPlatformTest, CallOnNativeWidgetVisibilityChanged) {
+  std::unique_ptr<Widget> widget = CreateWidgetWithNativeWidget();
+
+  TestWidgetObserver observer(widget->native_widget_private()->GetWidget());
+  EXPECT_FALSE(observer.visible());
+
+  widget->Show();
+  EXPECT_TRUE(observer.visible());
+
+  widget->Hide();
+  EXPECT_FALSE(observer.visible());
+
+  widget->Show();
+  EXPECT_TRUE(observer.visible());
+}
+
 }  // namespace views
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
index 22af8463..f1295c8 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
@@ -248,81 +248,6 @@
   return base::WrapUnique(drag_drop_client_);
 }
 
-void DesktopWindowTreeHostX11::Show(ui::WindowShowState show_state,
-                                    const gfx::Rect& restore_bounds) {
-  if (compositor())
-    SetVisible(true);
-
-  if (!GetXWindow()->mapped_in_client() || IsMinimized())
-    MapWindow(show_state);
-
-  switch (show_state) {
-    case ui::SHOW_STATE_MAXIMIZED:
-      Maximize();
-      if (!restore_bounds.IsEmpty()) {
-        // Enforce |restored_bounds_in_pixels_| since calling Maximize() could
-        // have reset it.
-        restored_bounds_in_pixels_ = ToPixelRect(restore_bounds);
-      }
-      break;
-    case ui::SHOW_STATE_MINIMIZED:
-      Minimize();
-      break;
-    case ui::SHOW_STATE_FULLSCREEN:
-      SetFullscreen(true);
-      break;
-    default:
-      break;
-  }
-
-  native_widget_delegate()->AsWidget()->SetInitialFocus(show_state);
-
-  content_window()->Show();
-}
-
-void DesktopWindowTreeHostX11::SetSize(const gfx::Size& requested_size) {
-  gfx::Size size_in_pixels = ToPixelRect(gfx::Rect(requested_size)).size();
-  size_in_pixels = AdjustSizeForDisplay(size_in_pixels);
-
-  bool size_changed = GetBoundsInPixels().size() != size_in_pixels;
-
-  GetXWindow()->SetSize(size_in_pixels);
-
-  if (size_changed) {
-    OnHostResizedInPixels(size_in_pixels);
-    ResetWindowRegion();
-  }
-}
-
-void DesktopWindowTreeHostX11::GetWindowPlacement(
-    gfx::Rect* bounds,
-    ui::WindowShowState* show_state) const {
-  *bounds = GetRestoredBounds();
-
-  if (IsFullscreen()) {
-    *show_state = ui::SHOW_STATE_FULLSCREEN;
-  } else if (IsMinimized()) {
-    *show_state = ui::SHOW_STATE_MINIMIZED;
-  } else if (IsMaximized()) {
-    *show_state = ui::SHOW_STATE_MAXIMIZED;
-  } else if (!IsActive()) {
-    *show_state = ui::SHOW_STATE_INACTIVE;
-  } else {
-    *show_state = ui::SHOW_STATE_NORMAL;
-  }
-}
-
-gfx::Rect DesktopWindowTreeHostX11::GetRestoredBounds() const {
-  // We can't reliably track the restored bounds of a window, but we can get
-  // the 90% case down. When *chrome* is the process that requests maximizing
-  // or restoring bounds, we can record the current bounds before we request
-  // maximization, and clear it when we detect a state change.
-  if (!restored_bounds_in_pixels_.IsEmpty())
-    return ToDIPRect(restored_bounds_in_pixels_);
-
-  return GetWindowBoundsInScreen();
-}
-
 std::string DesktopWindowTreeHostX11::GetWorkspace() const {
   base::Optional<int> workspace = GetXWindow()->workspace();
   return workspace ? base::NumberToString(workspace.value()) : std::string();
@@ -357,68 +282,10 @@
   return GetXWindow()->IsActive();
 }
 
-void DesktopWindowTreeHostX11::Maximize() {
-  // TODO(nickdiego): Move into XWindow. For now, it is kept outside
-  // it due to |AdjustSizeForDisplay|, which depends on display::Display, which
-  // is not accessible at Ozone layer.
-  if (GetXWindow()->IsFullscreen()) {
-    // Unfullscreen the window if it is fullscreen.
-    GetXWindow()->SetFullscreen(false);
-
-    // Resize the window so that it does not have the same size as a monitor.
-    // (Otherwise, some window managers immediately put the window back in
-    // fullscreen mode).
-    gfx::Rect bounds = GetBoundsInPixels();
-    gfx::Rect adjusted_bounds_in_pixels(bounds.origin(),
-                                        AdjustSizeForDisplay(bounds.size()));
-    if (adjusted_bounds_in_pixels != bounds)
-      SetBoundsInPixels(adjusted_bounds_in_pixels);
-  }
-
-  // When we are in the process of requesting to maximize a window, we can
-  // accurately keep track of our restored bounds instead of relying on the
-  // heuristics that are in the PropertyNotify and ConfigureNotify handlers.
-  restored_bounds_in_pixels_ = GetBoundsInPixels();
-
-  GetXWindow()->Maximize();
-  if (IsMinimized())
-    Show(ui::SHOW_STATE_NORMAL, gfx::Rect());
-}
-
-void DesktopWindowTreeHostX11::Minimize() {
-  ReleaseCapture();
-  GetXWindow()->Minimize();
-}
-
-void DesktopWindowTreeHostX11::Restore() {
-  GetXWindow()->Unmaximize();
-  Show(ui::SHOW_STATE_NORMAL, gfx::Rect());
-  GetXWindow()->Unhide();
-}
-
-bool DesktopWindowTreeHostX11::IsMaximized() const {
-  return GetXWindow()->IsMaximized();
-}
-
-bool DesktopWindowTreeHostX11::IsMinimized() const {
-  return GetXWindow()->IsMinimized();
-}
-
 bool DesktopWindowTreeHostX11::HasCapture() const {
   return g_current_capture == this;
 }
 
-void DesktopWindowTreeHostX11::SetVisible(bool visible) {
-  if (is_compositor_set_visible_ == visible)
-    return;
-
-  is_compositor_set_visible_ = visible;
-  if (compositor())
-    compositor()->SetVisible(visible);
-
-  native_widget_delegate()->OnNativeWidgetVisibilityChanged(visible);
-}
-
 void DesktopWindowTreeHostX11::SetVisibleOnAllWorkspaces(bool always_visible) {
   GetXWindow()->SetVisibleOnAllWorkspaces(always_visible);
 }
@@ -474,60 +341,6 @@
                      weak_factory_.GetWeakPtr(), new_type));
 }
 
-void DesktopWindowTreeHostX11::SetFullscreen(bool fullscreen) {
-  if (is_fullscreen_ == fullscreen)
-    return;
-
-  is_fullscreen_ = fullscreen;
-  if (is_fullscreen_)
-    GetXWindow()->CancelResize();
-
-  // Work around a bug where if we try to unfullscreen, metacity immediately
-  // fullscreens us again. This is a little flickery and not necessary if
-  // there's a gnome-panel, but it's not easy to detect whether there's a
-  // panel or not.
-  bool unmaximize_and_remaximize = !fullscreen && IsMaximized() &&
-                                   ui::GuessWindowManager() == ui::WM_METACITY;
-
-  if (unmaximize_and_remaximize)
-    Restore();
-
-  GetXWindow()->SetFullscreen(fullscreen);
-
-  if (unmaximize_and_remaximize)
-    Maximize();
-
-  // Try to guess the size we will have after the switch to/from fullscreen:
-  // - (may) avoid transient states
-  // - works around Flash content which expects to have the size updated
-  //   synchronously.
-  // See https://crbug.com/361408
-  gfx::Rect bounds = GetXWindow()->bounds();
-  if (fullscreen) {
-    display::Screen* screen = display::Screen::GetScreen();
-    const display::Display display = screen->GetDisplayNearestWindow(window());
-    restored_bounds_in_pixels_ = bounds;
-    bounds = ToPixelRect(display.bounds());
-  } else {
-    bounds = restored_bounds_in_pixels_;
-  }
-  GetXWindow()->set_bounds(bounds);
-
-  OnHostMovedInPixels(bounds.origin());
-  OnHostResizedInPixels(bounds.size());
-
-  if (GetXWindow()->IsFullscreen() == fullscreen) {
-    Relayout();
-    ResetWindowRegion();
-  }
-  // Else: the widget will be relaid out either when the window bounds change or
-  // when |xwindow_|'s fullscreen state changes.
-}
-
-bool DesktopWindowTreeHostX11::IsFullscreen() const {
-  return is_fullscreen_;
-}
-
 void DesktopWindowTreeHostX11::SetOpacity(float opacity) {
   GetXWindow()->SetOpacity(opacity);
 }
@@ -583,33 +396,6 @@
 ////////////////////////////////////////////////////////////////////////////////
 // DesktopWindowTreeHostX11, aura::WindowTreeHost implementatio
 
-void DesktopWindowTreeHostX11::ShowImpl() {
-  Show(ui::SHOW_STATE_NORMAL, gfx::Rect());
-}
-
-void DesktopWindowTreeHostX11::HideImpl() {
-  if (GetXWindow()->Hide())
-    SetVisible(false);
-}
-
-void DesktopWindowTreeHostX11::SetBoundsInPixels(
-    const gfx::Rect& requested_bounds_in_pixel) {
-  gfx::Rect bounds = GetXWindow()->bounds();
-  gfx::Rect bounds_in_pixels(
-      requested_bounds_in_pixel.origin(),
-      AdjustSizeForDisplay(requested_bounds_in_pixel.size()));
-
-  bool size_changed = bounds.size() != bounds_in_pixels.size();
-
-  if (size_changed) {
-    // Only cancel the delayed resize task if we're already about to call
-    // OnHostResized in this function.
-    GetXWindow()->CancelResize();
-  }
-
-  platform_window()->SetBounds(bounds_in_pixels);
-}
-
 void DesktopWindowTreeHostX11::SetCapture() {
   if (HasCapture())
     return;
@@ -742,33 +528,6 @@
   return *open_windows_;
 }
 
-void DesktopWindowTreeHostX11::MapWindow(ui::WindowShowState show_state) {
-  if (show_state != ui::SHOW_STATE_DEFAULT &&
-      show_state != ui::SHOW_STATE_NORMAL &&
-      show_state != ui::SHOW_STATE_INACTIVE &&
-      show_state != ui::SHOW_STATE_MAXIMIZED) {
-    // It will behave like SHOW_STATE_NORMAL.
-    NOTIMPLEMENTED_LOG_ONCE();
-  }
-
-  // If SHOW_STATE_INACTIVE, tell the window manager not to focus the window
-  // when mapping. This is done by setting the _NET_WM_USER_TIME to 0. See e.g.
-  // http://standards.freedesktop.org/wm-spec/latest/ar01s05.html
-  bool inactive = show_state == ui::SHOW_STATE_INACTIVE;
-
-  GetXWindow()->Map(inactive);
-}
-
-void DesktopWindowTreeHostX11::Relayout() {
-  Widget* widget = native_widget_delegate()->AsWidget();
-  NonClientView* non_client_view = widget->non_client_view();
-  // non_client_view may be NULL, especially during creation.
-  if (non_client_view) {
-    non_client_view->client_view()->InvalidateLayout();
-    non_client_view->InvalidateLayout();
-  }
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // DesktopWindowTreeHostX11 implementation:
 
@@ -835,50 +594,7 @@
 
 void DesktopWindowTreeHostX11::OnWindowStateChanged(
     ui::PlatformWindowState new_state) {
-  bool was_minimized = GetXWindow()->was_minimized();
-  bool is_minimized = IsMinimized();
-
-  // Propagate the window minimization information to the content window, so
-  // the render side can update its visibility properly. OnWMStateUpdated() is
-  // called by PropertyNofify event from DispatchEvent() when the browser is
-  // minimized or shown from minimized state. On Windows, this is realized by
-  // calling OnHostResizedInPixels() with an empty size. In particular,
-  // HWNDMessageHandler::GetClientAreaBounds() returns an empty size when the
-  // window is minimized. On Linux, returning empty size in GetBounds() or
-  // SetBoundsInPixels() does not work.
-  // We also propagate the minimization to the compositor, to makes sure that we
-  // don't draw any 'blank' frames that could be noticed in applications such as
-  // window manager previews, which show content even when a window is
-  // minimized.
-  if (is_minimized != was_minimized) {
-    if (is_minimized) {
-      SetVisible(false);
-      content_window()->Hide();
-    } else {
-      content_window()->Show();
-      SetVisible(true);
-    }
-  }
-
-  if (restored_bounds_in_pixels_.IsEmpty()) {
-    if (IsMaximized()) {
-      // The request that we become maximized originated from a different
-      // process. |bounds_in_pixels_| already contains our maximized bounds. Do
-      // a best effort attempt to get restored bounds by setting it to our
-      // previously set bounds (and if we get this wrong, we aren't any worse
-      // off since we'd otherwise be returning our maximized bounds).
-      restored_bounds_in_pixels_ = GetXWindow()->previous_bounds();
-    }
-  } else if (!IsMaximized() && !IsFullscreen()) {
-    // If we have restored bounds, but WM_STATE no longer claims to be
-    // maximized or fullscreen, we should clear our restored bounds.
-    restored_bounds_in_pixels_ = gfx::Rect();
-  }
-
-  // Now that we have different window properties, we may need to relayout the
-  // window. (The windows code doesn't need this because their window change is
-  // synchronous.)
-  Relayout();
+  DesktopWindowTreeHostPlatform::OnWindowStateChanged(new_state);
   ResetWindowRegion();
 }
 
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
index ffc520e..cc1dd17 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
@@ -94,20 +94,9 @@
   void OnNativeWidgetCreated(const Widget::InitParams& params) override;
   std::unique_ptr<aura::client::DragDropClient> CreateDragDropClient(
       DesktopNativeCursorManager* cursor_manager) override;
-  void Show(ui::WindowShowState show_state,
-            const gfx::Rect& restore_bounds) override;
-  void SetSize(const gfx::Size& requested_size) override;
-  void GetWindowPlacement(gfx::Rect* bounds,
-                          ui::WindowShowState* show_state) const override;
-  gfx::Rect GetRestoredBounds() const override;
   std::string GetWorkspace() const override;
   void SetShape(std::unique_ptr<Widget::ShapeRects> native_shape) override;
   bool IsActive() const override;
-  void Maximize() override;
-  void Minimize() override;
-  void Restore() override;
-  bool IsMaximized() const override;
-  bool IsMinimized() const override;
   bool HasCapture() const override;
   void SetVisibleOnAllWorkspaces(bool always_visible) override;
   bool IsVisibleOnAllWorkspaces() const override;
@@ -119,8 +108,6 @@
   void SetVisibilityChangedAnimationsEnabled(bool value) override;
   bool ShouldUseNativeFrame() const override;
   void FrameTypeChanged() override;
-  void SetFullscreen(bool fullscreen) override;
-  bool IsFullscreen() const override;
   void SetOpacity(float opacity) override;
   void SetAspectRatio(const gfx::SizeF& aspect_ratio) override;
   void SetWindowIcons(const gfx::ImageSkia& window_icon,
@@ -134,19 +121,12 @@
   bool ShouldCreateVisibilityController() const override;
 
   // Overridden from aura::WindowTreeHost:
-  void ShowImpl() override;
-  void HideImpl() override;
-  void SetBoundsInPixels(const gfx::Rect& requested_bounds_in_pixels) override;
   void SetCapture() override;
   void ReleaseCapture() override;
 
  private:
   friend class DesktopWindowTreeHostX11HighDPITest;
 
-  // Initializes our X11 surface to draw on. This method performs all
-  // initialization related to talking to the X11 server.
-  void InitX11Window(const Widget::InitParams& params);
-
   // Sets whether the window's borders are provided by the window manager.
   void SetUseNativeFrame(bool use_native_frame);
 
@@ -169,20 +149,12 @@
   // See comment for variable open_windows_.
   static std::list<XID>& open_windows();
 
-  // Map the window (shows it) taking into account the given |show_state|.
-  void MapWindow(ui::WindowShowState show_state);
-
-  // Relayout the widget's client and non-client views.
-  void Relayout();
 
   void DelayedChangeFrameType(Widget::FrameType new_type);
 
   // Enables event listening after closing |dialog|.
   void EnableEventListening();
 
-  // Set visibility and fire OnNativeWidgetVisibilityChanged() if it changed.
-  void SetVisible(bool visible);
-
   // Callback for a swapbuffer after resize.
   void OnCompleteSwapWithNewSize(const gfx::Size& size);
 
@@ -215,12 +187,6 @@
   ui::XWindow* GetXWindow();
   const ui::XWindow* GetXWindow() const;
 
-  // The bounds of our window before we were maximized.
-  gfx::Rect restored_bounds_in_pixels_;
-
-  // Whether |xwindow_| was requested to be fullscreen via SetFullscreen().
-  bool is_fullscreen_ = false;
-
   DesktopDragDropClientAuraX11* drag_drop_client_ = nullptr;
 
   std::unique_ptr<X11DesktopWindowMoveClient> x11_window_move_client_;
@@ -236,9 +202,6 @@
   // destroyed.
   static std::list<gfx::AcceleratedWidget>* open_windows_;
 
-  // Cached value for SetVisible.  Not the same as the IsVisible public API.
-  bool is_compositor_set_visible_ = false;
-
   std::unique_ptr<aura::ScopedWindowTargeter> targeter_for_modal_;
 
   uint32_t modal_dialog_counter_ = 0;
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc
index 1ea1d04e..543ff68 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc
@@ -101,7 +101,17 @@
   void UpdateWindowTitle() override {}
   void SizeConstraintsChanged() override {}
 
+  bool GetAndResetLayoutRequest() {
+    bool layout_requested = layout_requested_;
+    layout_requested_ = false;
+    return layout_requested;
+  }
+
  private:
+  void Layout() override { layout_requested_ = true; }
+
+  bool layout_requested_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(ShapedNonClientFrameView);
 };
 
@@ -258,12 +268,10 @@
 
     shape_rects = GetShapeRects(xid1);
     ASSERT_FALSE(shape_rects.empty());
-    EXPECT_TRUE(ShapeRectContainsPoint(shape_rects,
-                                       maximized_bounds.width() - 1,
-                                       5));
-    EXPECT_TRUE(ShapeRectContainsPoint(shape_rects,
-                                       maximized_bounds.width() - 1,
-                                       15));
+    EXPECT_TRUE(
+        ShapeRectContainsPoint(shape_rects, maximized_bounds.width() - 1, 5));
+    EXPECT_TRUE(
+        ShapeRectContainsPoint(shape_rects, maximized_bounds.width() - 1, 15));
   }
 
   // 2) Test setting the window shape via Widget::SetShape().
@@ -306,13 +314,16 @@
   EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 500, 500));
 }
 
-// Test that the widget ignores changes in fullscreen state initiated by the
+// Test that the widget reacts on changes in fullscreen state initiated by the
 // window manager (e.g. via a window manager accelerator key).
 TEST_F(DesktopWindowTreeHostX11Test, WindowManagerTogglesFullscreen) {
   if (!ui::WmSupportsHint(gfx::GetAtom("_NET_WM_STATE_FULLSCREEN")))
     return;
 
   std::unique_ptr<Widget> widget = CreateWidget(new ShapedWidgetDelegate());
+  auto* non_client_view = static_cast<ShapedNonClientFrameView*>(
+      widget->non_client_view()->frame_view());
+  ASSERT_TRUE(non_client_view);
   XID xid = widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
   widget->Show();
   ui::X11EventSource::GetInstance()->DispatchXEvents();
@@ -325,8 +336,14 @@
   }
   EXPECT_TRUE(widget->IsFullscreen());
 
+  // After the fullscreen state has been set, there must be a relayout request
+  EXPECT_TRUE(non_client_view->GetAndResetLayoutRequest());
+
+  // Ensure there is not request before we proceed.
+  EXPECT_FALSE(non_client_view->GetAndResetLayoutRequest());
+
   // Emulate the window manager exiting fullscreen via a window manager
-  // accelerator key. It should not affect the widget's fullscreen state.
+  // accelerator key. It should affect the widget's fullscreen state.
   {
     Display* display = gfx::GetXDisplay();
 
@@ -347,14 +364,13 @@
     WMStateWaiter waiter(xid, "_NET_WM_STATE_FULLSCREEN", false);
     waiter.Wait();
   }
-  EXPECT_TRUE(widget->IsFullscreen());
-
-  // Calling Widget::SetFullscreen(false) should clear the widget's fullscreen
-  // state and clean things up.
-  widget->SetFullscreen(false);
   EXPECT_FALSE(widget->IsFullscreen());
   EXPECT_EQ(initial_bounds.ToString(),
             widget->GetWindowBoundsInScreen().ToString());
+
+  // Even though the unfullscreen request came from the window manager, we must
+  // still react and relayout.
+  EXPECT_TRUE(non_client_view->GetAndResetLayoutRequest());
 }
 
 // Tests that the minimization information is propagated to the content window.
@@ -371,7 +387,7 @@
 
   // Minimize by sending _NET_WM_STATE_HIDDEN
   {
-    std::vector< ::Atom> atom_list;
+    std::vector<::Atom> atom_list;
     atom_list.push_back(gfx::GetAtom("_NET_WM_STATE_HIDDEN"));
     ui::SetAtomArrayProperty(xid, "_NET_WM_STATE", "ATOM", atom_list);
 
@@ -394,7 +410,7 @@
 
   // Show from minimized by sending _NET_WM_STATE_FOCUSED
   {
-    std::vector< ::Atom> atom_list;
+    std::vector<::Atom> atom_list;
     atom_list.push_back(gfx::GetAtom("_NET_WM_STATE_FOCUSED"));
     ui::SetAtomArrayProperty(xid, "_NET_WM_STATE", "ATOM", atom_list);
 
diff --git a/ui/views/widget/widget_hwnd_utils.cc b/ui/views/widget/widget_hwnd_utils.cc
index 0cccf60..9edfa2c 100644
--- a/ui/views/widget/widget_hwnd_utils.cc
+++ b/ui/views/widget/widget_hwnd_utils.cc
@@ -9,7 +9,6 @@
 #include "base/command_line.h"
 #include "build/build_config.h"
 #include "ui/base/l10n/l10n_util_win.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/views/widget/widget_delegate.h"
 #include "ui/views/win/hwnd_message_handler.h"
@@ -109,15 +108,6 @@
       break;
     case Widget::InitParams::TYPE_MENU:
       *style |= WS_POPUP;
-      if (features::IsFormControlsRefreshEnabled() &&
-          params.remove_standard_frame) {
-        // If the platform doesn't support drop shadow, decorate the Window
-        // with just a border.
-        if (ui::win::IsAeroGlassEnabled())
-          *style |= WS_THICKFRAME;
-        else
-          *style |= WS_BORDER;
-      }
       break;
     case Widget::InitParams::TYPE_TOOLTIP:
       *style |= WS_POPUP;
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc
index ad2cd7a..e5be3e3 100644
--- a/ui/views/widget/widget_unittest.cc
+++ b/ui/views/widget/widget_unittest.cc
@@ -1138,14 +1138,7 @@
   EXPECT_EQ(minimum_size, widget->GetClientAreaBoundsInScreen().size());
 
   widget->SetSize(smaller_size);
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-  // TODO(tapted): Desktop Linux ignores size constraints for SetSize. Fix it.
-  const bool use_small_size = true;
-#else
-  const bool use_small_size = false;
-#endif
-  EXPECT_EQ(use_small_size ? smaller_size : minimum_size,
-            widget->GetClientAreaBoundsInScreen().size());
+  EXPECT_EQ(minimum_size, widget->GetClientAreaBoundsInScreen().size());
 }
 
 // Tests that SetBounds() and GetWindowBoundsInScreen() is symmetric when the
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.js b/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.js
index 1682e5bb..f24bf2d9 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.js
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.js
@@ -134,9 +134,8 @@
     }
     if (this.networkState.type ==
         chromeos.networkConfig.mojom.NetworkType.kCellular) {
-      assert(this.networkState.cellular);
-      const technology =
-          this.getTechnologyId_(this.networkState.cellular.networkTechnology);
+      const technology = this.getTechnologyId_(
+          this.networkState.typeState.cellular.networkTechnology);
       if (technology != '') {
         return 'network:' + technology;
       }
@@ -189,6 +188,6 @@
       return false;
     }
     return this.networkState.type == mojom.NetworkType.kWiFi &&
-        this.networkState.wifi.security != mojom.SecurityType.kNone;
+        this.networkState.typeState.wifi.security != mojom.SecurityType.kNone;
   },
 });
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.js b/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.js
index ab22698a..31def06 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.js
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.js
@@ -140,10 +140,10 @@
     }
     const connectionState = this.networkState.connectionState;
     if (this.networkState.type == mojom.NetworkType.kCellular) {
-      if (this.networkState.cellular.scanning) {
+      if (this.networkState.typeState.cellular.scanning) {
         return CrOncStrings.networkListItemScanning;
       }
-      if (this.networkState.cellular.simLocked) {
+      if (this.networkState.typeState.cellular.simLocked) {
         return CrOncStrings.networkListItemSimCardLocked;
       }
     }
diff --git a/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js b/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js
index e8041a2a..d7bfc1b 100644
--- a/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js
+++ b/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js
@@ -242,7 +242,7 @@
 
     const cellular =
         OncMojo.getDefaultNetworkState(mojom.NetworkType.kCellular);
-    cellular.cellular.scanning = this.cellularDeviceState_.scanning;
+    cellular.typeState.cellular.scanning = this.cellularDeviceState_.scanning;
 
     // Note: the default connectionState is kNotConnected.
     // TODO(khorimoto): Maybe set an 'initializing' CellularState property if
diff --git a/ui/webui/resources/html/test_loader.html b/ui/webui/resources/html/test_loader.html
new file mode 100644
index 0000000..4aeb21f
--- /dev/null
+++ b/ui/webui/resources/html/test_loader.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<body>
+  <script src="test_loader.js"></script>
+</body>
+</html>
+
diff --git a/ui/webui/resources/js/chromeos/onc_mojo.js b/ui/webui/resources/js/chromeos/onc_mojo.js
index 6bee085..74ce13a 100644
--- a/ui/webui/resources/js/chromeos/onc_mojo.js
+++ b/ui/webui/resources/js/chromeos/onc_mojo.js
@@ -464,8 +464,10 @@
       return OncMojo.getNetworkTypeDisplayName(network.type);
     }
     const mojom = chromeos.networkConfig.mojom;
-    if (network.type == mojom.NetworkType.kVPN && network.vpn.providerName) {
-      return OncMojo.getVpnDisplayName(network.name, network.vpn.providerName);
+    if (network.type == mojom.NetworkType.kVPN &&
+        network.typeState.vpn.providerName) {
+      return OncMojo.getVpnDisplayName(
+          network.name, network.typeState.vpn.providerName);
     }
     return network.name;
   }
@@ -495,11 +497,11 @@
     const NetworkType = chromeos.networkConfig.mojom.NetworkType;
     switch (network.type) {
       case NetworkType.kCellular:
-        return network.cellular.signalStrength;
+        return network.typeState.cellular.signalStrength;
       case NetworkType.kTether:
-        return network.tether.signalStrength;
+        return network.typeState.tether.signalStrength;
       case NetworkType.kWiFi:
-        return network.wifi.signalStrength;
+        return network.typeState.wifi.signalStrength;
     }
     assertNotReached();
     return 0;
@@ -524,10 +526,11 @@
       prohibitedByPolicy: false,
       source: mojom.OncSource.kNone,
       type: type,
+      typeState: {},
     };
     switch (type) {
       case mojom.NetworkType.kCellular:
-        result.cellular = {
+        result.typeState.cellular = {
           activationState: mojom.ActivationStateType.kUnknown,
           networkTechnology: '',
           roaming: false,
@@ -536,12 +539,12 @@
         };
         break;
       case mojom.NetworkType.kEthernet:
-        result.ethernet = {
+        result.typeState.ethernet = {
           authentication: mojom.AuthenticationType.kNone,
         };
         break;
       case mojom.NetworkType.kTether:
-        result.tether = {
+        result.typeState.tether = {
           batteryPercentage: 0,
           carrier: '',
           hasConnectedToHost: false,
@@ -549,14 +552,14 @@
         };
         break;
       case mojom.NetworkType.kVPN:
-        result.vpn = {
+        result.typeState.vpn = {
           type: mojom.VpnType.kOpenVPN,
           providerId: '',
           providerName: '',
         };
         break;
       case mojom.NetworkType.kWiFi:
-        result.wifi = {
+        result.typeState.wifi = {
           bssid: '',
           frequency: 0,
           hexSsid: opt_name || '',
@@ -565,6 +568,8 @@
           ssid: '',
         };
         break;
+      default:
+        assertNotReached();
     }
     return result;
   }
@@ -591,39 +596,42 @@
 
     switch (properties.type) {
       case mojom.NetworkType.kCellular:
-        networkState.cellular.activationState =
+        networkState.typeState.cellular.activationState =
             properties.cellular.activationState;
-        networkState.cellular.networkTechnology =
+        networkState.typeState.cellular.networkTechnology =
             properties.cellular.networkTechnology || '';
-        networkState.cellular.roaming =
+        networkState.typeState.cellular.roaming =
             properties.cellular.roamingState == 'Roaming';
-        networkState.cellular.signalStrength =
+        networkState.typeState.cellular.signalStrength =
             properties.cellular.signalStrength;
         break;
       case mojom.NetworkType.kEthernet:
-        networkState.ethernet.authentication =
+        networkState.typeState.ethernet.authentication =
             properties.ethernet.authentication == '8021X' ?
             mojom.AuthenticationType.k8021x :
             mojom.AuthenticationType.kNone;
         break;
       case mojom.NetworkType.kTether:
         if (properties.tether) {
-          networkState.tether = /** @type {!mojom.TetherStateProperties}*/ (
-              Object.assign({}, properties.tether));
+          networkState.typeState.tether =
+              /** @type {!mojom.TetherStateProperties}*/ (
+                  Object.assign({}, properties.tether));
         }
         break;
       case mojom.NetworkType.kVPN:
-        networkState.vpn.providerName = properties.vpn.providerName;
-        networkState.vpn.vpnType = properties.vpn.type;
+        networkState.typeState.vpn.providerName = properties.vpn.providerName;
+        networkState.typeState.vpn.vpnType = properties.vpn.type;
         break;
       case mojom.NetworkType.kWiFi:
-        networkState.wifi.bssid = properties.wifi.bssid || '';
-        networkState.wifi.frequency = properties.wifi.frequency;
-        networkState.wifi.hexSsid =
+        networkState.typeState.wifi.bssid = properties.wifi.bssid || '';
+        networkState.typeState.wifi.frequency = properties.wifi.frequency;
+        networkState.typeState.wifi.hexSsid =
             OncMojo.getActiveString(properties.wifi.hexSsid);
-        networkState.wifi.security = properties.wifi.security;
-        networkState.wifi.signalStrength = properties.wifi.signalStrength;
-        networkState.wifi.ssid = OncMojo.getActiveString(properties.wifi.ssid);
+        networkState.typeState.wifi.security = properties.wifi.security;
+        networkState.typeState.wifi.signalStrength =
+            properties.wifi.signalStrength;
+        networkState.typeState.wifi.ssid =
+            OncMojo.getActiveString(properties.wifi.ssid);
         break;
     }
     return networkState;
diff --git a/ui/webui/resources/js/cr.js b/ui/webui/resources/js/cr.js
index 9a5e710..7fc7c2f 100644
--- a/ui/webui/resources/js/cr.js
+++ b/ui/webui/resources/js/cr.js
@@ -433,10 +433,6 @@
     webUIListenerCallback: webUIListenerCallback,
     webUIResponse: webUIResponse,
 
-    get doc() {
-      return document;
-    },
-
     /** Whether we are using a Mac or not. */
     get isMac() {
       return /Mac/.test(navigator.platform);
diff --git a/ui/webui/resources/js/cr/ui.js b/ui/webui/resources/js/cr/ui.js
index 2340aec..450de58 100644
--- a/ui/webui/resources/js/cr/ui.js
+++ b/ui/webui/resources/js/cr/ui.js
@@ -15,7 +15,7 @@
   function decorate(source, constr) {
     let elements;
     if (typeof source == 'string') {
-      elements = cr.doc.querySelectorAll(source);
+      elements = document.querySelectorAll(source);
     } else {
       elements = [source];
     }
@@ -36,7 +36,7 @@
     if (opt_bag && opt_bag.ownerDocument) {
       doc = opt_bag.ownerDocument;
     } else {
-      doc = cr.doc;
+      doc = document;
     }
     return doc.createElement(tagName);
   }
diff --git a/ui/webui/resources/js/cr/ui/grid.js b/ui/webui/resources/js/cr/ui/grid.js
index ab5676f..8752b5e 100644
--- a/ui/webui/resources/js/cr/ui/grid.js
+++ b/ui/webui/resources/js/cr/ui/grid.js
@@ -24,7 +24,7 @@
    * @extends {cr.ui.ListItem}
    */
   function GridItem(dataItem) {
-    const el = cr.doc.createElement('li');
+    const el = document.createElement('li');
     el.dataItem = dataItem;
     el.__proto__ = GridItem.prototype;
     return el;
diff --git a/ui/webui/resources/js/cr/ui/menu.js b/ui/webui/resources/js/cr/ui/menu.js
index eb60f61..eb367f2 100644
--- a/ui/webui/resources/js/cr/ui/menu.js
+++ b/ui/webui/resources/js/cr/ui/menu.js
@@ -299,7 +299,7 @@
             // Store |contextElement| since it'll be removed when handling the
             // 'activate' event.
             const contextElement = this.contextElement;
-            const activationEvent = cr.doc.createEvent('Event');
+            const activationEvent = document.createEvent('Event');
             activationEvent.initEvent('activate', true, true);
             activationEvent.originalEvent = e;
             if (item.dispatchEvent(activationEvent)) {
diff --git a/ui/webui/resources/js/cr/ui/menu_item.js b/ui/webui/resources/js/cr/ui/menu_item.js
index ab2cc4b..fe32ef6 100644
--- a/ui/webui/resources/js/cr/ui/menu_item.js
+++ b/ui/webui/resources/js/cr/ui/menu_item.js
@@ -16,10 +16,10 @@
 
   /**
    * Creates a new menu separator element.
-   * @return {cr.ui.MenuItem} The new separator element.
+   * @return {!cr.ui.MenuItem} The new separator element.
    */
   MenuItem.createSeparator = function() {
-    const el = cr.doc.createElement('hr');
+    const el = /** @type {!cr.ui.MenuItem} */ (document.createElement('hr'));
     MenuItem.decorate(el);
     return el;
   };
@@ -206,7 +206,7 @@
         const contextElement =
             /** @type {{contextElement: Element}} */ (this.parentNode)
                 .contextElement;
-        const activationEvent = cr.doc.createEvent('Event');
+        const activationEvent = document.createEvent('Event');
         activationEvent.initEvent('activate', true, true);
         activationEvent.originalEvent = e;
         // Dispatch command event followed by executing the command object.
diff --git a/ui/webui/resources/js/cr/ui/tree.js b/ui/webui/resources/js/cr/ui/tree.js
index 54455ae7..bc115f0 100644
--- a/ui/webui/resources/js/cr/ui/tree.js
+++ b/ui/webui/resources/js/cr/ui/tree.js
@@ -265,7 +265,7 @@
    * @type {!HTMLElement}
    */
   const treeItemProto = (function() {
-    const treeItem = cr.doc.createElement('div');
+    const treeItem = document.createElement('div');
     treeItem.className = 'tree-item';
     treeItem.innerHTML = '<div class="tree-row">' +
         '<span class="expand-icon"></span>' +
diff --git a/ui/webui/resources/js/test_loader.js b/ui/webui/resources/js/test_loader.js
new file mode 100644
index 0000000..dd40a214
--- /dev/null
+++ b/ui/webui/resources/js/test_loader.js
@@ -0,0 +1,25 @@
+// Copyright 2019 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.
+
+// @fileoverview Helper script used to load a JS module test into a WebUI page.
+//
+// Example usage:
+//   chrome://welcome/test_loader.html?module=my_test.js
+//
+// will load the JS module at chrome/test/data/webui/my_test.js
+//
+// test_loader.html and test_loader.js should be registered with the
+// WebUIDataSource corresponding to the WebUI being tested, such that the
+// testing code is loaded from the same origin. Also note that the
+// chrome://test/ data source only exists in a testing context, so using this
+// script in production will result in a failed network request.
+
+(function() {
+const params = new URLSearchParams(window.location.search);
+const module = params.get('module');
+const script = document.createElement('script');
+script.type = 'module';
+script.src = `chrome://test/${module}`;
+document.body.appendChild(script);
+})();
diff --git a/ui/webui/resources/webui_resources.grd b/ui/webui/resources/webui_resources.grd
index c34f376..3a64bd4 100644
--- a/ui/webui/resources/webui_resources.grd
+++ b/ui/webui/resources/webui_resources.grd
@@ -291,6 +291,9 @@
       <structure name="IDR_WEBUI_HTML_PARSE_HTML_SUBSET"
                  file="html/parse_html_subset.html" type="chrome_html"
                  compress="gzip" />
+      <structure name="IDR_WEBUI_HTML_TEST_LOADER"
+                 file="html/test_loader.html" type="chrome_html"
+                 compress="gzip"/>
       <structure name="IDR_WEBUI_HTML_UTIL"
                  file="html/util.html" type="chrome_html" compress="gzip" />
 
@@ -428,6 +431,9 @@
       <structure name="IDR_WEBUI_JS_PARSE_HTML_SUBSET"
                  file="js/parse_html_subset.js" type="chrome_html"
                  compress="gzip" />
+      <structure name="IDR_WEBUI_JS_TEST_LOADER"
+                 file="js/test_loader.js" type="chrome_html"
+                 compress="gzip"/>
       <structure name="IDR_WEBUI_JS_UTIL"
                  file="js/util.js" type="chrome_html" compress="gzip"
                  flattenhtml="true" />
diff --git a/weblayer/browser/browser_controller_impl.cc b/weblayer/browser/browser_controller_impl.cc
index cf72f57..05613da 100644
--- a/weblayer/browser/browser_controller_impl.cc
+++ b/weblayer/browser/browser_controller_impl.cc
@@ -107,7 +107,7 @@
 void BrowserControllerImpl::DidNavigateMainFramePostCommit(
     content::WebContents* web_contents) {
   for (auto& observer : observers_)
-    observer.DisplayedURLChanged(web_contents->GetVisibleURL());
+    observer.DisplayedUrlChanged(web_contents->GetVisibleURL());
 }
 
 int BrowserControllerImpl::GetTopControlsHeight() {
diff --git a/weblayer/browser/browser_observer_proxy.cc b/weblayer/browser/browser_observer_proxy.cc
index 5240bd2..25bc9b06 100644
--- a/weblayer/browser/browser_observer_proxy.cc
+++ b/weblayer/browser/browser_observer_proxy.cc
@@ -27,11 +27,11 @@
   browser_controller_->RemoveObserver(this);
 }
 
-void BrowserObserverProxy::DisplayedURLChanged(const GURL& url) {
+void BrowserObserverProxy::DisplayedUrlChanged(const GURL& url) {
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jstring> jstring_url(
       ConvertUTF8ToJavaString(env, url.spec()));
-  Java_BrowserObserverProxy_displayURLChanged(env, java_observer_, jstring_url);
+  Java_BrowserObserverProxy_visibleUrlChanged(env, java_observer_, jstring_url);
 }
 
 void BrowserObserverProxy::LoadingStateChanged(bool is_loading,
diff --git a/weblayer/browser/browser_observer_proxy.h b/weblayer/browser/browser_observer_proxy.h
index 81882881..7713ede9 100644
--- a/weblayer/browser/browser_observer_proxy.h
+++ b/weblayer/browser/browser_observer_proxy.h
@@ -25,7 +25,7 @@
   ~BrowserObserverProxy() override;
 
   // BrowserObserver:
-  void DisplayedURLChanged(const GURL& url) override;
+  void DisplayedUrlChanged(const GURL& url) override;
   void LoadingStateChanged(bool is_loading,
                            bool to_different_document) override;
   void LoadProgressChanged(double progress) override;
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserObserverProxy.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserObserverProxy.java
index 10c75bc..95e37f9 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserObserverProxy.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserObserverProxy.java
@@ -34,9 +34,9 @@
     }
 
     @CalledByNative
-    private void displayURLChanged(String string) {
+    private void visibleUrlChanged(String string) {
         try {
-            mClient.displayURLChanged(string);
+            mClient.visibleUrlChanged(string);
         } catch (RemoteException e) {
             throw new APICallException(e);
         }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserControllerClient.aidl b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserControllerClient.aidl
index 0aaf83c..a17e4b4 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserControllerClient.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserControllerClient.aidl
@@ -9,7 +9,7 @@
  *  BrowserObserver interface, but is a singleton to avoid unnecessary IPC.
  */
 interface IBrowserControllerClient {
-  void displayURLChanged(in String url) = 0;
+  void visibleUrlChanged(in String url) = 0;
   void loadingStateChanged(boolean is_loading, boolean to_different_document) = 1;
   void loadProgressChanged(double progress) = 2;
 }
diff --git a/weblayer/public/browser_observer.h b/weblayer/public/browser_observer.h
index 3fe3244..d6b2440d 100644
--- a/weblayer/public/browser_observer.h
+++ b/weblayer/public/browser_observer.h
@@ -14,7 +14,7 @@
   virtual ~BrowserObserver() {}
 
   // The URL bar should be updated to |url|.
-  virtual void DisplayedURLChanged(const GURL& url) {}
+  virtual void DisplayedUrlChanged(const GURL& url) {}
 
   // Indicates that loading has started (|is_loading| is true) or is done
   // (|is_loading| is false). |to_different_document| will be true unless the
diff --git a/weblayer/public/java/org/chromium/weblayer/BrowserController.java b/weblayer/public/java/org/chromium/weblayer/BrowserController.java
index 8d512a40..d9c0bde 100644
--- a/weblayer/public/java/org/chromium/weblayer/BrowserController.java
+++ b/weblayer/public/java/org/chromium/weblayer/BrowserController.java
@@ -51,10 +51,10 @@
 
     private final class BrowserClientImpl extends IBrowserControllerClient.Stub {
         @Override
-        public void displayURLChanged(String url) {
+        public void visibleUrlChanged(String url) {
             Uri uri = Uri.parse(url);
             for (BrowserObserver observer : mObservers) {
-                observer.displayURLChanged(uri);
+                observer.visibleUrlChanged(uri);
             }
         }
 
diff --git a/weblayer/public/java/org/chromium/weblayer/BrowserObserver.java b/weblayer/public/java/org/chromium/weblayer/BrowserObserver.java
index 3075668..3bf1015 100644
--- a/weblayer/public/java/org/chromium/weblayer/BrowserObserver.java
+++ b/weblayer/public/java/org/chromium/weblayer/BrowserObserver.java
@@ -15,14 +15,14 @@
      *
      * @param url The new user-visible url.
      */
-    public void displayURLChanged(Uri url) {}
+    public void visibleUrlChanged(Uri url) {}
 
     /**
      * The load state of the document has changed.
      *
      * @param isLoading Whether any resource is loading.
      * @param toDifferentDocument True if the main frame is loading a different document. Only valid
-     *         when |isLoading| is true.
+     *        when |isLoading| is true.
      */
     public void loadingStateChanged(boolean isLoading, boolean toDifferentDocument) {}
 
diff --git a/weblayer/shell/android/BUILD.gn b/weblayer/shell/android/BUILD.gn
index d718600c..ef019fd 100644
--- a/weblayer/shell/android/BUILD.gn
+++ b/weblayer/shell/android/BUILD.gn
@@ -250,6 +250,7 @@
     "//third_party/android_support_test_runner:runner_java",
   ]
   java_files = [
+    "javatests/src/org/chromium/weblayer/test/BrowserObserverTest.java",
     "javatests/src/org/chromium/weblayer/test/NavigationTest.java",
     "javatests/src/org/chromium/weblayer/test/SmokeTest.java",
     "javatests/src/org/chromium/weblayer/test/RenderingTest.java",
diff --git a/weblayer/shell/android/demo_apk/src/org/chromium/weblayer/demo/WebLayerAnimationDemoActivity.java b/weblayer/shell/android/demo_apk/src/org/chromium/weblayer/demo/WebLayerAnimationDemoActivity.java
index a44f50f..5ee3fdf 100644
--- a/weblayer/shell/android/demo_apk/src/org/chromium/weblayer/demo/WebLayerAnimationDemoActivity.java
+++ b/weblayer/shell/android/demo_apk/src/org/chromium/weblayer/demo/WebLayerAnimationDemoActivity.java
@@ -155,7 +155,7 @@
 
         controller.addObserver(new BrowserObserver() {
             @Override
-            public void displayURLChanged(Uri uri) {
+            public void visibleUrlChanged(Uri uri) {
                 urlView.setText(uri.toString());
             }
         });
diff --git a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/BrowserObserverTest.java b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/BrowserObserverTest.java
new file mode 100644
index 0000000..17546a4
--- /dev/null
+++ b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/BrowserObserverTest.java
@@ -0,0 +1,131 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer.test;
+
+import android.net.Uri;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.content_public.browser.test.util.Criteria;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.weblayer.BrowserObserver;
+import org.chromium.weblayer.shell.WebLayerShellActivity;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Tests that BrowserObserver methods are invoked as expected.
+ */
+@RunWith(BaseJUnit4ClassRunner.class)
+public class BrowserObserverTest {
+    @Rule
+    public WebLayerShellActivityTestRule mActivityTestRule = new WebLayerShellActivityTestRule();
+
+    // URL used for base tests.
+    private static final String URL = "data:text";
+
+    private static class Observer extends BrowserObserver {
+        public static class BrowserObserverValueRecorder {
+            private List<String> mObservedValues =
+                    Collections.synchronizedList(new ArrayList<String>());
+
+            public void recordValue(String parameter) {
+                mObservedValues.add(parameter);
+            }
+
+            public List<String> getObservedValues() {
+                return mObservedValues;
+            }
+
+            public void waitUntilValueObserved(String expectation) {
+                CriteriaHelper.pollInstrumentationThread(
+                        new Criteria() {
+                            @Override
+                            public boolean isSatisfied() {
+                                return mObservedValues.contains(expectation);
+                            }
+                        },
+                        CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL,
+                        CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+            }
+        }
+
+        public BrowserObserverValueRecorder visibleUrlChangedCallback =
+                new BrowserObserverValueRecorder();
+        public BrowserObserverValueRecorder loadingStateChangedCallback =
+                new BrowserObserverValueRecorder();
+        public BrowserObserverValueRecorder loadProgressChangedCallback =
+                new BrowserObserverValueRecorder();
+
+        @Override
+        public void visibleUrlChanged(Uri url) {
+            visibleUrlChangedCallback.recordValue(url.toString());
+        }
+
+        @Override
+        public void loadingStateChanged(boolean isLoading, boolean toDifferentDocument) {
+            loadingStateChangedCallback.recordValue(
+                    Boolean.toString(isLoading) + " " + Boolean.toString(toDifferentDocument));
+        }
+
+        @Override
+        public void loadProgressChanged(double progress) {
+            loadProgressChangedCallback.recordValue(
+                    progress == 1 ? "load complete" : "load started");
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testLoadEvents() throws Exception {
+        String startupUrl = "about:blank";
+        WebLayerShellActivity activity = mActivityTestRule.launchShellWithUrl(startupUrl);
+        Assert.assertNotNull(activity);
+        mActivityTestRule.waitForNavigation(startupUrl);
+
+        Observer observer = new Observer();
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { activity.getBrowserController().addObserver(observer); });
+
+        String url = "data:text,foo";
+        mActivityTestRule.loadUrl(url);
+        mActivityTestRule.waitForNavigation(url);
+
+        /* Verify that the visible URL changes to the target. */
+        observer.visibleUrlChangedCallback.waitUntilValueObserved(url);
+
+        /* Wait until the BrowserObserver is notified of load completion. */
+        observer.loadingStateChangedCallback.waitUntilValueObserved("false true");
+        observer.loadProgressChangedCallback.waitUntilValueObserved("load complete");
+
+        /* Verify that the BrowserObserver was notified of load progress /before/ load completion.
+         */
+        int finishStateIndex =
+                observer.loadingStateChangedCallback.getObservedValues().indexOf("false true");
+        int finishProgressIndex =
+                observer.loadProgressChangedCallback.getObservedValues().indexOf("load complete");
+        int startStateIndex =
+                observer.loadingStateChangedCallback.getObservedValues().lastIndexOf("true true");
+        int startProgressIndex =
+                observer.loadProgressChangedCallback.getObservedValues().lastIndexOf(
+                        "load started");
+
+        Assert.assertNotEquals(startStateIndex, -1);
+        Assert.assertNotEquals(startProgressIndex, -1);
+        Assert.assertNotEquals(finishStateIndex, -1);
+        Assert.assertNotEquals(finishProgressIndex, -1);
+
+        Assert.assertTrue(startStateIndex < finishStateIndex);
+        Assert.assertTrue(startProgressIndex < finishProgressIndex);
+    }
+}
diff --git a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
index 86e2be1..df44cd51 100644
--- a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
+++ b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
@@ -122,7 +122,7 @@
         loadUrl(startupUrl);
         mBrowserController.addObserver(new BrowserObserver() {
             @Override
-            public void displayURLChanged(Uri uri) {
+            public void visibleUrlChanged(Uri uri) {
                 mUrlView.setText(uri.toString());
             }
 
diff --git a/weblayer/shell/browser/shell.cc b/weblayer/shell/browser/shell.cc
index 33f95c0..78279a5d 100644
--- a/weblayer/shell/browser/shell.cc
+++ b/weblayer/shell/browser/shell.cc
@@ -109,7 +109,7 @@
   PlatformSetLoadProgress(progress);
 }
 
-void Shell::DisplayedURLChanged(const GURL& url) {
+void Shell::DisplayedUrlChanged(const GURL& url) {
   PlatformSetAddressBarURL(url);
 }
 
diff --git a/weblayer/shell/browser/shell.h b/weblayer/shell/browser/shell.h
index 51a3de5..49e0c9f 100644
--- a/weblayer/shell/browser/shell.h
+++ b/weblayer/shell/browser/shell.h
@@ -82,7 +82,7 @@
   void LoadingStateChanged(bool is_loading,
                            bool to_different_document) override;
   void LoadProgressChanged(double progress) override;
-  void DisplayedURLChanged(const GURL& url) override;
+  void DisplayedUrlChanged(const GURL& url) override;
 
   // Helper to create a new Shell.
   static Shell* CreateShell(